From 7701e47d0f0e35958f5ce26b2a1d5fc2ce8e9ac9 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 17 Feb 2022 15:14:27 -0800 Subject: [PATCH 01/24] Add a CustomTypeMarshallerAttribute to enable us to more easily identify which types are marshaller types to make it easier to version the shape model and help users write shape types. --- .../GeneratedMarshallingAttribute.cs | 29 ++ .../Analyzers/AnalyzerDiagnostics.cs | 6 +- .../ManualTypeMarshallingAnalyzer.cs | 340 +++++++++--------- .../DllImportGenerator/Resources.Designer.cs | 113 +++--- .../gen/DllImportGenerator/Resources.resx | 47 ++- .../IGeneratorDiagnostics.cs | 2 +- .../ManualTypeMarshallingHelper.cs | 67 +++- .../MarshallingAttributeInfo.cs | 18 +- .../TypeNames.cs | 2 +- .../Ancillary.Interop/SpanMarshallers.cs | 10 +- .../CodeSnippets.cs | 19 +- .../ManualTypeMarshallingAnalyzerTests.cs | 149 ++++---- .../TestAssets/SharedTypes/NonBlittable.cs | 8 +- 13 files changed, 442 insertions(+), 368 deletions(-) diff --git a/src/libraries/Common/src/System/Runtime/InteropServices/GeneratedMarshallingAttribute.cs b/src/libraries/Common/src/System/Runtime/InteropServices/GeneratedMarshallingAttribute.cs index c594fa3e7b754..5572df405eb04 100644 --- a/src/libraries/Common/src/System/Runtime/InteropServices/GeneratedMarshallingAttribute.cs +++ b/src/libraries/Common/src/System/Runtime/InteropServices/GeneratedMarshallingAttribute.cs @@ -64,6 +64,35 @@ public MarshalUsingAttribute(Type nativeType) public const string ReturnsCountValue = "return-value"; } + [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class)] +#if DLLIMPORT_GENERATOR_TEST + public +#else + internal +#endif + sealed class CustomTypeMarshallerAttribute : Attribute + { + public CustomTypeMarshallerAttribute(Type managedType, CustomTypeMarshallerKind marshallerKind = CustomTypeMarshallerKind.Value) + { + ManagedType = managedType; + MarshallerKind = marshallerKind; + } + + public Type ManagedType { get; } + public CustomTypeMarshallerKind MarshallerKind { get; } + } + +#if DLLIMPORT_GENERATOR_TEST + public +#else + internal +#endif + enum CustomTypeMarshallerKind + { + Value, + SpanCollection + } + [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class)] #if DLLIMPORT_GENERATOR_TEST public diff --git a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Analyzers/AnalyzerDiagnostics.cs b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Analyzers/AnalyzerDiagnostics.cs index 3153a25954dd3..132ea3e21142e 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Analyzers/AnalyzerDiagnostics.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Analyzers/AnalyzerDiagnostics.cs @@ -15,9 +15,9 @@ public static class Ids public const string Prefix = "DLLIMPORTGENANALYZER"; // ManualTypeMarshalling - public const string BlittableTypeMustBeBlittable = Prefix + "001"; - public const string CannotHaveMultipleMarshallingAttributes = Prefix + "002"; - public const string NativeTypeMustBeNonNull = Prefix + "003"; + public const string MarshallerTypeMustSpecifyManagedType = Prefix + "001"; + public const string MarshallerKindMustBeValid = Prefix + "002"; + public const string NativeTypeMustHaveCustomTypeMarshallerAttribute = Prefix + "003"; public const string NativeTypeMustBeBlittable = Prefix + "004"; public const string GetPinnableReferenceReturnTypeBlittable = Prefix + "005"; public const string NativeTypeMustBePointerSized = Prefix + "006"; diff --git a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Analyzers/ManualTypeMarshallingAnalyzer.cs b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Analyzers/ManualTypeMarshallingAnalyzer.cs index 00abe866653b7..df521c67e4062 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Analyzers/ManualTypeMarshallingAnalyzer.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Analyzers/ManualTypeMarshallingAnalyzer.cs @@ -1,10 +1,12 @@ // 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.Immutable; using System.Linq; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; using static Microsoft.Interop.Analyzers.AnalyzerDiagnostics; @@ -16,35 +18,35 @@ public class ManualTypeMarshallingAnalyzer : DiagnosticAnalyzer { private const string Category = "Usage"; - public static readonly DiagnosticDescriptor BlittableTypeMustBeBlittableRule = + public static readonly DiagnosticDescriptor MarshallerTypeMustSpecifyManagedTypeRule = new DiagnosticDescriptor( - Ids.BlittableTypeMustBeBlittable, - "BlittableTypeMustBeBlittable", - GetResourceString(nameof(Resources.BlittableTypeMustBeBlittableMessage)), + Ids.MarshallerTypeMustSpecifyManagedType, + "MarshallerTypeMustSpecifyManagedType", + GetResourceString(nameof(Resources.MarshallerTypeMustSpecifyManagedTypeMessage)), Category, DiagnosticSeverity.Error, isEnabledByDefault: true, - description: GetResourceString(nameof(Resources.BlittableTypeMustBeBlittableDescription))); + description: GetResourceString(nameof(Resources.MarshallerTypeMustSpecifyManagedTypeDescription))); - public static readonly DiagnosticDescriptor CannotHaveMultipleMarshallingAttributesRule = + public static readonly DiagnosticDescriptor MarshallerKindMustBeValidRule = new DiagnosticDescriptor( - Ids.CannotHaveMultipleMarshallingAttributes, - "CannotHaveMultipleMarshallingAttributes", - GetResourceString(nameof(Resources.CannotHaveMultipleMarshallingAttributesMessage)), + Ids.MarshallerKindMustBeValid, + "MarshallerKindMustBeValid", + GetResourceString(nameof(Resources.MarshallerKindMustBeValidMessage)), Category, DiagnosticSeverity.Error, isEnabledByDefault: true, - description: GetResourceString(nameof(Resources.CannotHaveMultipleMarshallingAttributesDescription))); + description: GetResourceString(nameof(Resources.MarshallerKindMustBeValidDescription))); - public static readonly DiagnosticDescriptor NativeTypeMustBeNonNullRule = + public static readonly DiagnosticDescriptor NativeTypeMustHaveCustomTypeMarshallerAttributeRule = new DiagnosticDescriptor( - Ids.NativeTypeMustBeNonNull, - "NativeTypeMustBeNonNull", - GetResourceString(nameof(Resources.NativeTypeMustBeNonNullMessage)), + Ids.NativeTypeMustHaveCustomTypeMarshallerAttribute, + "NativeTypeMustHaveCustomTypeMarshallerAttribute", + GetResourceString(nameof(Resources.NativeTypeMustHaveCustomTypeMarshallerAttributeMessage)), Category, DiagnosticSeverity.Error, isEnabledByDefault: true, - description: GetResourceString(nameof(Resources.NativeTypeMustBeNonNullDescription))); + description: GetResourceString(nameof(Resources.NativeTypeMustHaveCustomTypeMarshallerAttributeDescription))); public static readonly DiagnosticDescriptor NativeTypeMustBeBlittableRule = new DiagnosticDescriptor( @@ -178,9 +180,9 @@ public class ManualTypeMarshallingAnalyzer : DiagnosticAnalyzer public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create( - BlittableTypeMustBeBlittableRule, - CannotHaveMultipleMarshallingAttributesRule, - NativeTypeMustBeNonNullRule, + MarshallerTypeMustSpecifyManagedTypeRule, + MarshallerKindMustBeValidRule, + NativeTypeMustHaveCustomTypeMarshallerAttributeRule, NativeTypeMustBeBlittableRule, GetPinnableReferenceReturnTypeBlittableRule, NativeTypeMustBePointerSizedRule, @@ -205,245 +207,256 @@ public override void Initialize(AnalysisContext context) private void PrepareForAnalysis(CompilationStartAnalysisContext context) { - INamedTypeSymbol? generatedMarshallingAttribute = context.Compilation.GetTypeByMetadataName(TypeNames.GeneratedMarshallingAttribute); - INamedTypeSymbol? nativeMarshallingAttribute = context.Compilation.GetTypeByMetadataName(TypeNames.NativeMarshallingAttribute); - INamedTypeSymbol? marshalUsingAttribute = context.Compilation.GetTypeByMetadataName(TypeNames.MarshalUsingAttribute); - INamedTypeSymbol? genericContiguousCollectionMarshallerAttribute = context.Compilation.GetTypeByMetadataName(TypeNames.GenericContiguousCollectionMarshallerAttribute); INamedTypeSymbol? spanOfByte = context.Compilation.GetTypeByMetadataName(TypeNames.System_Span_Metadata)!.Construct(context.Compilation.GetSpecialType(SpecialType.System_Byte)); - if (generatedMarshallingAttribute is not null - && nativeMarshallingAttribute is not null - && marshalUsingAttribute is not null - && genericContiguousCollectionMarshallerAttribute is not null - && spanOfByte is not null) + if (spanOfByte is not null) { - var perCompilationAnalyzer = new PerCompilationAnalyzer( - generatedMarshallingAttribute, - nativeMarshallingAttribute, - marshalUsingAttribute, - genericContiguousCollectionMarshallerAttribute, - spanOfByte, - context.Compilation.GetTypeByMetadataName(TypeNames.System_Runtime_InteropServices_StructLayoutAttribute)!); - context.RegisterSymbolAction(context => perCompilationAnalyzer.AnalyzeTypeDefinition(context), SymbolKind.NamedType); - context.RegisterSymbolAction(context => perCompilationAnalyzer.AnalyzeElement(context), SymbolKind.Parameter, SymbolKind.Field); - context.RegisterSymbolAction(context => perCompilationAnalyzer.AnalyzeReturnType(context), SymbolKind.Method); + var perCompilationAnalyzer = new PerCompilationAnalyzer(spanOfByte); + + // Analyze NativeMarshalling/MarshalUsing for correctness + context.RegisterSymbolAction(perCompilationAnalyzer.AnalyzeTypeDefinition, SymbolKind.NamedType); + context.RegisterSymbolAction(perCompilationAnalyzer.AnalyzeElement, SymbolKind.Parameter, SymbolKind.Field); + context.RegisterSymbolAction(perCompilationAnalyzer.AnalyzeReturnType, SymbolKind.Method); + + // Analyze marshaller type to validate shape. + context.RegisterSymbolAction(perCompilationAnalyzer.AnalyzeMarshallerType, SymbolKind.NamedType); } } private class PerCompilationAnalyzer { - private readonly INamedTypeSymbol _generatedMarshallingAttribute; - private readonly INamedTypeSymbol _nativeMarshallingAttribute; - private readonly INamedTypeSymbol _marshalUsingAttribute; - private readonly INamedTypeSymbol _genericContiguousCollectionMarshallerAttribute; private readonly INamedTypeSymbol _spanOfByte; - private readonly INamedTypeSymbol _structLayoutAttribute; - - public PerCompilationAnalyzer(INamedTypeSymbol generatedMarshallingAttribute, - INamedTypeSymbol nativeMarshallingAttribute, - INamedTypeSymbol marshalUsingAttribute, - INamedTypeSymbol genericContiguousCollectionMarshallerAttribute, - INamedTypeSymbol spanOfByte, - INamedTypeSymbol structLayoutAttribute) + + public PerCompilationAnalyzer(INamedTypeSymbol spanOfByte) { - _generatedMarshallingAttribute = generatedMarshallingAttribute; - _nativeMarshallingAttribute = nativeMarshallingAttribute; - _marshalUsingAttribute = marshalUsingAttribute; - _genericContiguousCollectionMarshallerAttribute = genericContiguousCollectionMarshallerAttribute; _spanOfByte = spanOfByte; - _structLayoutAttribute = structLayoutAttribute; } public void AnalyzeTypeDefinition(SymbolAnalysisContext context) { INamedTypeSymbol type = (INamedTypeSymbol)context.Symbol; - foreach (AttributeData attr in type.GetAttributes()) + (AttributeData? attributeData, INamedTypeSymbol? marshallerType) = ManualTypeMarshallingHelper.GetDefaultMarshallerInfo(type); + + if (attributeData is null) { - if (SymbolEqualityComparer.Default.Equals(attr.AttributeClass, _generatedMarshallingAttribute)) - { - // If the type has the GeneratedMarshallingAttribute, - // we let the source generator handle error checking. - return; - } - else if (SymbolEqualityComparer.Default.Equals(attr.AttributeClass, _nativeMarshallingAttribute)) - { - AnalyzeNativeMarshalerType(context, type, attr, isNativeMarshallingAttribute: true); - return; - } + return; } + + AnalyzeManagedTypeMarshallingInfo(context, type, attributeData, marshallerType); } public void AnalyzeElement(SymbolAnalysisContext context) { - AttributeData? attrData = context.Symbol.GetAttributes().FirstOrDefault(attr => SymbolEqualityComparer.Default.Equals(_marshalUsingAttribute, attr.AttributeClass)); - if (attrData is not null) + ITypeSymbol managedType = context.Symbol switch { - if (context.Symbol is IParameterSymbol param) - { - AnalyzeNativeMarshalerType(context, param.Type, attrData, isNativeMarshallingAttribute: false); - } - else if (context.Symbol is IFieldSymbol field) - { - AnalyzeNativeMarshalerType(context, field.Type, attrData, isNativeMarshallingAttribute: false); - } + IParameterSymbol param => param.Type, + IFieldSymbol field => field.Type, + _ => throw new InvalidOperationException() + }; + AttributeData? attributeData = context.Symbol.GetAttributes().FirstOrDefault(attr => attr.AttributeClass.ToDisplayString() == TypeNames.MarshalUsingAttribute); + if (attributeData is null || attributeData.ConstructorArguments.Length == 0) + { + return; } + AnalyzeManagedTypeMarshallingInfo(context, managedType, attributeData, attributeData.ConstructorArguments[0].Value as INamedTypeSymbol); } public void AnalyzeReturnType(SymbolAnalysisContext context) { - var method = (IMethodSymbol)context.Symbol; - AttributeData? attrData = method.GetReturnTypeAttributes().FirstOrDefault(attr => SymbolEqualityComparer.Default.Equals(_marshalUsingAttribute, attr.AttributeClass)); - if (attrData is not null) + IMethodSymbol method = (IMethodSymbol)context.Symbol; + ITypeSymbol managedType = method.ReturnType; + AttributeData? attributeData = method.GetReturnTypeAttributes().FirstOrDefault(attr => attr.AttributeClass.ToDisplayString() == TypeNames.MarshalUsingAttribute); + if (attributeData is null || attributeData.ConstructorArguments.Length == 0) { - AnalyzeNativeMarshalerType(context, method.ReturnType, attrData, isNativeMarshallingAttribute: false); + return; } + AnalyzeManagedTypeMarshallingInfo(context, managedType, attributeData, attributeData.ConstructorArguments[0].Value as INamedTypeSymbol); } - private void AnalyzeNativeMarshalerType(SymbolAnalysisContext context, ITypeSymbol type, AttributeData nativeMarshalerAttributeData, bool isNativeMarshallingAttribute) + private static void AnalyzeManagedTypeMarshallingInfo(SymbolAnalysisContext context, ITypeSymbol type, AttributeData attributeData, INamedTypeSymbol? marshallerType) { - if (nativeMarshalerAttributeData.ConstructorArguments.Length == 0) + if (marshallerType is null) { - // This is a MarshalUsing with just count information. + context.ReportDiagnostic( + attributeData.CreateDiagnostic( + NativeTypeMustHaveCustomTypeMarshallerAttributeRule, + type.ToDisplayString())); return; } - if (nativeMarshalerAttributeData.ConstructorArguments[0].IsNull) + if (marshallerType.IsUnboundGenericType) { context.ReportDiagnostic( - nativeMarshalerAttributeData.CreateDiagnostic( - NativeTypeMustBeNonNullRule, + attributeData.CreateDiagnostic( + NativeGenericTypeMustBeClosedOrMatchArityRule, + marshallerType.ToDisplayString(), type.ToDisplayString())); - return; } - ITypeSymbol nativeType = (ITypeSymbol)nativeMarshalerAttributeData.ConstructorArguments[0].Value!; - ISymbol nativeTypeDiagnosticsTargetSymbol = nativeType; + (bool hasCustomTypeMarshallerAttribute, ITypeSymbol? marshallerManagedType, _) = ManualTypeMarshallingHelper.GetMarshallerShapeInfo(marshallerType); - if (nativeType is not INamedTypeSymbol marshalerType) + if (!hasCustomTypeMarshallerAttribute) { context.ReportDiagnostic( - GetDiagnosticLocations(context, nativeType, nativeMarshalerAttributeData).CreateDiagnostic( - NativeTypeMustHaveRequiredShapeRule, - nativeType.ToDisplayString(), + attributeData.CreateDiagnostic( + NativeTypeMustHaveCustomTypeMarshallerAttributeRule, type.ToDisplayString())); return; } - DiagnosticDescriptor requiredShapeRule = NativeTypeMustHaveRequiredShapeRule; - - ManualTypeMarshallingHelper.NativeTypeMarshallingVariant variant = ManualTypeMarshallingHelper.NativeTypeMarshallingVariant.Standard; - if (marshalerType.GetAttributes().Any(a => SymbolEqualityComparer.Default.Equals(_genericContiguousCollectionMarshallerAttribute, a.AttributeClass))) + if (marshallerManagedType is null) { - variant = ManualTypeMarshallingHelper.NativeTypeMarshallingVariant.ContiguousCollection; - requiredShapeRule = CollectionNativeTypeMustHaveRequiredShapeRule; - if (!ManualTypeMarshallingHelper.TryGetManagedValuesProperty(marshalerType, out _) - || !ManualTypeMarshallingHelper.HasNativeValueStorageProperty(marshalerType, _spanOfByte)) - { - context.ReportDiagnostic( - GetDiagnosticLocations(context, marshalerType, nativeMarshalerAttributeData).CreateDiagnostic( - requiredShapeRule, - nativeType.ToDisplayString(), - type.ToDisplayString())); - return; - } + context.ReportDiagnostic( + attributeData.CreateDiagnostic( + NativeTypeMustHaveCustomTypeMarshallerAttributeRule, + type.ToDisplayString())); + return; } - if (!nativeType.IsValueType) + if (!TypeSymbolsConstructedFromEqualTypes(type, marshallerManagedType)) { context.ReportDiagnostic( - GetDiagnosticLocations(context, nativeType, nativeMarshalerAttributeData).CreateDiagnostic( - requiredShapeRule, - nativeType.ToDisplayString(), + attributeData.CreateDiagnostic( + NativeTypeMustHaveCustomTypeMarshallerAttributeRule, type.ToDisplayString())); return; } + } - if (marshalerType.IsUnboundGenericType) + private static bool TypeSymbolsConstructedFromEqualTypes(ITypeSymbol left, ITypeSymbol right) + { + return (left, right) switch { - if (!isNativeMarshallingAttribute) - { - context.ReportDiagnostic( - nativeMarshalerAttributeData.CreateDiagnostic( - NativeGenericTypeMustBeClosedOrMatchArityRule, - nativeType.ToDisplayString(), - type.ToDisplayString())); - return; - } - if (type is not INamedTypeSymbol namedType || marshalerType.TypeArguments.Length != namedType.TypeArguments.Length) + (INamedTypeSymbol namedLeft, INamedTypeSymbol namedRight) => SymbolEqualityComparer.Default.Equals(namedLeft.ConstructedFrom, namedRight.ConstructedFrom), + _ => SymbolEqualityComparer.Default.Equals(left, right) + }; + } + + public void AnalyzeMarshallerType(SymbolAnalysisContext context) + { + INamedTypeSymbol marshallerType = (INamedTypeSymbol)context.Symbol; + (bool hasCustomTypeMarshallerAttribute, ITypeSymbol? type, ManualTypeMarshallingHelper.CustomTypeMarshallerKind? marshallerKind) = ManualTypeMarshallingHelper.GetMarshallerShapeInfo(marshallerType); + if (!hasCustomTypeMarshallerAttribute) + { + return; + } + if (type is null) + { + context.ReportDiagnostic(marshallerType.CreateDiagnostic(MarshallerTypeMustSpecifyManagedTypeRule, marshallerType.ToDisplayString())); + return; + } + + if (marshallerKind == null || !Enum.IsDefined(typeof(ManualTypeMarshallingHelper.CustomTypeMarshallerKind), marshallerKind)) + { + context.ReportDiagnostic(marshallerType.CreateDiagnostic(MarshallerKindMustBeValidRule, marshallerType.ToDisplayString())); + } + + if (type is INamedTypeSymbol { IsUnboundGenericType: true } generic) + { + if (generic.TypeArguments.Length != marshallerType.TypeArguments.Length) { context.ReportDiagnostic( - nativeMarshalerAttributeData.CreateDiagnostic( + marshallerType.CreateDiagnostic( NativeGenericTypeMustBeClosedOrMatchArityRule, - nativeType.ToDisplayString(), + marshallerType.ToDisplayString(), type.ToDisplayString())); return; } - // Construct the marshaler type around the same type arguments as the managed type. - nativeType = marshalerType = marshalerType.ConstructedFrom.Construct(namedType.TypeArguments, namedType.TypeArgumentNullableAnnotations); + type = generic.ConstructedFrom.Construct(marshallerType.TypeArguments, marshallerType.TypeArgumentNullableAnnotations); + } + + DiagnosticDescriptor requiredShapeRule = marshallerKind switch + { + ManualTypeMarshallingHelper.CustomTypeMarshallerKind.Value => NativeTypeMustHaveRequiredShapeRule, + ManualTypeMarshallingHelper.CustomTypeMarshallerKind.SpanCollection => CollectionNativeTypeMustHaveRequiredShapeRule, + _ => throw new InvalidOperationException() + }; + + if (!marshallerType.IsValueType) + { + context.ReportDiagnostic( + marshallerType.CreateDiagnostic( + requiredShapeRule, + marshallerType.ToDisplayString(), + type.ToDisplayString())); } bool hasConstructor = false; bool hasCallerAllocSpanConstructor = false; - foreach (IMethodSymbol ctor in marshalerType.Constructors) + foreach (IMethodSymbol ctor in marshallerType.Constructors) { if (ctor.IsStatic) { continue; } - hasConstructor = hasConstructor || ManualTypeMarshallingHelper.IsManagedToNativeConstructor(ctor, type, variant); + hasConstructor = hasConstructor || ManualTypeMarshallingHelper.IsManagedToNativeConstructor(ctor, type, marshallerKind.Value); - if (!hasCallerAllocSpanConstructor && ManualTypeMarshallingHelper.IsCallerAllocatedSpanConstructor(ctor, type, _spanOfByte, variant)) + if (!hasCallerAllocSpanConstructor && ManualTypeMarshallingHelper.IsCallerAllocatedSpanConstructor(ctor, type, _spanOfByte, marshallerKind.Value)) { hasCallerAllocSpanConstructor = true; - IFieldSymbol bufferSizeField = nativeType.GetMembers(ManualTypeMarshallingHelper.BufferSizeFieldName).OfType().FirstOrDefault(); + IFieldSymbol bufferSizeField = marshallerType.GetMembers(ManualTypeMarshallingHelper.BufferSizeFieldName).OfType().FirstOrDefault(); if (bufferSizeField is null or { DeclaredAccessibility: not Accessibility.Public } or { IsConst: false } or { Type: not { SpecialType: SpecialType.System_Int32 } }) { context.ReportDiagnostic( - GetDiagnosticLocations(context, ctor, nativeMarshalerAttributeData).CreateDiagnostic( + ctor.CreateDiagnostic( CallerAllocConstructorMustHaveBufferSizeConstantRule, - nativeType.ToDisplayString())); + marshallerType.ToDisplayString())); } } } - bool hasToManaged = ManualTypeMarshallingHelper.HasToManagedMethod(marshalerType, type); + if (marshallerKind == ManualTypeMarshallingHelper.CustomTypeMarshallerKind.SpanCollection) + { + requiredShapeRule = CollectionNativeTypeMustHaveRequiredShapeRule; + if (!ManualTypeMarshallingHelper.TryGetManagedValuesProperty(marshallerType, out _) + || !ManualTypeMarshallingHelper.HasNativeValueStorageProperty(marshallerType, _spanOfByte)) + { + context.ReportDiagnostic( + marshallerType.CreateDiagnostic( + requiredShapeRule, + marshallerType.ToDisplayString(), + type.ToDisplayString())); + return; + } + } + + bool hasToManaged = ManualTypeMarshallingHelper.HasToManagedMethod(marshallerType, type); // Validate that the native type has at least one marshalling method (either managed to native or native to managed) if (!hasConstructor && !hasCallerAllocSpanConstructor && !hasToManaged) { context.ReportDiagnostic( - GetDiagnosticLocations(context, marshalerType, nativeMarshalerAttributeData).CreateDiagnostic( + marshallerType.CreateDiagnostic( requiredShapeRule, - marshalerType.ToDisplayString(), + marshallerType.ToDisplayString(), type.ToDisplayString())); } // Validate that this type can support marshalling when stackalloc is not usable. - if (isNativeMarshallingAttribute && hasCallerAllocSpanConstructor && !hasConstructor) + if (hasCallerAllocSpanConstructor && !hasConstructor) { context.ReportDiagnostic( - GetDiagnosticLocations(context, marshalerType, nativeMarshalerAttributeData).CreateDiagnostic( + marshallerType.CreateDiagnostic( CallerAllocMarshallingShouldSupportAllocatingMarshallingFallbackRule, - marshalerType.ToDisplayString())); + marshallerType.ToDisplayString())); } - IPropertySymbol? valueProperty = ManualTypeMarshallingHelper.FindValueProperty(nativeType); + IPropertySymbol? valueProperty = ManualTypeMarshallingHelper.FindValueProperty(marshallerType); bool valuePropertyIsRefReturn = valueProperty is { ReturnsByRef: true } or { ReturnsByRefReadonly: true }; - + ITypeSymbol nativeType = marshallerType; if (valueProperty is not null) { if (valuePropertyIsRefReturn) { context.ReportDiagnostic( - GetDiagnosticLocations(context, valueProperty, nativeMarshalerAttributeData).CreateDiagnostic( + valueProperty.CreateDiagnostic( RefValuePropertyUnsupportedRule, - marshalerType.ToDisplayString())); + marshallerType.ToDisplayString())); } nativeType = valueProperty.Type; - nativeTypeDiagnosticsTargetSymbol = valueProperty; // Validate that we don't have partial implementations. // We error if either of the conditions below are partially met but not fully met: @@ -452,19 +465,19 @@ private void AnalyzeNativeMarshalerType(SymbolAnalysisContext context, ITypeSymb if ((hasConstructor || hasCallerAllocSpanConstructor) && valueProperty.GetMethod is null) { context.ReportDiagnostic( - GetDiagnosticLocations(context, valueProperty, nativeMarshalerAttributeData).CreateDiagnostic( + valueProperty.CreateDiagnostic( ValuePropertyMustHaveGetterRule, - marshalerType.ToDisplayString())); + marshallerType.ToDisplayString())); } if (hasToManaged && valueProperty.SetMethod is null) { context.ReportDiagnostic( - GetDiagnosticLocations(context, valueProperty, nativeMarshalerAttributeData).CreateDiagnostic( + valueProperty.CreateDiagnostic( ValuePropertyMustHaveSetterRule, - marshalerType.ToDisplayString())); + marshallerType.ToDisplayString())); } } - else if (ManualTypeMarshallingHelper.FindGetPinnableReference(marshalerType) is IMethodSymbol marshallerGetPinnableReferenceMethod) + else if (ManualTypeMarshallingHelper.FindGetPinnableReference(marshallerType) is IMethodSymbol marshallerGetPinnableReferenceMethod) { // If we don't have a Value property, then we disallow a GetPinnableReference on the marshaler type. // We do this since there is no valid use case that we can think of for a GetPinnableReference on a blittable type @@ -479,23 +492,24 @@ private void AnalyzeNativeMarshalerType(SymbolAnalysisContext context, ITypeSymb if (!nativeType.IsConsideredBlittable()) { context.ReportDiagnostic( - GetDiagnosticLocations(context, nativeTypeDiagnosticsTargetSymbol, nativeMarshalerAttributeData).CreateDiagnostic( + (valueProperty ?? (ISymbol)marshallerType).CreateDiagnostic( NativeTypeMustBeBlittableRule, nativeType.ToDisplayString(), type.ToDisplayString())); } - if (isNativeMarshallingAttribute && ManualTypeMarshallingHelper.FindGetPinnableReference(type) is IMethodSymbol managedGetPinnableReferenceMethod) + if (SymbolEqualityComparer.Default.Equals(ManualTypeMarshallingHelper.GetDefaultMarshallerInfo(type).marshallerType, marshallerType) + && ManualTypeMarshallingHelper.FindGetPinnableReference(type) is IMethodSymbol managedGetPinnableReferenceMethod) { if (!managedGetPinnableReferenceMethod.ReturnType.IsConsideredBlittable()) { context.ReportDiagnostic(managedGetPinnableReferenceMethod.CreateDiagnostic(GetPinnableReferenceReturnTypeBlittableRule)); } // Validate that our marshaler supports scenarios where GetPinnableReference cannot be used. - if (isNativeMarshallingAttribute && (!hasConstructor || valueProperty is { GetMethod: null })) + if (!hasConstructor || valueProperty is { GetMethod: null }) { context.ReportDiagnostic( - nativeMarshalerAttributeData.CreateDiagnostic( + type.CreateDiagnostic( GetPinnableReferenceShouldSupportAllocatingMarshallingFallbackRule, type.ToDisplayString())); } @@ -508,28 +522,14 @@ IPointerTypeSymbol or { SpecialType: SpecialType.System_IntPtr } or { SpecialType: SpecialType.System_UIntPtr })) { - IMethodSymbol getPinnableReferenceMethodToMention = managedGetPinnableReferenceMethod; - context.ReportDiagnostic( - GetDiagnosticLocations(context, nativeTypeDiagnosticsTargetSymbol, nativeMarshalerAttributeData).CreateDiagnostic( + valueProperty.CreateDiagnostic( NativeTypeMustBePointerSizedRule, nativeType.ToDisplayString(), - getPinnableReferenceMethodToMention.ContainingType.ToDisplayString())); + managedGetPinnableReferenceMethod.ContainingType.ToDisplayString())); } } } - - private ImmutableArray GetDiagnosticLocations(SymbolAnalysisContext context, ISymbol targetSymbol, AttributeData marshallingAttribute) - { - // If we're using a compilation that references another compilation, the symbol locations can be in source in the wrong compilation, - // which can cause exceptions when reporting diagnostics. Make sure the symbol is defined in the current Compilation's source module before using its locations. - // If the symbol is not defined in the current Compilation's source module, report the diagnostic at the marshalling attribute's location. - if (SymbolEqualityComparer.Default.Equals(context.Compilation.SourceModule, targetSymbol.ContainingModule)) - { - return targetSymbol.Locations; - } - return ImmutableArray.Create(marshallingAttribute.ApplicationSyntaxReference?.GetSyntax().GetLocation() ?? Location.None); - } } } } diff --git a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Resources.Designer.cs b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Resources.Designer.cs index a8c33b93818ec..221e4ad40b98c 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Resources.Designer.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Resources.Designer.cs @@ -60,24 +60,6 @@ internal Resources() { } } - /// - /// Looks up a localized string similar to A type marked with 'BlittableTypeAttribute' must be blittable.. - /// - internal static string BlittableTypeMustBeBlittableDescription { - get { - return ResourceManager.GetString("BlittableTypeMustBeBlittableDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Type '{0}' is marked with 'BlittableTypeAttribute' but is not blittable. - /// - internal static string BlittableTypeMustBeBlittableMessage { - get { - return ResourceManager.GetString("BlittableTypeMustBeBlittableMessage", resourceCulture); - } - } - /// /// Looks up a localized string similar to When a constructor taking a Span<byte> is specified on the native type, the type must also have a public integer constant named BufferSize to provide the size of the caller-allocated buffer.. /// @@ -115,25 +97,7 @@ internal static string CallerAllocMarshallingShouldSupportAllocatingMarshallingF } /// - /// Looks up a localized string similar to The 'BlittableTypeAttribute' and 'NativeMarshallingAttribute' attributes are mutually exclusive.. - /// - internal static string CannotHaveMultipleMarshallingAttributesDescription { - get { - return ResourceManager.GetString("CannotHaveMultipleMarshallingAttributesDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Type '{0}' is marked with 'BlittableTypeAttribute' and 'NativeMarshallingAttribute'. A type can only have one of these two attributes.. - /// - internal static string CannotHaveMultipleMarshallingAttributesMessage { - get { - return ResourceManager.GetString("CannotHaveMultipleMarshallingAttributesMessage", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to A native type with the 'GenericContiguousCollectionMarshallerAttribute' must have at least one of the two marshalling methods as well as a 'ManagedValues' property of type 'Span<T>' for some 'T' and a 'NativeValueStorage' property of type 'Span<byte>' to enable marshalling the managed type.. + /// Looks up a localized string similar to A 'SpanCollection'-kind native type must have at least one of the two marshalling methods as well as a 'ManagedValues' property of type 'Span<T>' for some 'T' and a 'NativeValueStorage' property of type 'Span<byte>' to enable marshalling the managed type.. /// internal static string CollectionNativeTypeMustHaveRequiredShapeDescription { get { @@ -393,6 +357,42 @@ internal static string MarshallerGetPinnableReferenceRequiresValuePropertyMessag } } + /// + /// Looks up a localized string similar to The specified marshaller kind must be valid value of the 'System.Runtime.InteropServices.CustomMarshallerKind' enum.. + /// + internal static string MarshallerKindMustBeValidDescription { + get { + return ResourceManager.GetString("MarshallerKindMustBeValidDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The specified custom marshller kind for '{0}' is invalid. + /// + internal static string MarshallerKindMustBeValidMessage { + get { + return ResourceManager.GetString("MarshallerKindMustBeValidMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to A type with a 'System.Runtime.InteropServices.CustomTypeMarshallerAttribute' must specify a managed type. + /// + internal static string MarshallerTypeMustSpecifyManagedTypeDescription { + get { + return ResourceManager.GetString("MarshallerTypeMustSpecifyManagedTypeDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The type '{0}' does not specify a managed type in the 'System.Runtime.InteropServices.CustomTypeMarshallerAttribute' applied to the type. + /// + internal static string MarshallerTypeMustSpecifyManagedTypeMessage { + get { + return ResourceManager.GetString("MarshallerTypeMustSpecifyManagedTypeMessage", resourceCulture); + } + } + /// /// Looks up a localized string similar to The native type '{0}' must be a closed generic so the emitted code can use a specific instantiation.. /// @@ -421,7 +421,7 @@ internal static string NativeGenericTypeMustBeClosedOrMatchArityMessage { } /// - /// Looks up a localized string similar to A native type for a given type must be blittable.. + /// Looks up a localized string similar to A native type must be blittable.. /// internal static string NativeTypeMustBeBlittableDescription { get { @@ -430,7 +430,7 @@ internal static string NativeTypeMustBeBlittableDescription { } /// - /// Looks up a localized string similar to The native type '{0}' for the type '{1}' is not blittable. + /// Looks up a localized string similar to The native type '{0}' for type '{1}' must be blittable. /// internal static string NativeTypeMustBeBlittableMessage { get { @@ -439,43 +439,43 @@ internal static string NativeTypeMustBeBlittableMessage { } /// - /// Looks up a localized string similar to A native type for a given type must be non-null.. + /// Looks up a localized string similar to The native type must be pointer sized so the pinned result of 'GetPinnableReference' can be cast to the native type.. /// - internal static string NativeTypeMustBeNonNullDescription { + internal static string NativeTypeMustBePointerSizedDescription { get { - return ResourceManager.GetString("NativeTypeMustBeNonNullDescription", resourceCulture); + return ResourceManager.GetString("NativeTypeMustBePointerSizedDescription", resourceCulture); } } /// - /// Looks up a localized string similar to The native type for the type '{0}' is null. + /// Looks up a localized string similar to The native type '{0}' must be pointer sized because the managed type '{1}' has a 'GetPinnableReference' method. /// - internal static string NativeTypeMustBeNonNullMessage { + internal static string NativeTypeMustBePointerSizedMessage { get { - return ResourceManager.GetString("NativeTypeMustBeNonNullMessage", resourceCulture); + return ResourceManager.GetString("NativeTypeMustBePointerSizedMessage", resourceCulture); } } /// - /// Looks up a localized string similar to The native type must be pointer sized so the pinned result of 'GetPinnableReference' can be cast to the native type.. + /// Looks up a localized string similar to A native type for a given type must have the 'System.Runtime.InteropServices.CustomTypeMarshallerAttribute' that specifies this type as the managed type.. /// - internal static string NativeTypeMustBePointerSizedDescription { + internal static string NativeTypeMustHaveCustomTypeMarshallerAttributeDescription { get { - return ResourceManager.GetString("NativeTypeMustBePointerSizedDescription", resourceCulture); + return ResourceManager.GetString("NativeTypeMustHaveCustomTypeMarshallerAttributeDescription", resourceCulture); } } /// - /// Looks up a localized string similar to The native type '{0}' must be pointer sized because the managed type '{1}' has a 'GetPinnableReference' method. + /// Looks up a localized string similar to The native type for the type '{0}' must be a type with the 'System.Runtime.InteropServices.CustomTypeMarshallerAttribute' that specifies this type as the managed type. /// - internal static string NativeTypeMustBePointerSizedMessage { + internal static string NativeTypeMustHaveCustomTypeMarshallerAttributeMessage { get { - return ResourceManager.GetString("NativeTypeMustBePointerSizedMessage", resourceCulture); + return ResourceManager.GetString("NativeTypeMustHaveCustomTypeMarshallerAttributeMessage", resourceCulture); } } /// - /// Looks up a localized string similar to The native type must have at least one of the two marshalling methods to enable marshalling the managed type.. + /// Looks up a localized string similar to A 'Value'-kind native type must have at least one of the two marshalling methods to enable marshalling the managed type.. /// internal static string NativeTypeMustHaveRequiredShapeDescription { get { @@ -510,15 +510,6 @@ internal static string RefValuePropertyUnsupportedMessage { } } - /// - /// Looks up a localized string similar to . - /// - internal static string RuntimeMarshallingMustBeDisabled { - get { - return ResourceManager.GetString("RuntimeMarshallingMustBeDisabled", resourceCulture); - } - } - /// /// Looks up a localized string similar to An abstract type derived from 'SafeHandle' cannot be marshalled by reference. The provided type must be concrete.. /// diff --git a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Resources.resx b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Resources.resx index 8a9d0f5b7e12e..198521950b527 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Resources.resx +++ b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Resources.resx @@ -117,12 +117,6 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - A type marked with 'BlittableTypeAttribute' must be blittable. - - - Type '{0}' is marked with 'BlittableTypeAttribute' but is not blittable - When a constructor taking a Span<byte> is specified on the native type, the type must also have a public integer constant named BufferSize to provide the size of the caller-allocated buffer. @@ -135,14 +129,8 @@ Native type '{0}' has a constructor taking a caller-allocated buffer, but does not support marshalling in scenarios where using a caller-allocated buffer is impossible - - The 'BlittableTypeAttribute' and 'NativeMarshallingAttribute' attributes are mutually exclusive. - - - Type '{0}' is marked with 'BlittableTypeAttribute' and 'NativeMarshallingAttribute'. A type can only have one of these two attributes. - - A native type with the 'GenericContiguousCollectionMarshallerAttribute' must have at least one of the two marshalling methods as well as a 'ManagedValues' property of type 'Span<T>' for some 'T' and a 'NativeValueStorage' property of type 'Span<byte>' to enable marshalling the managed type. + A 'SpanCollection'-kind native type must have at least one of the two marshalling methods as well as a 'ManagedValues' property of type 'Span<T>' for some 'T' and a 'NativeValueStorage' property of type 'Span<byte>' to enable marshalling the managed type. The native type '{0}' must be a value type and have a constructor that takes two parameters, one of type '{1}' and an 'int', or have a parameterless instance method named 'ToManaged' that returns '{1}' as well as a 'ManagedValues' property of type 'Span<T>' for some 'T' and a 'NativeValueStorage' property of type 'Span<byte>' @@ -229,6 +217,18 @@ The 'GetPinnableReference' method cannot be provided on the native type '{0}' unless a 'Value' property is also provided + + The specified marshaller kind must be valid value of the 'System.Runtime.InteropServices.CustomMarshallerKind' enum. + + + The specified custom marshller kind for '{0}' is invalid + + + A type with a 'System.Runtime.InteropServices.CustomTypeMarshallerAttribute' must specify a managed type + + + The type '{0}' does not specify a managed type in the 'System.Runtime.InteropServices.CustomTypeMarshallerAttribute' applied to the type + The native type '{0}' must be a closed generic so the emitted code can use a specific instantiation. @@ -239,16 +239,10 @@ The native type '{0}' for managed type '{1}' must be a closed generic type or have the same arity as the managed type. - A native type for a given type must be blittable. + A native type must be blittable. - The native type '{0}' for the type '{1}' is not blittable - - - A native type for a given type must be non-null. - - - The native type for the type '{0}' is null + The native type '{0}' for type '{1}' must be blittable The native type must be pointer sized so the pinned result of 'GetPinnableReference' can be cast to the native type. @@ -256,8 +250,14 @@ The native type '{0}' must be pointer sized because the managed type '{1}' has a 'GetPinnableReference' method + + A native type for a given type must have the 'System.Runtime.InteropServices.CustomTypeMarshallerAttribute' that specifies this type as the managed type. + + + The native type for the type '{0}' must be a type with the 'System.Runtime.InteropServices.CustomTypeMarshallerAttribute' that specifies this type as the managed type + - The native type must have at least one of the two marshalling methods to enable marshalling the managed type. + A 'Value'-kind native type must have at least one of the two marshalling methods to enable marshalling the managed type. The native type '{0}' must be a value type and have a constructor that takes one parameter of type '{1}' or a parameterless instance method named 'ToManaged' that returns '{1}' @@ -268,9 +268,6 @@ The 'Value' property on the native type '{0}' must not be a 'ref' or 'readonly ref' property. - - - An abstract type derived from 'SafeHandle' cannot be marshalled by reference. The provided type must be concrete. diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/IGeneratorDiagnostics.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/IGeneratorDiagnostics.cs index 58375bd955ef6..bc6f1119cbf70 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/IGeneratorDiagnostics.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/IGeneratorDiagnostics.cs @@ -29,7 +29,7 @@ public static Diagnostic CreateDiagnostic( { SyntaxReference? syntaxReference = attributeData.ApplicationSyntaxReference; Location location = syntaxReference is not null - ? syntaxReference.GetSyntax().GetLocation() + ? syntaxReference.SyntaxTree.GetLocation(syntaxReference.Span) : Location.None; return location.CreateDiagnostic(descriptor, args); 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 b6343bb5d23e2..586ee91667b68 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 @@ -26,10 +26,63 @@ public static class MarshalUsingProperties public const string ConstantElementCount = nameof(ConstantElementCount); } - public enum NativeTypeMarshallingVariant + public enum CustomTypeMarshallerKind { - Standard, - ContiguousCollection + Value, + SpanCollection + } + + public static (bool hasAttribute, ITypeSymbol? managedType, CustomTypeMarshallerKind? kind) GetMarshallerShapeInfo(ITypeSymbol marshallerType) + { + var attr = marshallerType.GetAttributes().FirstOrDefault(attr => attr.AttributeClass.ToDisplayString() == TypeNames.CustomTypeMarshallerAttribute); + if (attr is null) + { + return (false, null, null); + } + if (attr.ConstructorArguments.Length == 0) + { + return (true, null, null); + } + if (attr.ConstructorArguments.Length == 1) + { + return (true, attr.ConstructorArguments[0].Value as ITypeSymbol, CustomTypeMarshallerKind.Value); + } + return (true, attr.ConstructorArguments[0].Value as ITypeSymbol, attr.ConstructorArguments[1].Value is int i ? (CustomTypeMarshallerKind)i : null); + } + + public static (AttributeData? attribute, INamedTypeSymbol? marshallerType) GetDefaultMarshallerInfo(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) + { + return (attr, null); + } + + marshallerType = attr.ConstructorArguments[0].Value as INamedTypeSymbol; + if (managedType is not INamedTypeSymbol namedType || marshallerType is null) + { + return (attr, null); + } + if (namedType.TypeArguments.Length == 0) + { + return (attr, marshallerType); + } + else if (marshallerType.TypeArguments.Length != namedType.TypeArguments.Length) + { + return (attr, null); + } + else if (marshallerType.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, marshallerType); } public static bool HasToManagedMethod(ITypeSymbol nativeType, ITypeSymbol managedType) @@ -46,9 +99,9 @@ public static bool HasToManagedMethod(ITypeSymbol nativeType, ITypeSymbol manage public static bool IsManagedToNativeConstructor( IMethodSymbol ctor, ITypeSymbol managedType, - NativeTypeMarshallingVariant variant) + CustomTypeMarshallerKind variant) { - if (variant == NativeTypeMarshallingVariant.ContiguousCollection) + if (variant == CustomTypeMarshallerKind.SpanCollection) { return ctor.Parameters.Length == 2 && SymbolEqualityComparer.Default.Equals(managedType, ctor.Parameters[0].Type) @@ -62,9 +115,9 @@ public static bool IsCallerAllocatedSpanConstructor( IMethodSymbol ctor, ITypeSymbol managedType, ITypeSymbol spanOfByte, - NativeTypeMarshallingVariant variant) + CustomTypeMarshallerKind variant) { - if (variant == NativeTypeMarshallingVariant.ContiguousCollection) + if (variant == CustomTypeMarshallerKind.SpanCollection) { return ctor.Parameters.Length == 3 && SymbolEqualityComparer.Default.Equals(managedType, ctor.Parameters[0].Type) 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 c151607c9721d..7b14e3313782e 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 @@ -612,23 +612,17 @@ private MarshallingInfo CreateNativeMarshallingInfo( } } - ITypeSymbol contiguousCollectionMarshalerAttribute = _compilation.GetTypeByMetadataName(TypeNames.GenericContiguousCollectionMarshallerAttribute)!; - - bool isContiguousCollectionMarshaller = nativeType.GetAttributes().Any(attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass, contiguousCollectionMarshalerAttribute)); + var (_, _, marshallingVariant) = ManualTypeMarshallingHelper.GetMarshallerShapeInfo(nativeType); IPropertySymbol? valueProperty = ManualTypeMarshallingHelper.FindValueProperty(nativeType); - ManualTypeMarshallingHelper.NativeTypeMarshallingVariant marshallingVariant = isContiguousCollectionMarshaller - ? ManualTypeMarshallingHelper.NativeTypeMarshallingVariant.ContiguousCollection - : ManualTypeMarshallingHelper.NativeTypeMarshallingVariant.Standard; - bool hasInt32Constructor = false; foreach (IMethodSymbol ctor in nativeType.Constructors) { - if (ManualTypeMarshallingHelper.IsManagedToNativeConstructor(ctor, type, marshallingVariant) && (valueProperty is null or { GetMethod: not null })) + if (ManualTypeMarshallingHelper.IsManagedToNativeConstructor(ctor, type, marshallingVariant.Value) && (valueProperty is null or { GetMethod: not null })) { features |= CustomMarshallingFeatures.ManagedToNative; } - else if (ManualTypeMarshallingHelper.IsCallerAllocatedSpanConstructor(ctor, type, spanOfByte, marshallingVariant) + else if (ManualTypeMarshallingHelper.IsCallerAllocatedSpanConstructor(ctor, type, spanOfByte, marshallingVariant.Value) && (valueProperty is null or { GetMethod: not null })) { features |= CustomMarshallingFeatures.ManagedToNativeStackalloc; @@ -641,7 +635,7 @@ private MarshallingInfo CreateNativeMarshallingInfo( // The constructor that takes only the native element size is required for collection marshallers // in the native-to-managed scenario. - if ((!isContiguousCollectionMarshaller + if ((marshallingVariant != ManualTypeMarshallingHelper.CustomTypeMarshallerKind.SpanCollection || (hasInt32Constructor && ManualTypeMarshallingHelper.HasSetUnmarshalledCollectionLengthMethod(nativeType))) && ManualTypeMarshallingHelper.HasToManagedMethod(nativeType, type) && (valueProperty is null or { SetMethod: not null })) @@ -653,7 +647,7 @@ private MarshallingInfo CreateNativeMarshallingInfo( { _diagnostics.ReportInvalidMarshallingAttributeInfo( attrData, - isContiguousCollectionMarshaller + marshallingVariant == ManualTypeMarshallingHelper.CustomTypeMarshallerKind.SpanCollection ? nameof(Resources.CollectionNativeTypeMustHaveRequiredShapeMessage) : nameof(Resources.NativeTypeMustHaveRequiredShapeMessage), nativeType.ToDisplayString()); @@ -670,7 +664,7 @@ private MarshallingInfo CreateNativeMarshallingInfo( features |= CustomMarshallingFeatures.NativeTypePinning; } - if (isContiguousCollectionMarshaller) + if (marshallingVariant == ManualTypeMarshallingHelper.CustomTypeMarshallerKind.SpanCollection) { if (!ManualTypeMarshallingHelper.HasNativeValueStorageProperty(nativeType, spanOfByte)) { 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 8111b0787bc88..273ab5519ae98 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 @@ -20,7 +20,7 @@ public static class TypeNames public const string MarshalUsingAttribute = "System.Runtime.InteropServices.MarshalUsingAttribute"; - public const string GenericContiguousCollectionMarshallerAttribute = "System.Runtime.InteropServices.GenericContiguousCollectionMarshallerAttribute"; + public const string CustomTypeMarshallerAttribute = "System.Runtime.InteropServices.CustomTypeMarshallerAttribute"; public const string LCIDConversionAttribute = "System.Runtime.InteropServices.LCIDConversionAttribute"; diff --git a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/SpanMarshallers.cs b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/SpanMarshallers.cs index 46cd7302fbff5..9afedb29fe7d1 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/SpanMarshallers.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/SpanMarshallers.cs @@ -6,7 +6,7 @@ namespace System.Runtime.InteropServices.GeneratedMarshalling { - [GenericContiguousCollectionMarshaller] + [CustomTypeMarshaller(typeof(ReadOnlySpan<>), CustomTypeMarshallerKind.SpanCollection)] public unsafe ref struct ReadOnlySpanMarshaller { private ReadOnlySpan _managedSpan; @@ -95,7 +95,7 @@ public void FreeNative() } } - [GenericContiguousCollectionMarshaller] + [CustomTypeMarshaller(typeof(Span<>), CustomTypeMarshallerKind.SpanCollection)] public unsafe ref struct SpanMarshaller { private ReadOnlySpanMarshaller _inner; @@ -156,7 +156,7 @@ public void FreeNative() } } - [GenericContiguousCollectionMarshaller] + [CustomTypeMarshaller(typeof(Span<>), CustomTypeMarshallerKind.SpanCollection)] public unsafe ref struct NeverNullSpanMarshaller { private SpanMarshaller _inner; @@ -227,7 +227,7 @@ public void FreeNative() } } - [GenericContiguousCollectionMarshaller] + [CustomTypeMarshaller(typeof(ReadOnlySpan<>), CustomTypeMarshallerKind.SpanCollection)] public unsafe ref struct NeverNullReadOnlySpanMarshaller { private ReadOnlySpanMarshaller _inner; @@ -299,7 +299,7 @@ public void FreeNative() } } - [GenericContiguousCollectionMarshaller] + [CustomTypeMarshaller(typeof(Span<>), CustomTypeMarshallerKind.SpanCollection)] public unsafe ref struct DirectSpanMarshaller where T : unmanaged { diff --git a/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/CodeSnippets.cs b/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/CodeSnippets.cs index 535000d886f87..71739d2a73647 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/CodeSnippets.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/CodeSnippets.cs @@ -593,6 +593,7 @@ struct S public bool b; } +[CustomTypeMarshaller] struct Native { private int i; @@ -630,6 +631,7 @@ struct S public bool b; } +[CustomTypeMarshaller] struct Native { private int i; @@ -650,6 +652,7 @@ struct S public bool b; } +[CustomTypeMarshaller] struct Native { private int i; @@ -671,6 +674,7 @@ struct S public bool b; } +[CustomTypeMarshaller] struct Native { private int i; @@ -697,6 +701,7 @@ struct S public bool b; } +[CustomTypeMarshaller] struct Native { public Native(S s, System.Span b) @@ -718,6 +723,7 @@ struct S public bool b; } +[CustomTypeMarshaller] struct Native { public Native(S s) @@ -739,6 +745,7 @@ class S public ref int GetPinnableReference() => ref i; } +[CustomTypeMarshaller] unsafe struct Native { private int* ptr; @@ -770,6 +777,7 @@ class S public byte c; } +[CustomTypeMarshaller] unsafe ref struct Native { private byte* ptr; @@ -827,6 +835,7 @@ class S public byte c = 0; } +[CustomTypeMarshaller] unsafe struct Native { private S value; @@ -877,6 +886,7 @@ struct S public bool b; } +[CustomTypeMarshaller] struct Native { private int i; @@ -895,6 +905,7 @@ struct S public bool b; } +[CustomTypeMarshaller] struct Native { private int i; @@ -927,6 +938,7 @@ public struct IntStructWrapper public int Value; } +[CustomTypeMarshaller] public struct IntStructWrapperNative { public IntStructWrapperNative(IntStructWrapper managed) @@ -947,6 +959,7 @@ public struct IntStructWrapper public int Value; } +[CustomTypeMarshaller] public struct IntStructWrapperNative { private int value; @@ -1072,7 +1085,7 @@ public static string CollectionByValue(string elementType) => BasicParameterByVa [NativeMarshalling(typeof(Marshaller<>))] class TestCollection {} -[GenericContiguousCollectionMarshaller] +[CustomTypeMarshaller(CustomTypeMarshallerKind.SpanCollection)] ref struct Marshaller { public Marshaller(TestCollection managed, int nativeElementSize) : this() {} @@ -1106,7 +1119,7 @@ public static string CustomCollectionWithMarshaller(bool enableDefaultMarshallin string nativeMarshallingAttribute = enableDefaultMarshalling ? "[NativeMarshalling(typeof(Marshaller<>))]" : string.Empty; return nativeMarshallingAttribute + @"class TestCollection {} -[GenericContiguousCollectionMarshaller] +[CustomTypeMarshaller(CustomTypeMarshallerKind.SpanCollection)] ref struct Marshaller { public Marshaller(int nativeElementSize) : this() {} @@ -1203,7 +1216,7 @@ public static partial void Method( [NativeMarshalling(typeof(Marshaller<,>))] class TestCollection {} -[GenericContiguousCollectionMarshaller] +[CustomTypeMarshaller(CustomTypeMarshallerKind.SpanCollection)] ref struct Marshaller { public Marshaller(TestCollection managed, int nativeElementSize) : this() {} diff --git a/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/ManualTypeMarshallingAnalyzerTests.cs b/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/ManualTypeMarshallingAnalyzerTests.cs index 793629f99a2e9..f13fdc09897d1 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/ManualTypeMarshallingAnalyzerTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/ManualTypeMarshallingAnalyzerTests.cs @@ -28,7 +28,7 @@ struct S }"; await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(NativeTypeMustBeNonNullRule).WithLocation(0).WithArguments("S")); + VerifyCS.Diagnostic(NativeTypeMustHaveCustomTypeMarshallerAttributeRule).WithLocation(0).WithArguments("S")); } [ConditionalFact] @@ -44,7 +44,7 @@ struct S }"; await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(NativeTypeMustHaveRequiredShapeRule).WithLocation(0).WithArguments("int*", "S")); + VerifyCS.Diagnostic(NativeTypeMustHaveCustomTypeMarshallerAttributeRule).WithLocation(0).WithArguments("S")); } [ConditionalFact] @@ -59,6 +59,7 @@ struct S public string s; } +[CustomTypeMarshaller(typeof(S))] struct {|#0:Native|} { private string value; @@ -87,6 +88,7 @@ struct S public string s; } +[CustomTypeMarshaller(typeof(S))] class {|#0:Native|} { private IntPtr value; @@ -98,7 +100,8 @@ public Native(S s) public S ToManaged() => new S(); }"; await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(NativeTypeMustHaveRequiredShapeRule).WithLocation(0).WithArguments("Native", "S")); + VerifyCS.Diagnostic(NativeTypeMustHaveRequiredShapeRule).WithLocation(0).WithArguments("Native", "S"), + VerifyCS.Diagnostic(NativeTypeMustBeBlittableRule).WithLocation(0).WithArguments("Native", "S")); } [ConditionalFact] @@ -114,6 +117,7 @@ struct S public string s; } +[CustomTypeMarshaller(typeof(S))] struct Native { private IntPtr value; @@ -142,6 +146,7 @@ struct S public string s; } +[CustomTypeMarshaller(typeof(S))] struct Native { private IntPtr value; @@ -173,6 +178,7 @@ struct S public string s; } +[CustomTypeMarshaller(typeof(S))] struct Native { private string value; @@ -203,6 +209,7 @@ struct S public string s; } +[CustomTypeMarshaller(typeof(S))] class {|#0:Native|} { private string value; @@ -236,6 +243,7 @@ class S public ref string {|#0:GetPinnableReference|}() => ref s; } +[CustomTypeMarshaller(typeof(S))] unsafe struct Native { private IntPtr value; @@ -269,6 +277,7 @@ class S public ref byte GetPinnableReference() => ref c; } +[CustomTypeMarshaller(typeof(S))] unsafe struct Native { private IntPtr value; @@ -299,6 +308,7 @@ class S public char c; } +[CustomTypeMarshaller(typeof(S))] unsafe struct Native { private IntPtr value; @@ -331,6 +341,7 @@ class S public byte c; } +[CustomTypeMarshaller(typeof(S))] unsafe struct Native { private IntPtr value; @@ -365,6 +376,7 @@ class S public ref byte GetPinnableReference() => ref c; } +[CustomTypeMarshaller(typeof(S))] unsafe struct Native { private IntPtr value; @@ -398,6 +410,7 @@ class S public ref byte GetPinnableReference() => ref c; } +[CustomTypeMarshaller(typeof(S))] unsafe struct Native { private IntPtr value; @@ -430,6 +443,7 @@ class S public ref byte GetPinnableReference() => ref c; } +[CustomTypeMarshaller(typeof(S))] unsafe struct Native { private S value; @@ -459,6 +473,7 @@ class S public byte c; } +[CustomTypeMarshaller(typeof(S))] unsafe struct Native { private S value; @@ -490,6 +505,7 @@ class S public byte c; } +[CustomTypeMarshaller(typeof(S))] unsafe struct Native { private byte value; @@ -519,6 +535,7 @@ class S public byte c; } +[CustomTypeMarshaller(typeof(S))] unsafe struct Native { private byte value; @@ -549,6 +566,7 @@ class S public byte c; } +[CustomTypeMarshaller(typeof(S))] struct {|#0:Native|} { }"; @@ -570,7 +588,7 @@ class S public byte c; } -[GenericContiguousCollectionMarshaller] +[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.SpanCollection)] struct {|#0:Native|} { }"; @@ -592,7 +610,7 @@ class S public byte c; } -[GenericContiguousCollectionMarshaller] +[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.SpanCollection)] ref struct {|#0:Native|} { public Native(S s) : this() {} @@ -620,7 +638,7 @@ class S public byte c; } -[GenericContiguousCollectionMarshaller] +[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.SpanCollection)] ref struct Native { public Native(S s, int nativeElementSize) : this() {} @@ -647,7 +665,7 @@ class S public byte c; } -[GenericContiguousCollectionMarshaller] +[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.SpanCollection)] ref struct {|#0:Native|} { public Native(S s, Span stackSpace) : this() {} @@ -677,7 +695,7 @@ class S public byte c; } -[GenericContiguousCollectionMarshaller] +[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.SpanCollection)] ref struct {|#0:Native|} { public Native(S s, Span stackSpace, int nativeElementSize) : this() {} @@ -707,7 +725,7 @@ class S public byte c; } -[GenericContiguousCollectionMarshaller] +[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.SpanCollection)] ref struct {|#0:Native|} { public Native(S s, int nativeElementSize) : this() {} @@ -734,7 +752,7 @@ class S public byte c; } -[GenericContiguousCollectionMarshaller] +[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.SpanCollection)] ref struct {|#0:Native|} { public Native(S s, int nativeElementSize) : this() {} @@ -761,6 +779,7 @@ class S public byte c; } +[CustomTypeMarshaller(typeof(S))] struct Native { public Native(S s) {} @@ -782,6 +801,7 @@ class S public byte c; } +[CustomTypeMarshaller(typeof(S))] struct Native { public S ToManaged() => new S(); @@ -803,6 +823,7 @@ class S public byte c; } +[CustomTypeMarshaller(typeof(S))] struct {|#0:Native|} { public Native(S s, Span buffer) {} @@ -821,13 +842,14 @@ public async Task TypeWithOnlyNativeStackallocConstructorAndGetPinnableReference using System; using System.Runtime.InteropServices; -[{|#0:NativeMarshalling(typeof(Native))|}] -class S +[NativeMarshalling(typeof(Native))] +class {|#0:S|} { public byte c; public ref byte GetPinnableReference() => ref c; } +[CustomTypeMarshaller(typeof(S))] struct {|#1:Native|} { public Native(S s, Span buffer) {} @@ -855,6 +877,7 @@ class S public byte c; } +[CustomTypeMarshaller(typeof(S))] struct Native { public Native(S s) {} @@ -879,6 +902,7 @@ class S public byte c; } +[CustomTypeMarshaller(typeof(S))] struct Native { public S ToManaged() => new S(); @@ -902,6 +926,7 @@ struct S public string s; } +[CustomTypeMarshaller(typeof(S))] struct Native { private IntPtr value; @@ -936,6 +961,7 @@ struct S public string s; } +[CustomTypeMarshaller(typeof(S))] struct {|#0:Native|} { private string value; @@ -958,57 +984,6 @@ await VerifyCS.VerifyAnalyzerAsync(source, VerifyCS.Diagnostic(NativeTypeMustBeBlittableRule).WithLocation(0).WithArguments("Native", "S")); } - [ConditionalFact] - public async Task NonBlittableNativeTypeOnMarshalUsingParameter_MultipleCompilations_ReportsDiagnostic_WithLocation() - { - string source1 = @" -using System; -using System.Runtime.InteropServices; - -public struct S -{ - public string s; -} - -public struct Native -{ - private string value; - - public Native(S s) : this() - { - } - - public S ToManaged() => new S(); -} -"; - Compilation compilation1 = await TestUtils.CreateCompilation(source1); - - string source2 = @" -using System; -using System.Runtime.InteropServices; - -static class Test -{ - static void Foo([{|#0:MarshalUsing(typeof(Native))|}] S s) - {} -} -"; - var test = new Verifiers.CSharpCodeFixVerifier.Test - { - ExpectedDiagnostics = - { - VerifyCS.Diagnostic(NativeTypeMustBeBlittableRule).WithLocation(0).WithArguments("Native", "S") - }, - SolutionTransforms = - { - (solution, projectId) => solution.AddMetadataReference(projectId, compilation1.ToMetadataReference()) - }, - TestCode = source2 - }; - - await test.RunAsync(); - } - [ConditionalFact] public async Task NonBlittableNativeTypeOnMarshalUsingReturn_ReportsDiagnostic() { @@ -1021,6 +996,7 @@ struct S public string s; } +[CustomTypeMarshaller(typeof(S))] struct {|#0:Native|} { private string value; @@ -1055,6 +1031,7 @@ struct S public string s; } +[CustomTypeMarshaller(typeof(S))] struct {|#0:Native|} { private string value; @@ -1078,47 +1055,50 @@ await VerifyCS.VerifyAnalyzerAsync(source, } [ConditionalFact] - public async Task GenericNativeTypeWithValueTypeValueProperty_DoesNotReportDiagnostic() + public async Task GenericNativeTypeWithGenericMemberInstantiatedWithBlittable_DoesNotReportDiagnostic() { + string source = @" using System.Runtime.InteropServices; -[NativeMarshalling(typeof(Native))] +[NativeMarshalling(typeof(Native))] struct S { public string s; } +[CustomTypeMarshaller(typeof(S))] struct Native - where T : new() + where T : unmanaged { - public Native(T s) + public Native(S s) { - Value = 0; + Value = new T(); } - public T ToManaged() => new T(); + public S ToManaged() => new S(); - public int Value { get; set; } + public T Value { get; set; } }"; await VerifyCS.VerifyAnalyzerAsync(source); } [ConditionalFact] - public async Task GenericNativeTypeWithGenericMemberInstantiatedWithBlittable_DoesNotReportDiagnostic() + public async Task UninstantiatedGenericNativeTypeOnNonGeneric_ReportsDiagnostic() { string source = @" using System.Runtime.InteropServices; -[NativeMarshalling(typeof(Native))] +[{|#0:NativeMarshalling(typeof(Native<>))|}] struct S { public string s; } +[CustomTypeMarshaller(typeof(S))] struct Native - where T : new() + where T : unmanaged { public Native(S s) { @@ -1129,24 +1109,24 @@ public Native(S s) public T Value { get; set; } }"; - await VerifyCS.VerifyAnalyzerAsync(source); + await VerifyCS.VerifyAnalyzerAsync(source, VerifyCS.Diagnostic(NativeGenericTypeMustBeClosedOrMatchArityRule).WithLocation(0).WithArguments("Native<>", "S")); } [ConditionalFact] - public async Task UninstantiatedGenericNativeTypeOnNonGeneric_ReportsDiagnostic() + public async Task MarshalUsingUninstantiatedGenericNativeType_ReportsDiagnostic() { string source = @" using System.Runtime.InteropServices; -[{|#0:NativeMarshalling(typeof(Native<>))|}] struct S { public string s; } +[CustomTypeMarshaller(typeof(S))] struct Native - where T : new() + where T : unmanaged { public Native(S s) { @@ -1156,6 +1136,12 @@ public Native(S s) public S ToManaged() => new S(); public T Value { get; set; } +} + +static class Test +{ + static void Foo([{|#0:MarshalUsing(typeof(Native<>))|}] S s) + {} }"; await VerifyCS.VerifyAnalyzerAsync(source, VerifyCS.Diagnostic(NativeGenericTypeMustBeClosedOrMatchArityRule).WithLocation(0).WithArguments("Native<>", "S")); } @@ -1172,7 +1158,8 @@ struct S public string s; } -struct Native +[CustomTypeMarshaller(typeof(S<>))] +struct {|#1:Native|} where T : new() { public Native(S s) @@ -1184,7 +1171,9 @@ public Native(S s) public int Value { get; set; } }"; - await VerifyCS.VerifyAnalyzerAsync(source, VerifyCS.Diagnostic(NativeGenericTypeMustBeClosedOrMatchArityRule).WithLocation(0).WithArguments("Native<,>", "S")); + await VerifyCS.VerifyAnalyzerAsync(source, + VerifyCS.Diagnostic(NativeTypeMustHaveCustomTypeMarshallerAttributeRule).WithLocation(0).WithArguments("S"), + VerifyCS.Diagnostic(NativeGenericTypeMustBeClosedOrMatchArityRule).WithLocation(1).WithArguments("Native", "S<>")); } [ConditionalFact] @@ -1199,6 +1188,7 @@ struct S public T t; } +[CustomTypeMarshaller(typeof(S<>))] struct Native where T : new() { @@ -1227,6 +1217,7 @@ class S public byte c; } +[CustomTypeMarshaller(typeof(S))] struct Native { public Native(S s) {} 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 3f8e4747c2f37..0be74fbe74ed7 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/NonBlittable.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/NonBlittable.cs @@ -16,6 +16,7 @@ public struct StringContainer public string str2; } + [CustomTypeMarshaller(typeof(StringContainer))] public struct StringContainerNative { public IntPtr str1; @@ -43,6 +44,7 @@ public void FreeNative() } } + [CustomTypeMarshaller(typeof(double))] public struct DoubleToLongMarshaler { public long l; @@ -69,6 +71,7 @@ public struct BoolStruct public bool b3; } + [CustomTypeMarshaller(typeof(BoolStruct))] public struct BoolStructNative { public byte b1; @@ -100,6 +103,7 @@ public class IntWrapper public ref int GetPinnableReference() => ref i; } + [CustomTypeMarshaller(typeof(IntWrapper))] public unsafe struct IntWrapperMarshaler { public IntWrapperMarshaler(IntWrapper managed) @@ -118,6 +122,7 @@ public void FreeNative() } } + [CustomTypeMarshaller(typeof(string))] public unsafe ref struct Utf16StringMarshaler { private ushort* allocated; @@ -201,6 +206,7 @@ public struct IntStructWrapper public int Value; } + [CustomTypeMarshaller(typeof(IntStructWrapper))] public struct IntStructWrapperNative { public int value; @@ -212,7 +218,7 @@ public IntStructWrapperNative(IntStructWrapper managed) public IntStructWrapper ToManaged() => new IntStructWrapper { Value = value }; } - [GenericContiguousCollectionMarshaller] + [CustomTypeMarshaller(typeof(List<>), CustomTypeMarshallerKind.SpanCollection)] public unsafe ref struct ListMarshaller { private List managedList; From cd04737a3d822b7d13470d3857a101abddfe0710 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 17 Feb 2022 15:20:19 -0800 Subject: [PATCH 02/24] Do some renaming to be consistent. --- .../ManualTypeMarshallingHelper.cs | 2 +- ...AttributedMarshallingModelGeneratorFactory.cs | 8 ++++---- .../ICustomNativeTypeMarshallingStrategy.cs | 16 ++++++++-------- .../MarshallingAttributeInfo.cs | 2 +- ...anCollectionElementMarshallingCodeContext.cs} | 6 +++--- 5 files changed, 17 insertions(+), 17 deletions(-) rename src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/{ContiguousCollectionElementMarshallingCodeContext.cs => SpanCollectionElementMarshallingCodeContext.cs} (91%) 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 586ee91667b68..03a50205c50be 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 @@ -164,7 +164,7 @@ public static bool TryGetManagedValuesProperty(ITypeSymbol type, out IPropertySy return managedValuesProperty is not null; } - public static bool TryGetElementTypeFromContiguousCollectionMarshaller(ITypeSymbol type, out ITypeSymbol elementType) + public static bool TryGetElementTypeFromSpanCollectionMarshaller(ITypeSymbol type, out ITypeSymbol elementType) { if (!TryGetManagedValuesProperty(type, out IPropertySymbol managedValuesProperty)) { 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 3484e42713015..0b54b52774b4b 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 @@ -129,7 +129,7 @@ static ExpressionSyntax GetIndexedNumElementsExpression(StubCodeContext context, while (currentContext is not null) { - if (currentContext is ContiguousCollectionElementMarshallingCodeContext collectionContext) + if (currentContext is SpanCollectionElementMarshallingCodeContext collectionContext) { indexerStack.Push(collectionContext.IndexerIdentifier); } @@ -257,18 +257,18 @@ private IMarshallingGenerator CreateNativeCollectionMarshaller( var elementInfo = new TypePositionInfo(collectionInfo.ElementType, collectionInfo.ElementMarshallingInfo) { ManagedIndex = info.ManagedIndex }; IMarshallingGenerator elementMarshaller = _elementMarshallingGenerator.Create( elementInfo, - new ContiguousCollectionElementMarshallingCodeContext(StubCodeContext.Stage.Setup, string.Empty, context)); + new SpanCollectionElementMarshallingCodeContext(StubCodeContext.Stage.Setup, string.Empty, context)); TypeSyntax elementType = elementMarshaller.AsNativeType(elementInfo); bool isBlittable = elementMarshaller is BlittableMarshaller; if (isBlittable) { - marshallingStrategy = new ContiguousBlittableElementCollectionMarshalling(marshallingStrategy, collectionInfo.ElementType.Syntax); + marshallingStrategy = new SpanCollectionWithBlittableElementsMarshalling(marshallingStrategy, collectionInfo.ElementType.Syntax); } else { - marshallingStrategy = new ContiguousNonBlittableElementCollectionMarshalling(marshallingStrategy, elementMarshaller, elementInfo); + marshallingStrategy = new SpanCollectionWithNonBlittableElementsMarshalling(marshallingStrategy, elementMarshaller, elementInfo); } // Explicitly insert the Value property handling here (before numElements handling) so that the numElements handling will be emitted before the Value property handling in unmarshalling. diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ICustomNativeTypeMarshallingStrategy.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ICustomNativeTypeMarshallingStrategy.cs index f1b4cc171aa9f..5e0dba589d2f4 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ICustomNativeTypeMarshallingStrategy.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ICustomNativeTypeMarshallingStrategy.cs @@ -708,14 +708,14 @@ public bool UsesNativeIdentifier(TypePositionInfo info, StubCodeContext context) } /// - /// Marshaller that enables support for marshalling blittable elements of a contiguous collection via a native type that implements the contiguous collection marshalling spec. + /// Marshaller that enables support for marshalling blittable elements of a collection via a native type that implements the SpanCollection marshalling spec. /// - internal sealed class ContiguousBlittableElementCollectionMarshalling : ICustomNativeTypeMarshallingStrategy + internal sealed class SpanCollectionWithBlittableElementsMarshalling : ICustomNativeTypeMarshallingStrategy { private readonly ICustomNativeTypeMarshallingStrategy _innerMarshaller; private readonly TypeSyntax _elementType; - public ContiguousBlittableElementCollectionMarshalling(ICustomNativeTypeMarshallingStrategy innerMarshaller, TypeSyntax elementType) + public SpanCollectionWithBlittableElementsMarshalling(ICustomNativeTypeMarshallingStrategy innerMarshaller, TypeSyntax elementType) { _innerMarshaller = innerMarshaller; _elementType = elementType; @@ -848,15 +848,15 @@ public bool UsesNativeIdentifier(TypePositionInfo info, StubCodeContext context) } /// - /// Marshaller that enables support for marshalling non-blittable elements of a contiguous collection via a native type that implements the contiguous collection marshalling spec. + /// Marshaller that enables support for marshalling non-blittable elements of a collection via a native type that implements the SpanCollection marshalling spec. /// - internal sealed class ContiguousNonBlittableElementCollectionMarshalling : ICustomNativeTypeMarshallingStrategy + internal sealed class SpanCollectionWithNonBlittableElementsMarshalling : ICustomNativeTypeMarshallingStrategy { private readonly ICustomNativeTypeMarshallingStrategy _innerMarshaller; private readonly IMarshallingGenerator _elementMarshaller; private readonly TypePositionInfo _elementInfo; - public ContiguousNonBlittableElementCollectionMarshalling(ICustomNativeTypeMarshallingStrategy innerMarshaller, + public SpanCollectionWithNonBlittableElementsMarshalling(ICustomNativeTypeMarshallingStrategy innerMarshaller, IMarshallingGenerator elementMarshaller, TypePositionInfo elementInfo) { @@ -902,11 +902,11 @@ private StatementSyntax GenerateContentsMarshallingStatement(TypePositionInfo in { string nativeIdentifier = context.GetIdentifiers(info).native; string nativeSpanIdentifier = MarshallerHelpers.GetNativeSpanIdentifier(info, context); - var elementSetupSubContext = new ContiguousCollectionElementMarshallingCodeContext( + var elementSetupSubContext = new SpanCollectionElementMarshallingCodeContext( StubCodeContext.Stage.Setup, nativeSpanIdentifier, context); - var elementSubContext = new ContiguousCollectionElementMarshallingCodeContext( + var elementSubContext = new SpanCollectionElementMarshallingCodeContext( context.CurrentStage, nativeSpanIdentifier, context); 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 7b14e3313782e..68dceb99713bb 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 @@ -672,7 +672,7 @@ private MarshallingInfo CreateNativeMarshallingInfo( return NoMarshallingInfo.Instance; } - if (!ManualTypeMarshallingHelper.TryGetElementTypeFromContiguousCollectionMarshaller(nativeType, out ITypeSymbol elementType)) + if (!ManualTypeMarshallingHelper.TryGetElementTypeFromSpanCollectionMarshaller(nativeType, out ITypeSymbol elementType)) { _diagnostics.ReportInvalidMarshallingAttributeInfo(attrData, nameof(Resources.CollectionNativeTypeMustHaveRequiredShapeMessage), nativeType.ToDisplayString()); return NoMarshallingInfo.Instance; diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/ContiguousCollectionElementMarshallingCodeContext.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/SpanCollectionElementMarshallingCodeContext.cs similarity index 91% rename from src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/ContiguousCollectionElementMarshallingCodeContext.cs rename to src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/SpanCollectionElementMarshallingCodeContext.cs index 02d965c9f29ce..2710ce98ba3cd 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/ContiguousCollectionElementMarshallingCodeContext.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/SpanCollectionElementMarshallingCodeContext.cs @@ -3,7 +3,7 @@ namespace Microsoft.Interop { - internal sealed class ContiguousCollectionElementMarshallingCodeContext : StubCodeContext + internal sealed class SpanCollectionElementMarshallingCodeContext : StubCodeContext { private readonly string _nativeSpanIdentifier; @@ -20,7 +20,7 @@ internal sealed class ContiguousCollectionElementMarshallingCodeContext : StubCo /// The indexer in the loop to get the element to marshal from the collection. /// The identifier of the native value storage cast to the target element type. /// The parent context. - public ContiguousCollectionElementMarshallingCodeContext( + public SpanCollectionElementMarshallingCodeContext( Stage currentStage, string nativeSpanIdentifier, StubCodeContext parentContext) @@ -55,7 +55,7 @@ private static string CalculateIndexerIdentifierBasedOnParentContext(StubCodeCon int i = 0; while (parentContext is StubCodeContext context) { - if (context is ContiguousCollectionElementMarshallingCodeContext) + if (context is SpanCollectionElementMarshallingCodeContext) { i++; } From 1909ae2b15c926c6043eeb3412f25e87a55311bb Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 17 Feb 2022 16:35:53 -0800 Subject: [PATCH 03/24] Add a placeholder type to support the array marshallers. --- .../InteropServices/ArrayMarshaller.cs | 24 ++--- .../GeneratedMarshallingAttribute.cs | 23 ++--- .../ManualTypeMarshallingAnalyzer.cs | 20 ++-- .../ManualTypeMarshallingHelper.cs | 94 +++++++++++++++++-- ...ributedMarshallingModelGeneratorFactory.cs | 12 ++- .../Marshalling/Forwarder.cs | 2 +- .../ICustomNativeTypeMarshallingStrategy.cs | 14 ++- .../Marshalling/MarshallerHelpers.cs | 2 +- .../MarshallingAttributeInfo.cs | 42 ++++++--- .../TypeNames.cs | 2 + .../ManualTypeMarshallingAnalyzerTests.cs | 48 ++++++++++ .../TestAssets/SharedTypes/NonBlittable.cs | 15 +-- 12 files changed, 211 insertions(+), 87 deletions(-) diff --git a/src/libraries/Common/src/System/Runtime/InteropServices/ArrayMarshaller.cs b/src/libraries/Common/src/System/Runtime/InteropServices/ArrayMarshaller.cs index 7dfa067b00103..0bda52b9f052a 100644 --- a/src/libraries/Common/src/System/Runtime/InteropServices/ArrayMarshaller.cs +++ b/src/libraries/Common/src/System/Runtime/InteropServices/ArrayMarshaller.cs @@ -15,6 +15,10 @@ namespace System.Runtime.InteropServices.GeneratedMarshalling { + // Stack-alloc threshold set to 256 bytes to enable small arrays to be passed on the stack. + // Number kept small to ensure that P/Invokes with a lot of array parameters doesn't + // blow the stack since this is a new optimization in the code-generated interop. + [CustomTypeMarshaller(typeof(CustomTypeMarshallerAttribute.GenericPlaceholder[]), CustomTypeMarshallerKind.SpanCollection, BufferSize = 0x200, RequiresStackBuffer = true)] #if DLLIMPORT_GENERATOR_TEST public #else @@ -61,14 +65,6 @@ public ArrayMarshaller(T[]? managed, Span stackSpace, int sizeOfNativeElem } } - /// - /// Stack-alloc threshold set to 256 bytes to enable small arrays to be passed on the stack. - /// Number kept small to ensure that P/Invokes with a lot of array parameters doesn't - /// blow the stack since this is a new optimization in the code-generated interop. - /// - public const int BufferSize = 0x200; - public const bool RequiresStackBuffer = true; - public Span ManagedValues => _managedArray; public Span NativeValueStorage { get; private set; } @@ -109,6 +105,10 @@ public void FreeNative() } } + // Stack-alloc threshold set to 256 bytes to enable small arrays to be passed on the stack. + // Number kept small to ensure that P/Invokes with a lot of array parameters doesn't + // blow the stack since this is a new optimization in the code-generated interop. + [CustomTypeMarshaller(typeof(CustomTypeMarshallerAttribute.GenericPlaceholder*[]), CustomTypeMarshallerKind.SpanCollection, BufferSize = 0x200, RequiresStackBuffer = true)] #if DLLIMPORT_GENERATOR_TEST public #else @@ -155,14 +155,6 @@ public PtrArrayMarshaller(T*[]? managed, Span stackSpace, int sizeOfNative } } - /// - /// Stack-alloc threshold set to 256 bytes to enable small arrays to be passed on the stack. - /// Number kept small to ensure that P/Invokes with a lot of array parameters doesn't - /// blow the stack since this is a new optimization in the code-generated interop. - /// - public const int BufferSize = 0x200; - public const bool RequiresStackBuffer = true; - public Span ManagedValues => Unsafe.As(_managedArray); public Span NativeValueStorage { get; private set; } diff --git a/src/libraries/Common/src/System/Runtime/InteropServices/GeneratedMarshallingAttribute.cs b/src/libraries/Common/src/System/Runtime/InteropServices/GeneratedMarshallingAttribute.cs index 5572df405eb04..330efa3d7eb71 100644 --- a/src/libraries/Common/src/System/Runtime/InteropServices/GeneratedMarshallingAttribute.cs +++ b/src/libraries/Common/src/System/Runtime/InteropServices/GeneratedMarshallingAttribute.cs @@ -80,6 +80,16 @@ public CustomTypeMarshallerAttribute(Type managedType, CustomTypeMarshallerKind public Type ManagedType { get; } public CustomTypeMarshallerKind MarshallerKind { get; } + public int BufferSize { get; set; } + public bool RequiresStackBuffer { get; set; } + + /// + /// This type is used as a placeholder for the first generic parameter when generic parameters cannot be used + /// to identify the managed type (i.e. when the marshaller type is generic over T and the managed type is T[]) + /// + public struct GenericPlaceholder + { + } } #if DLLIMPORT_GENERATOR_TEST @@ -92,17 +102,4 @@ enum CustomTypeMarshallerKind Value, SpanCollection } - - [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class)] -#if DLLIMPORT_GENERATOR_TEST - public -#else - internal -#endif - sealed class GenericContiguousCollectionMarshallerAttribute : Attribute - { - public GenericContiguousCollectionMarshallerAttribute() - { - } - } } diff --git a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Analyzers/ManualTypeMarshallingAnalyzer.cs b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Analyzers/ManualTypeMarshallingAnalyzer.cs index df521c67e4062..e45e6008bb576 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Analyzers/ManualTypeMarshallingAnalyzer.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Analyzers/ManualTypeMarshallingAnalyzer.cs @@ -296,6 +296,8 @@ private static void AnalyzeManagedTypeMarshallingInfo(SymbolAnalysisContext cont (bool hasCustomTypeMarshallerAttribute, ITypeSymbol? marshallerManagedType, _) = ManualTypeMarshallingHelper.GetMarshallerShapeInfo(marshallerType); + marshallerManagedType = ManualTypeMarshallingHelper.ResolveManagedType(marshallerManagedType, marshallerType, context.Compilation); + if (!hasCustomTypeMarshallerAttribute) { context.ReportDiagnostic( @@ -336,7 +338,9 @@ private static bool TypeSymbolsConstructedFromEqualTypes(ITypeSymbol left, IType public void AnalyzeMarshallerType(SymbolAnalysisContext context) { INamedTypeSymbol marshallerType = (INamedTypeSymbol)context.Symbol; - (bool hasCustomTypeMarshallerAttribute, ITypeSymbol? type, ManualTypeMarshallingHelper.CustomTypeMarshallerKind? marshallerKind) = ManualTypeMarshallingHelper.GetMarshallerShapeInfo(marshallerType); + (bool hasCustomTypeMarshallerAttribute, ITypeSymbol? type, CustomTypeMarshallerData? marshallerData) = ManualTypeMarshallingHelper.GetMarshallerShapeInfo(marshallerType); + type = ManualTypeMarshallingHelper.ResolveManagedType(type, marshallerType, context.Compilation); + if (!hasCustomTypeMarshallerAttribute) { return; @@ -347,7 +351,7 @@ public void AnalyzeMarshallerType(SymbolAnalysisContext context) return; } - if (marshallerKind == null || !Enum.IsDefined(typeof(ManualTypeMarshallingHelper.CustomTypeMarshallerKind), marshallerKind)) + if (marshallerData == null || !Enum.IsDefined(typeof(CustomTypeMarshallerKind), marshallerData.Value.Kind)) { context.ReportDiagnostic(marshallerType.CreateDiagnostic(MarshallerKindMustBeValidRule, marshallerType.ToDisplayString())); } @@ -366,10 +370,10 @@ public void AnalyzeMarshallerType(SymbolAnalysisContext context) type = generic.ConstructedFrom.Construct(marshallerType.TypeArguments, marshallerType.TypeArgumentNullableAnnotations); } - DiagnosticDescriptor requiredShapeRule = marshallerKind switch + DiagnosticDescriptor requiredShapeRule = marshallerData.Value.Kind switch { - ManualTypeMarshallingHelper.CustomTypeMarshallerKind.Value => NativeTypeMustHaveRequiredShapeRule, - ManualTypeMarshallingHelper.CustomTypeMarshallerKind.SpanCollection => CollectionNativeTypeMustHaveRequiredShapeRule, + CustomTypeMarshallerKind.Value => NativeTypeMustHaveRequiredShapeRule, + CustomTypeMarshallerKind.SpanCollection => CollectionNativeTypeMustHaveRequiredShapeRule, _ => throw new InvalidOperationException() }; @@ -391,9 +395,9 @@ public void AnalyzeMarshallerType(SymbolAnalysisContext context) continue; } - hasConstructor = hasConstructor || ManualTypeMarshallingHelper.IsManagedToNativeConstructor(ctor, type, marshallerKind.Value); + hasConstructor = hasConstructor || ManualTypeMarshallingHelper.IsManagedToNativeConstructor(ctor, type, marshallerData.Value.Kind); - if (!hasCallerAllocSpanConstructor && ManualTypeMarshallingHelper.IsCallerAllocatedSpanConstructor(ctor, type, _spanOfByte, marshallerKind.Value)) + if (!hasCallerAllocSpanConstructor && ManualTypeMarshallingHelper.IsCallerAllocatedSpanConstructor(ctor, type, _spanOfByte, marshallerData.Value.Kind)) { hasCallerAllocSpanConstructor = true; IFieldSymbol bufferSizeField = marshallerType.GetMembers(ManualTypeMarshallingHelper.BufferSizeFieldName).OfType().FirstOrDefault(); @@ -407,7 +411,7 @@ public void AnalyzeMarshallerType(SymbolAnalysisContext context) } } - if (marshallerKind == ManualTypeMarshallingHelper.CustomTypeMarshallerKind.SpanCollection) + if (marshallerData.Value.Kind == CustomTypeMarshallerKind.SpanCollection) { requiredShapeRule = CollectionNativeTypeMustHaveRequiredShapeRule; if (!ManualTypeMarshallingHelper.TryGetManagedValuesProperty(marshallerType, out _) 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 03a50205c50be..bab0651781904 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 @@ -2,11 +2,19 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis; namespace Microsoft.Interop { + public enum CustomTypeMarshallerKind + { + Value, + SpanCollection + } + public readonly record struct CustomTypeMarshallerData(CustomTypeMarshallerKind Kind, int? BufferSize, bool RequiresStackBuffer); public static class ManualTypeMarshallingHelper { public const string ValuePropertyName = "Value"; @@ -26,13 +34,7 @@ public static class MarshalUsingProperties public const string ConstantElementCount = nameof(ConstantElementCount); } - public enum CustomTypeMarshallerKind - { - Value, - SpanCollection - } - - public static (bool hasAttribute, ITypeSymbol? managedType, CustomTypeMarshallerKind? kind) GetMarshallerShapeInfo(ITypeSymbol marshallerType) + public static (bool hasAttribute, ITypeSymbol? managedType, CustomTypeMarshallerData? kind) GetMarshallerShapeInfo(ITypeSymbol marshallerType) { var attr = marshallerType.GetAttributes().FirstOrDefault(attr => attr.AttributeClass.ToDisplayString() == TypeNames.CustomTypeMarshallerAttribute); if (attr is null) @@ -43,11 +45,83 @@ public static (bool hasAttribute, ITypeSymbol? managedType, CustomTypeMarshaller { return (true, null, null); } - if (attr.ConstructorArguments.Length == 1) + CustomTypeMarshallerKind kind = CustomTypeMarshallerKind.Value; + ITypeSymbol? managedType = attr.ConstructorArguments[0].Value as ITypeSymbol; + if (attr.ConstructorArguments.Length >= 1) + { + if (attr.ConstructorArguments[1].Value is not int i) + { + return (true, managedType, null); + } + kind = (CustomTypeMarshallerKind)i; + } + var namedArguments = attr.NamedArguments.ToImmutableDictionary(); + int? bufferSize = null; + if (namedArguments.TryGetValue(BufferSizeFieldName, out TypedConstant bufferSizeConstant)) + { + bufferSize = bufferSizeConstant.Value as int?; + } + bool requiresStackBuffer = false; + if (namedArguments.TryGetValue(RequiresStackBufferFieldName, out TypedConstant requiresStackBufferConstant)) + { + requiresStackBuffer = bufferSizeConstant.Value as bool? ?? false; + } + return (true, managedType, new CustomTypeMarshallerData(kind, bufferSize, requiresStackBuffer)); + } + + /// + /// 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 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) + { + if (managedType is null or INamedTypeSymbol || !marshallerType.IsGenericType) + { + return managedType; + } + Stack typeStack = new(); + ITypeSymbol? innerType = managedType; + while (innerType.TypeKind is TypeKind.Array or TypeKind.Pointer) + { + if (innerType is IArrayTypeSymbol array) + { + typeStack.Push(innerType); + innerType = array.ElementType; + } + else if (innerType is IPointerTypeSymbol pointerType) + { + typeStack.Push(innerType); + innerType = pointerType.PointedAtType; + } + } + + if (innerType.ToDisplayString() != TypeNames.CustomTypeMarshallerAttributeGenericPlaceholder) { - return (true, attr.ConstructorArguments[0].Value as ITypeSymbol, CustomTypeMarshallerKind.Value); + return managedType; + } + + ITypeSymbol resultType = marshallerType.TypeArguments[0]; + + while (typeStack.Count > 0) + { + ITypeSymbol wrapperType = typeStack.Pop(); + if (wrapperType.TypeKind == TypeKind.Pointer) + { + resultType = compilation.CreatePointerTypeSymbol(resultType); + } + else if (wrapperType.TypeKind == TypeKind.Array) + { + IArrayTypeSymbol arrayType = (IArrayTypeSymbol)wrapperType; + if (arrayType.IsSZArray) + { + resultType = compilation.CreateArrayTypeSymbol(resultType, arrayType.Rank); + } + } } - return (true, attr.ConstructorArguments[0].Value as ITypeSymbol, attr.ConstructorArguments[1].Value is int i ? (CustomTypeMarshallerKind)i : null); + return resultType; } public static (AttributeData? attribute, INamedTypeSymbol? marshallerType) GetDefaultMarshallerInfo(ITypeSymbol managedType) 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 0b54b52774b4b..0c870695ef99f 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 @@ -93,7 +93,7 @@ ExpressionSyntax GetExpressionForParam(TypePositionInfo paramInfo) for (int i = 0; i < numIndirectionLevels; i++) { - if (marshallingInfo is NativeContiguousCollectionMarshallingInfo collectionInfo) + if (marshallingInfo is NativeSpanCollectionMarshallingInfo collectionInfo) { type = collectionInfo.ElementType; marshallingInfo = collectionInfo.ElementMarshallingInfo; @@ -159,7 +159,11 @@ private IMarshallingGenerator CreateCustomNativeTypeMarshaller(TypePositionInfo if ((marshalInfo.MarshallingFeatures & CustomMarshallingFeatures.ManagedToNativeStackalloc) != 0) { - marshallingStrategy = new StackallocOptimizationMarshalling(marshallingStrategy); + if (marshalInfo.BufferSize is null) + { + throw new MarshallingNotSupportedException(info, context); + } + marshallingStrategy = new StackallocOptimizationMarshalling(marshallingStrategy, marshalInfo.BufferSize.Value); } if ((marshalInfo.MarshallingFeatures & CustomMarshallingFeatures.FreeNativeResources) != 0) @@ -168,7 +172,7 @@ private IMarshallingGenerator CreateCustomNativeTypeMarshaller(TypePositionInfo } // Collections have extra configuration, so handle them here. - if (marshalInfo is NativeContiguousCollectionMarshallingInfo collectionMarshallingInfo) + if (marshalInfo is NativeSpanCollectionMarshallingInfo collectionMarshallingInfo) { return CreateNativeCollectionMarshaller(info, context, collectionMarshallingInfo, marshallingStrategy); } @@ -251,7 +255,7 @@ private ICustomNativeTypeMarshallingStrategy DecorateWithValuePropertyStrategy(N private IMarshallingGenerator CreateNativeCollectionMarshaller( TypePositionInfo info, StubCodeContext context, - NativeContiguousCollectionMarshallingInfo collectionInfo, + NativeSpanCollectionMarshallingInfo collectionInfo, ICustomNativeTypeMarshallingStrategy marshallingStrategy) { var elementInfo = new TypePositionInfo(collectionInfo.ElementType, collectionInfo.ElementMarshallingInfo) { ManagedIndex = info.ManagedIndex }; diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/Forwarder.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/Forwarder.cs index 92533d720031e..79fd1c9066d3e 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/Forwarder.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/Forwarder.cs @@ -41,7 +41,7 @@ private bool TryRehydrateMarshalAsAttribute(TypePositionInfo info, out Attribute { CountInfo countInfo; MarshallingInfo elementMarshallingInfo; - if (info.MarshallingAttributeInfo is NativeContiguousCollectionMarshallingInfo collectionMarshalling + if (info.MarshallingAttributeInfo is NativeSpanCollectionMarshallingInfo collectionMarshalling && collectionMarshalling.UseDefaultMarshalling && collectionMarshalling.ElementCountInfo is NoCountInfo or SizeAndParamIndexInfo && collectionMarshalling.ElementMarshallingInfo is NoMarshallingInfo or MarshalAsInfo { UnmanagedType: not UnmanagedType.CustomMarshaler } diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ICustomNativeTypeMarshallingStrategy.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ICustomNativeTypeMarshallingStrategy.cs index 5e0dba589d2f4..3378939dbfb63 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ICustomNativeTypeMarshallingStrategy.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ICustomNativeTypeMarshallingStrategy.cs @@ -281,10 +281,12 @@ public IEnumerable GeneratePinStatements(TypePositionInfo info, internal sealed class StackallocOptimizationMarshalling : ICustomNativeTypeMarshallingStrategy { private readonly ICustomNativeTypeMarshallingStrategy _innerMarshaller; + private readonly int _bufferSize; - public StackallocOptimizationMarshalling(ICustomNativeTypeMarshallingStrategy innerMarshaller) + public StackallocOptimizationMarshalling(ICustomNativeTypeMarshallingStrategy innerMarshaller, int bufferSize) { _innerMarshaller = innerMarshaller; + _bufferSize = bufferSize; } public ArgumentSyntax AsArgument(TypePositionInfo info, StubCodeContext context) @@ -306,7 +308,7 @@ public IEnumerable GenerateMarshalStatements(TypePositionInfo i { if (StackAllocOptimizationValid(info, context)) { - // byte* __stackptr = stackalloc byte[<_nativeLocalType>.BufferSize]; + // byte* __stackptr = stackalloc byte[<_bufferSize>]; yield return LocalDeclarationStatement( VariableDeclaration( PointerType(PredefinedType(Token(SyntaxKind.ByteKeyword))), @@ -317,9 +319,7 @@ public IEnumerable GenerateMarshalStatements(TypePositionInfo i ArrayType( PredefinedType(Token(SyntaxKind.ByteKeyword)), SingletonList(ArrayRankSpecifier(SingletonSeparatedList( - MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, - AsNativeType(info), - IdentifierName(ManualTypeMarshallingHelper.BufferSizeFieldName)) + LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(_bufferSize)) )))))))))); } @@ -371,9 +371,7 @@ public IEnumerable GetNativeTypeConstructorArguments(TypePositio ArgumentList(SeparatedList(new ArgumentSyntax[] { Argument(IdentifierName(GetStackAllocPointerIdentifier(info, context))), - Argument(MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, - AsNativeType(info), - IdentifierName(ManualTypeMarshallingHelper.BufferSizeFieldName))) + Argument(LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(_bufferSize))) })))); } } diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs index 01b0c4aae2bf5..2497c18e5d02d 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs @@ -232,7 +232,7 @@ public bool AnyIncomingEdge(int to) public static IEnumerable GetDependentElementsOfMarshallingInfo( MarshallingInfo elementMarshallingInfo) { - if (elementMarshallingInfo is NativeContiguousCollectionMarshallingInfo nestedCollection) + if (elementMarshallingInfo is NativeSpanCollectionMarshallingInfo nestedCollection) { if (nestedCollection.ElementCountInfo is CountElementCountInfo { ElementInfo: TypePositionInfo nestedCountElement }) { 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 68dceb99713bb..9d1814f842357 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 @@ -131,7 +131,8 @@ public record NativeMarshallingAttributeInfo( ManagedTypeInfo NativeMarshallingType, ManagedTypeInfo? ValuePropertyType, CustomMarshallingFeatures MarshallingFeatures, - bool UseDefaultMarshalling) : MarshallingInfo; + bool UseDefaultMarshalling, + int? BufferSize) : MarshallingInfo; /// /// User-applied System.Runtime.InteropServices.GeneratedMarshallingAttribute @@ -149,18 +150,20 @@ public sealed record SafeHandleMarshallingInfo(bool AccessibleDefaultConstructor /// User-applied System.Runtime.InteropServices.NativeMarshallingAttribute /// with a contiguous collection marshaller /// - public sealed record NativeContiguousCollectionMarshallingInfo( + public sealed record NativeSpanCollectionMarshallingInfo( ManagedTypeInfo NativeMarshallingType, ManagedTypeInfo? ValuePropertyType, CustomMarshallingFeatures MarshallingFeatures, bool UseDefaultMarshalling, + int? BufferSize, CountInfo ElementCountInfo, ManagedTypeInfo ElementType, MarshallingInfo ElementMarshallingInfo) : NativeMarshallingAttributeInfo( NativeMarshallingType, ValuePropertyType, MarshallingFeatures, - UseDefaultMarshalling + UseDefaultMarshalling, + BufferSize ); @@ -553,13 +556,18 @@ private MarshallingInfo CreateInfoFromMarshalAs( return new MissingSupportCollectionMarshallingInfo(arraySizeInfo, elementMarshallingInfo); } + var (_, _, customTypeMarshallerData) = ManualTypeMarshallingHelper.GetMarshallerShapeInfo(arrayMarshaller); + + Debug.Assert(customTypeMarshallerData is not null); + ITypeSymbol? valuePropertyType = ManualTypeMarshallingHelper.FindValueProperty(arrayMarshaller)?.Type; - return new NativeContiguousCollectionMarshallingInfo( + return new NativeSpanCollectionMarshallingInfo( NativeMarshallingType: ManagedTypeInfo.CreateTypeInfoForTypeSymbol(arrayMarshaller), ValuePropertyType: valuePropertyType is not null ? ManagedTypeInfo.CreateTypeInfoForTypeSymbol(valuePropertyType) : null, MarshallingFeatures: ~CustomMarshallingFeatures.ManagedTypePinning, UseDefaultMarshalling: true, + BufferSize: customTypeMarshallerData.Value.BufferSize, ElementCountInfo: arraySizeInfo, ElementType: ManagedTypeInfo.CreateTypeInfoForTypeSymbol(elementType), ElementMarshallingInfo: elementMarshallingInfo); @@ -612,17 +620,21 @@ private MarshallingInfo CreateNativeMarshallingInfo( } } - var (_, _, marshallingVariant) = ManualTypeMarshallingHelper.GetMarshallerShapeInfo(nativeType); + var (_, _, customTypeMarshallerData) = ManualTypeMarshallingHelper.GetMarshallerShapeInfo(nativeType); + if (customTypeMarshallerData is null) + { + return NoMarshallingInfo.Instance; + } IPropertySymbol? valueProperty = ManualTypeMarshallingHelper.FindValueProperty(nativeType); bool hasInt32Constructor = false; foreach (IMethodSymbol ctor in nativeType.Constructors) { - if (ManualTypeMarshallingHelper.IsManagedToNativeConstructor(ctor, type, marshallingVariant.Value) && (valueProperty is null or { GetMethod: not null })) + if (ManualTypeMarshallingHelper.IsManagedToNativeConstructor(ctor, type, customTypeMarshallerData.Value.Kind) && (valueProperty is null or { GetMethod: not null })) { features |= CustomMarshallingFeatures.ManagedToNative; } - else if (ManualTypeMarshallingHelper.IsCallerAllocatedSpanConstructor(ctor, type, spanOfByte, marshallingVariant.Value) + else if (ManualTypeMarshallingHelper.IsCallerAllocatedSpanConstructor(ctor, type, spanOfByte, customTypeMarshallerData.Value.Kind) && (valueProperty is null or { GetMethod: not null })) { features |= CustomMarshallingFeatures.ManagedToNativeStackalloc; @@ -635,7 +647,7 @@ private MarshallingInfo CreateNativeMarshallingInfo( // The constructor that takes only the native element size is required for collection marshallers // in the native-to-managed scenario. - if ((marshallingVariant != ManualTypeMarshallingHelper.CustomTypeMarshallerKind.SpanCollection + if ((customTypeMarshallerData.Value.Kind != CustomTypeMarshallerKind.SpanCollection || (hasInt32Constructor && ManualTypeMarshallingHelper.HasSetUnmarshalledCollectionLengthMethod(nativeType))) && ManualTypeMarshallingHelper.HasToManagedMethod(nativeType, type) && (valueProperty is null or { SetMethod: not null })) @@ -647,7 +659,7 @@ private MarshallingInfo CreateNativeMarshallingInfo( { _diagnostics.ReportInvalidMarshallingAttributeInfo( attrData, - marshallingVariant == ManualTypeMarshallingHelper.CustomTypeMarshallerKind.SpanCollection + customTypeMarshallerData.Value.Kind == CustomTypeMarshallerKind.SpanCollection ? nameof(Resources.CollectionNativeTypeMustHaveRequiredShapeMessage) : nameof(Resources.NativeTypeMustHaveRequiredShapeMessage), nativeType.ToDisplayString()); @@ -664,7 +676,7 @@ private MarshallingInfo CreateNativeMarshallingInfo( features |= CustomMarshallingFeatures.NativeTypePinning; } - if (marshallingVariant == ManualTypeMarshallingHelper.CustomTypeMarshallerKind.SpanCollection) + if (customTypeMarshallerData.Value.Kind == CustomTypeMarshallerKind.SpanCollection) { if (!ManualTypeMarshallingHelper.HasNativeValueStorageProperty(nativeType, spanOfByte)) { @@ -678,11 +690,12 @@ private MarshallingInfo CreateNativeMarshallingInfo( return NoMarshallingInfo.Instance; } - return new NativeContiguousCollectionMarshallingInfo( + return new NativeSpanCollectionMarshallingInfo( ManagedTypeInfo.CreateTypeInfoForTypeSymbol(nativeType), valueProperty is not null ? ManagedTypeInfo.CreateTypeInfoForTypeSymbol(valueProperty.Type) : null, features, UseDefaultMarshalling: !isMarshalUsingAttribute, + customTypeMarshallerData.Value.BufferSize, parsedCountInfo, ManagedTypeInfo.CreateTypeInfoForTypeSymbol(elementType), GetMarshallingInfo(elementType, useSiteAttributes, indirectionLevel + 1, inspectedElements, ref maxIndirectionLevelUsed)); @@ -692,7 +705,8 @@ private MarshallingInfo CreateNativeMarshallingInfo( ManagedTypeInfo.CreateTypeInfoForTypeSymbol(nativeType), valueProperty is not null ? ManagedTypeInfo.CreateTypeInfoForTypeSymbol(valueProperty.Type) : null, features, - UseDefaultMarshalling: !isMarshalUsingAttribute); + UseDefaultMarshalling: !isMarshalUsingAttribute, + customTypeMarshallerData.Value.BufferSize); } private bool TryCreateTypeBasedMarshallingInfo( @@ -751,13 +765,15 @@ private bool TryCreateTypeBasedMarshallingInfo( return true; } + var (_, _, customTypeMarshallerData) = ManualTypeMarshallingHelper.GetMarshallerShapeInfo(arrayMarshaller); ITypeSymbol? valuePropertyType = ManualTypeMarshallingHelper.FindValueProperty(arrayMarshaller)?.Type; - marshallingInfo = new NativeContiguousCollectionMarshallingInfo( + marshallingInfo = new NativeSpanCollectionMarshallingInfo( NativeMarshallingType: ManagedTypeInfo.CreateTypeInfoForTypeSymbol(arrayMarshaller), ValuePropertyType: valuePropertyType is not null ? ManagedTypeInfo.CreateTypeInfoForTypeSymbol(valuePropertyType) : null, MarshallingFeatures: ~CustomMarshallingFeatures.ManagedTypePinning, UseDefaultMarshalling: true, + customTypeMarshallerData.Value.BufferSize, ElementCountInfo: parsedCountInfo, ElementType: ManagedTypeInfo.CreateTypeInfoForTypeSymbol(elementType), ElementMarshallingInfo: GetMarshallingInfo(elementType, useSiteAttributes, indirectionLevel + 1, inspectedElements, ref maxIndirectionLevelUsed)); 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 273ab5519ae98..ea8778cc8a7da 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 @@ -22,6 +22,8 @@ public static class TypeNames public const string CustomTypeMarshallerAttribute = "System.Runtime.InteropServices.CustomTypeMarshallerAttribute"; + public const string CustomTypeMarshallerAttributeGenericPlaceholder = "System.Runtime.InteropServices.CustomTypeMarshallerAttribute.GenericPlaceholder"; + public const string LCIDConversionAttribute = "System.Runtime.InteropServices.LCIDConversionAttribute"; public const string SuppressGCTransitionAttribute = "System.Runtime.InteropServices.SuppressGCTransitionAttribute"; diff --git a/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/ManualTypeMarshallingAnalyzerTests.cs b/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/ManualTypeMarshallingAnalyzerTests.cs index f13fdc09897d1..750af9b1e3909 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/ManualTypeMarshallingAnalyzerTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/ManualTypeMarshallingAnalyzerTests.cs @@ -1227,5 +1227,53 @@ public Native(S s) {} await VerifyCS.VerifyAnalyzerAsync(source, VerifyCS.Diagnostic(CallerAllocConstructorMustHaveBufferSizeConstantRule).WithLocation(0).WithArguments("Native")); } + + [ConditionalFact] + public async Task CustomTypeMarshallerForArrayTypeWithPlaceholder_DoesNotReportDiagnostic() + { + string source = @" +using System; +using System.Runtime.InteropServices; + +[CustomTypeMarshaller(typeof(CustomTypeMarshallerAttribute.GenericPlaceholder[]))] +struct Native +{ + public Native(T[] a) {} +}"; + + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [ConditionalFact] + public async Task CustomTypeMarshallerForPointerTypeWithPlaceholder_DoesNotReportDiagnostic() + { + string source = @" +using System; +using System.Runtime.InteropServices; + +[CustomTypeMarshaller(typeof(CustomTypeMarshallerAttribute.GenericPlaceholder*))] +unsafe struct Native where T : unmanaged +{ + public Native(T* a) {} +}"; + + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [ConditionalFact] + public async Task CustomTypeMarshallerForArrayOfPointerTypeWithPlaceholder_DoesNotReportDiagnostic() + { + string source = @" +using System; +using System.Runtime.InteropServices; + +[CustomTypeMarshaller(typeof(CustomTypeMarshallerAttribute.GenericPlaceholder*[]))] +unsafe struct Native where T : unmanaged +{ + public Native(T*[] a) {} +}"; + + await VerifyCS.VerifyAnalyzerAsync(source); + } } } 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 0be74fbe74ed7..b493f52c380da 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/NonBlittable.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/NonBlittable.cs @@ -122,7 +122,7 @@ public void FreeNative() } } - [CustomTypeMarshaller(typeof(string))] + [CustomTypeMarshaller(typeof(string), BufferSize = 0x100, RequiresStackBuffer = true)] public unsafe ref struct Utf16StringMarshaler { private ushort* allocated; @@ -194,9 +194,6 @@ public void FreeNative() { Marshal.FreeCoTaskMem((IntPtr)allocated); } - - public const int BufferSize = 0x100; - public const bool RequiresStackBuffer = true; } @@ -218,7 +215,7 @@ public IntStructWrapperNative(IntStructWrapper managed) public IntStructWrapper ToManaged() => new IntStructWrapper { Value = value }; } - [CustomTypeMarshaller(typeof(List<>), CustomTypeMarshallerKind.SpanCollection)] + [CustomTypeMarshaller(typeof(List<>), CustomTypeMarshallerKind.SpanCollection, BufferSize = 0x200, RequiresStackBuffer = true)] public unsafe ref struct ListMarshaller { private List managedList; @@ -260,14 +257,6 @@ public ListMarshaller(List managed, Span stackSpace, int sizeOfNativeEl } } - /// - /// Stack-alloc threshold set to 256 bytes to enable small arrays to be passed on the stack. - /// Number kept small to ensure that P/Invokes with a lot of array parameters doesn't - /// blow the stack since this is a new optimization in the code-generated interop. - /// - public const int BufferSize = 0x200; - public const bool RequiresStackBuffer = true; - public Span ManagedValues => CollectionsMarshal.AsSpan(managedList); public Span NativeValueStorage { get; private set; } From c108257e25b6154d9d45a8c88783be2a6300a7de Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 18 Feb 2022 11:52:10 -0800 Subject: [PATCH 04/24] Update custom marshalling design docs and the runtime usage --- .../DllImportGenerator/SpanMarshallers.md | 52 +++++++++++++------ .../DllImportGenerator/StructMarshalling.md | 51 ++++++++++++------ .../Common/src/Interop/Interop.Ldap.cs | 3 ++ .../Advapi32/Interop.LsaLookupNames2.cs | 1 + .../CryptUI/Interop.CryptUIDlgCertificate.cs | 2 + .../Windows/WebSocket/Interop.Structs.cs | 1 + .../Windows/WinHttp/Interop.winhttp.cs | 1 + .../Windows/WinHttp/Interop.winhttp_types.cs | 1 + .../Windows/WinSock/Interop.WinsockBSD.cs | 1 + .../GeneratedMarshallingAttribute.cs | 2 +- .../InteropServices/HandleRefMarshaller.cs | 1 + .../Diagnostics/Reader/UnsafeNativeMethods.cs | 2 + .../src/Interop/Windows/Interop.Gdi32.cs | 1 + .../src/System/Drawing/Imaging/BitmapData.cs | 1 + .../src/System/Drawing/Imaging/ColorMatrix.cs | 1 + .../Drawing/Imaging/MetafileHeaderEmf.cs | 1 + .../Drawing/Imaging/MetafileHeaderWmf.cs | 1 + .../Drawing/Imaging/WmfPlaceableFileHeader.cs | 1 + .../MsQuic/Interop/MsQuicNativeMethods.cs | 2 + .../Analyzers/AnalyzerDiagnostics.cs | 2 +- .../ManualTypeMarshallingAnalyzer.cs | 17 +++--- .../DllImportGenerator/Resources.Designer.cs | 12 ++--- .../gen/DllImportGenerator/Resources.resx | 8 +-- .../ManualTypeMarshallingAnalyzerTests.cs | 22 +++----- 24 files changed, 118 insertions(+), 69 deletions(-) diff --git a/docs/design/libraries/DllImportGenerator/SpanMarshallers.md b/docs/design/libraries/DllImportGenerator/SpanMarshallers.md index a1b3e74f672fe..b2840ee2a53dc 100644 --- a/docs/design/libraries/DllImportGenerator/SpanMarshallers.md +++ b/docs/design/libraries/DllImportGenerator/SpanMarshallers.md @@ -55,16 +55,28 @@ Span marshalling would still be implemented with similar semantics as mentioned ### Proposed extension to the custom type marshalling design -Introduce a new attribute named `GenericContiguousCollectionMarshallerAttribute`. This attribute would have the following shape: +Introduce a marshaller kind named `SpanCollection`. -```csharp +```diff namespace System.Runtime.InteropServices { - [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class)] - public sealed class GenericContiguousCollectionMarshallerAttribute : Attribute - { - public GenericContiguousCollectionMarshallerAttribute(); - } + [AttributeUsage(AttributeTargets.Struct)] + public sealed class CustomTypeMarshallerAttribute : Attribute + { ++ /// ++ /// This type is used as a placeholder for the first generic parameter when generic parameters cannot be used ++ /// to identify the managed type (i.e. when the marshaller type is generic over T and the managed type is T[]) ++ /// ++ public struct GenericPlaceholder ++ { ++ } + } + + public enum CustomTypeMarshallerKind + { + Value, ++ SpanCollection + } } ``` @@ -77,21 +89,27 @@ public ref struct Span ... } -[GenericContiguousCollectionMarshaller] +[CustomTypeMarshaller(typeof(Span<>), CustomTypeMarshallerKind.SpanCollection)] public ref struct DefaultSpanMarshaler { ... } ``` -The `GenericContiguousCollectionMarshallerAttribute` attribute is applied to a generic marshaler type with the "collection marshaller" shape described below. Since generic parameters cannot be used in attributes, open generic types will be permitted in the `NativeTypeMarshallingAttribute` constructor as long as they have the same arity as the type the attribute is applied to and generic parameters provided to the applied-to type can also be used to construct the type passed as a parameter. +The `CustomTypeMarshallerKind.SpanCollection` kind is applied to a generic marshaler type with the "SpanCollection marshaller shape" described below. + +#### Supporting generics + +Since generic parameters cannot be used in attributes, open generic types will be permitted in the `NativeTypeMarshallingAttribute` and the `CustomTypeMarshallerAttribute` as long as they have the same arity as the type the attribute is applied to and generic parameters provided to the applied-to type can also be used to construct the type passed as a parameter. + +If a `CustomTypeMarshaller`-attributed type is a marshaller for a type for a pointer, an array, or a combination of pointers and arrays, the `CustomTypeMarshallerAttribute.GenericPlaceholder` type can be used in the place of the first generic parameter of the marshaller type. -#### Generic collection marshaller shape +#### SpanCollection marshaller shape -A generic collection marshaller would be required to have the following shape, in addition to the requirements for marshaler types used with the `NativeTypeMarshallingAttribute`, excluding the constructors. +A generic collection marshaller would be required to have the following shape, in addition to the requirements for marshaler types used with the `CustomTypeMarshallerKind.Value` shape, excluding the constructors. ```csharp -[GenericContiguousCollectionMarshaller] +[CustomTypeMarshaller(typeof(GenericCollection<, , ,...>), CustomTypeMarshallerKind.SpanCollection)] public struct GenericContiguousCollectionMarshallerImpl { // this constructor is required if marshalling from native to managed is supported. @@ -174,7 +192,7 @@ Alternatively, the `MarshalUsingAttribute` could provide a `Type ElementNativeTy This design could be used to provide a default marshaller for spans and arrays. Below is an example simple marshaller for `Span`. This design does not include all possible optimizations, such as stack allocation, for simpilicity of the example. ```csharp -[GenericContiguousCollectionMarshaller] +[CustomTypeMarshaller(typeof(Span<>), CustomTypeMarshallerKind.SpanCollection)] public ref struct SpanMarshaler { private Span managedCollection; @@ -261,11 +279,11 @@ This design could also be applied to support the built-in array marshalling if i If a managed or native representation of a collection has a non-contiguous element layout, then developers currently will need to convert to or from array/span types at the interop boundary. This section proposes an API that would enable developers to convert directly between a managed and native non-contiguous collection layout as part of marshalling. -A new attribute named `GenericCollectionMarshaller` attribute could be added that would specify that the collection is noncontiguous in either managed or native representations. Then additional methods should be added to the generic collection model, and some methods would be removed: +A new marshaller kind named `GenericCollection` could be added that would specify that the collection is noncontiguous in either managed or native representations. Then additional methods should be added to the generic collection model, and some methods would be removed: ```diff -- [GenericContiguousCollectionMarshaller] -+ [GenericCollectionMarshaller] +- [CustomTypeMarshaller(typeof(Span<>), CustomTypeMarshallerKind.SpanCollection)] ++ [CustomTypeMarshaller(typeof(Span<>), CustomTypeMarshallerKind.GenericCollection)] public struct GenericContiguousCollectionMarshallerImpl { // these constructors are required if marshalling from managed to native is supported. @@ -312,6 +330,6 @@ Cons: - Introduces more attribute types into the BCL. - Introduces more complexity in the marshalling type model. - It may be worth describing the required members (other than constructors) in interfaces just to simplify the mental load of which members are required for which scenarios. - - A set of interfaces (one for managed-to-native members, one for native-to-managed members, and one for the sequential-specific members) could replace the `GenericContiguousCollectionMarshaller` attribute. + - A set of interfaces (one for managed-to-native members, one for native-to-managed members, and one for the sequential-specific members) could replace the new marshaller kind. - The base proposal only supports contiguous collections. - The feeling at time of writing is that we are okay asking developers to convert to/from arrays or spans at the interop boundary. diff --git a/docs/design/libraries/DllImportGenerator/StructMarshalling.md b/docs/design/libraries/DllImportGenerator/StructMarshalling.md index 63781d6d77dbb..adb6f6b775f61 100644 --- a/docs/design/libraries/DllImportGenerator/StructMarshalling.md +++ b/docs/design/libraries/DllImportGenerator/StructMarshalling.md @@ -25,24 +25,45 @@ All design options would use these attributes: ```csharp [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class)] -public class GeneratedMarshallingAttribute : Attribute {} +public sealed class GeneratedMarshallingAttribute : Attribute {} [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class)] -public class NativeMarshallingAttribute : Attribute +public sealed class NativeMarshallingAttribute : Attribute { public NativeMarshallingAttribute(Type nativeType) {} } [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.Field)] -public class MarshalUsingAttribute : Attribute +public sealed class MarshalUsingAttribute : Attribute { public MarshalUsingAttribute(Type nativeType) {} } + +[AttributeUsage(AttributeTargets.Struct)] +public sealed class CustomTypeMarshallerAttribute : Attribute +{ + public CustomTypeMarshallerAttribute(Type managedType, CustomTypeMarshallerKind marshallerKind = CustomTypeMarshallerKind.Value) + { + ManagedType = managedType; + MarshallerKind = marshallerKind; + } + + public Type ManagedType { get; } + public CustomTypeMarshallerKind MarshallerKind { get; } + public int BufferSize { get; set; } + public bool RequiresStackBuffer { get; set; } +} + +public enum CustomTypeMarshallerKind +{ + Value +} ``` -The `NativeMarshallingAttribute` and `MarshalUsingAttribute` attributes would require that the provided native type `TNative` is a `struct` that does not require any marshalling and has a subset of three methods with the following names and shapes (with the managed type named TManaged): +The `NativeMarshallingAttribute` and `MarshalUsingAttribute` attributes would require that the provided native type `TNative` is a `struct` that does not require any marshalling and has the `CustomTypeMarshallerAttribute` with the first parameter being a `typeof()` of the managed type and a subset of three methods with the following names and shapes (with the managed type named TManaged): ```csharp +[CustomTypeMarshaller(typeof(TManaged))] partial struct TNative { public TNative(TManaged managed) {} @@ -57,7 +78,7 @@ The analyzer will report an error if neither the constructor nor the `ToManaged` > :question: Does this API surface and shape work for all marshalling scenarios we plan on supporting? It may have issues with the current "layout class" by-value `[Out]` parameter marshalling where the runtime updates a `class` typed object in place. We already recommend against using classes for interop for performance reasons and a struct value passed via `ref` or `out` with the same members would cover this scenario. -If the native type `TNative` also has a public `Value` property, then the value of the `Value` property will be passed to native code instead of the `TNative` value itself. As a result, the type `TNative` will be allowed to require marshalling and the type of the `Value` property will be required be passable to native code without any additional marshalling. If the `Value` property is settable, then when marshalling in the native-to-managed direction, a default value of `TNative` will have its `Value` property set to the native value. If `Value` does not have a setter, then marshalling from native to managed is not supported. +If the native type `TNative` also has a public `Value` property, then the value of the `Value` property will be passed to native code instead of the `TNative` value itself. As a result, the type `TNative` will be allowed to require marshalling and the type of the `Value` property will be required be passable to native code without any additional marshalling. When the `Value` property is provided, the `CustomTypeMarshallerAttribute` will still need to be provided on `TNative`. If the `Value` property is settable, then when marshalling in the native-to-managed direction, a default value of `TNative` will have its `Value` property set to the native value. If `Value` does not have a setter, then marshalling from native to managed is not supported. If a `Value` property is provided, the developer may also provide a ref-returning or readonly-ref-returning `GetPinnableReference` method. The `GetPinnableReference` method will be called before the `Value` property getter is called. The ref returned by `GetPinnableReference` will be pinned with a `fixed` statement, but the pinned value will not be used (it acts exclusively as a side-effect). @@ -70,6 +91,7 @@ public struct TManaged // ... } +[CustomTypeMarshaller(typeof(TManaged))] public struct TMarshaler { public TMarshaler(TManaged managed) {} @@ -92,20 +114,17 @@ Since C# 7.3 added a feature to enable custom pinning logic for user types, we s #### Caller-allocated memory -Custom marshalers of collection-like types or custom string encodings (such as UTF-32) may want to use stack space for extra storage for additional performance when possible. If the `TNative` type provides additional members with the following signatures, then it will opt in to using a caller-allocated buffer: +Custom marshalers of collection-like types or custom string encodings (such as UTF-32) may want to use stack space for extra storage for additional performance when possible. If the `TNative` type provides additional constructor with the following signature and sets the `BufferSize` field on the `CustomTypeMarshallerAttribute`, then it will opt in to using a caller-allocated buffer: ```csharp +[CustomTypeMarshaller(typeof(TManaged), BufferSize = /* */, RequiresStackBuffer = /* */)] partial struct TNative { public TNative(TManaged managed, Span buffer) {} - - public const int BufferSize = /* */; - - public const bool RequiresStackBuffer = /* */; } ``` -When these members are present, the source generator will call the two-parameter constructor with a possibly stack-allocated buffer of `BufferSize` bytes when a stack-allocated buffer is usable. If a stack-allocated buffer is a requirement, the `RequiresStackBuffer` field should be set to `true` and the `buffer` will be guaranteed to be allocated on the stack. Setting the `RequiresStackBuffer` field to `false` is the same as omitting the field definition. Since a dynamically allocated buffer is not usable in all scenarios, for example Reverse P/Invoke and struct marshalling, a one-parameter constructor must also be provided for usage in those scenarios. This may also be provided by providing a two-parameter constructor with a default value for the second parameter. +When these members are present, the source generator will call the two-parameter constructor with a possibly stack-allocated buffer of `BufferSize` bytes when a stack-allocated buffer is usable. If a stack-allocated buffer is a requirement, the `RequiresStackBuffer` field should be set to `true` and the `buffer` will be guaranteed to be allocated on the stack. Setting the `RequiresStackBuffer` field to `false` is the same as not specifying the value in the attribute. Since a dynamically allocated buffer is not usable in all scenarios, for example Reverse P/Invoke and struct marshalling, a one-parameter constructor must also be provided for usage in those scenarios. This may also be provided by providing a two-parameter constructor with a default value for the second parameter. Type authors can pass down the `buffer` pointer to native code by defining a `Value` property that returns a pointer to the first element, generally through code using `MemoryMarshal.GetReference()` and `Unsafe.AsPointer`. If `RequiresStackBuffer` is not provided or set to `false`, the `buffer` span must be pinned to be used safely. The `buffer` span can be pinned by defining a `GetPinnableReference()` method on the native type that returns a reference to the first element of the span. @@ -251,6 +270,7 @@ struct HResult public readonly int Result; } +[CustomTypeMarshaller(typeof(HResult))] struct HRESULT { public HRESULT(HResult hr) @@ -276,21 +296,20 @@ Building on this Transparent Structures support, we can also support ComWrappers class Foo {} -struct ComWrappersMarshaler - where TComWrappers : ComWrappers, new() +struct FooComWrappersMarshaler { - private static readonly TComWrappers ComWrappers = new TComWrappers(); + private static readonly FooComWrappers ComWrappers = new FooComWrappers(); private IntPtr nativeObj; - public ComWrappersMarshaler(TClass obj) + public ComWrappersMarshaler(Foo obj) { nativeObj = ComWrappers.GetOrCreateComInterfaceForObject(obj, CreateComInterfaceFlags.None); } public IntPtr Value { get => nativeObj; set => nativeObj = value; } - public TClass ToManaged() => (TClass)ComWrappers.GetOrCreateObjectForComInstance(nativeObj, CreateObjectFlags.None); + public Foo ToManaged() => (Foo)ComWrappers.GetOrCreateObjectForComInstance(nativeObj, CreateObjectFlags.None); public unsafe void FreeNative() { diff --git a/src/libraries/Common/src/Interop/Interop.Ldap.cs b/src/libraries/Common/src/Interop/Interop.Ldap.cs index a48640d1e4619..83617a5e91322 100644 --- a/src/libraries/Common/src/Interop/Interop.Ldap.cs +++ b/src/libraries/Common/src/Interop/Interop.Ldap.cs @@ -45,6 +45,7 @@ internal struct SEC_WINNT_AUTH_IDENTITY_EX public int packageListLength; [StructLayout(LayoutKind.Sequential)] + [CustomTypeMarshaller(typeof(SEC_WINNT_AUTH_IDENTITY_EX))] internal struct Native { public int version; @@ -173,6 +174,7 @@ internal sealed class BerVal public IntPtr bv_val = IntPtr.Zero; #if NET7_0_OR_GREATER + [CustomTypeMarshaller(typeof(BerVal))] internal unsafe struct PinningMarshaller { private readonly BerVal _managed; @@ -211,6 +213,7 @@ internal struct LdapReferralCallback #if NET7_0_OR_GREATER public static readonly unsafe int Size = sizeof(Marshaller.Native); + [CustomTypeMarshaller(typeof(LdapReferralCallback))] public unsafe struct Marshaller { public unsafe struct Native diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.LsaLookupNames2.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.LsaLookupNames2.cs index f2ada1ff4f443..923469a7a8a29 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.LsaLookupNames2.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.LsaLookupNames2.cs @@ -26,6 +26,7 @@ internal struct MARSHALLED_UNICODE_STRING internal ushort MaximumLength; internal string Buffer; + [CustomTypeMarshaller(typeof(MARSHALLED_UNICODE_STRING))] public struct Native { internal ushort Length; 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 e98d1aeb621cd..d2735c99905cd 100644 --- a/src/libraries/Common/src/Interop/Windows/CryptUI/Interop.CryptUIDlgCertificate.cs +++ b/src/libraries/Common/src/Interop/Windows/CryptUI/Interop.CryptUIDlgCertificate.cs @@ -37,6 +37,7 @@ internal struct CRYPTUI_VIEWCERTIFICATE_STRUCTW internal uint nStartPage; #if NET7_0_OR_GREATER + [CustomTypeMarshaller(typeof(CRYPTUI_VIEWCERTIFICATE_STRUCTW))] internal unsafe struct Native { private uint dwSize; @@ -139,6 +140,7 @@ internal struct CRYPTUI_SELECTCERTIFICATE_STRUCTW internal IntPtr hSelectedCertStore; #if NET7_0_OR_GREATER + [CustomTypeMarshaller(typeof(CRYPTUI_SELECTCERTIFICATE_STRUCTW))] internal unsafe struct Native { private uint dwSize; diff --git a/src/libraries/Common/src/Interop/Windows/WebSocket/Interop.Structs.cs b/src/libraries/Common/src/Interop/Windows/WebSocket/Interop.Structs.cs index 26c0dd93fd3bc..8f3db8e88931c 100644 --- a/src/libraries/Common/src/Interop/Windows/WebSocket/Interop.Structs.cs +++ b/src/libraries/Common/src/Interop/Windows/WebSocket/Interop.Structs.cs @@ -49,6 +49,7 @@ internal struct HttpHeader internal string Value; internal uint ValueLength; + [CustomTypeMarshaller(typeof(HttpHeader))] internal struct Native { private IntPtr Name; 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 165551fa6f997..f0860c9019f45 100644 --- a/src/libraries/Common/src/Interop/Windows/WinHttp/Interop.winhttp.cs +++ b/src/libraries/Common/src/Interop/Windows/WinHttp/Interop.winhttp.cs @@ -53,6 +53,7 @@ public static partial bool WinHttpAddRequestHeaders( uint modifiers); #if NET7_0_OR_GREATER + [CustomTypeMarshaller(typeof(StringBuilder))] private unsafe struct SimpleStringBufferMarshaller { public SimpleStringBufferMarshaller(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 47f105b6c3fc1..bcfc21dd767c6 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 @@ -259,6 +259,7 @@ public struct WINHTTP_AUTOPROXY_OPTIONS [MarshalAs(UnmanagedType.Bool)] public bool AutoLoginIfChallenged; #if NET7_0_OR_GREATER + [CustomTypeMarshaller(typeof(WINHTTP_AUTOPROXY_OPTIONS))] public struct Native { private uint Flags; 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 28478031eb38f..b19cd8dba064d 100644 --- a/src/libraries/Common/src/Interop/Windows/WinSock/Interop.WinsockBSD.cs +++ b/src/libraries/Common/src/Interop/Windows/WinSock/Interop.WinsockBSD.cs @@ -74,6 +74,7 @@ internal struct IPv6MulticastRequest internal byte[] MulticastAddress; // IP address of group. internal int InterfaceIndex; // Local interface index. + [CustomTypeMarshaller(typeof(IPv6MulticastRequest))] public unsafe struct Native { private const int MulticastAddressLength = 16; diff --git a/src/libraries/Common/src/System/Runtime/InteropServices/GeneratedMarshallingAttribute.cs b/src/libraries/Common/src/System/Runtime/InteropServices/GeneratedMarshallingAttribute.cs index 330efa3d7eb71..a760d468704b9 100644 --- a/src/libraries/Common/src/System/Runtime/InteropServices/GeneratedMarshallingAttribute.cs +++ b/src/libraries/Common/src/System/Runtime/InteropServices/GeneratedMarshallingAttribute.cs @@ -64,7 +64,7 @@ public MarshalUsingAttribute(Type nativeType) public const string ReturnsCountValue = "return-value"; } - [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class)] + [AttributeUsage(AttributeTargets.Struct)] #if DLLIMPORT_GENERATOR_TEST public #else diff --git a/src/libraries/Common/src/System/Runtime/InteropServices/HandleRefMarshaller.cs b/src/libraries/Common/src/System/Runtime/InteropServices/HandleRefMarshaller.cs index 5278e31ae0e2c..f01b86054ed4b 100644 --- a/src/libraries/Common/src/System/Runtime/InteropServices/HandleRefMarshaller.cs +++ b/src/libraries/Common/src/System/Runtime/InteropServices/HandleRefMarshaller.cs @@ -6,6 +6,7 @@ namespace System.Runtime.InteropServices.GeneratedMarshalling { + [CustomTypeMarshaller(typeof(HandleRef))] internal struct HandleRefMarshaller { private HandleRef _handle; 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 f571f0246ae02..b7716364a8f95 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 @@ -353,6 +353,7 @@ internal struct EvtRpcLogin public CoTaskMemUnicodeSafeHandle Password; public int Flags; #if NET7_0_OR_GREATER + [CustomTypeMarshaller(typeof(EvtRpcLogin))] public struct Marshaller { public struct Native @@ -680,6 +681,7 @@ internal struct EvtStringVariant public uint Type; #if NET7_0_OR_GREATER + [CustomTypeMarshaller(typeof(EvtStringVariant))] [StructLayout(LayoutKind.Explicit)] public struct Native { 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 c438ef92640af..f44cfe13e18e2 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,6 +188,7 @@ internal sealed class DOCINFO internal int fwType; #if NET7_0_OR_GREATER + [CustomTypeMarshaller(typeof(DOCINFO))] internal struct Native { internal int cbSize; diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/BitmapData.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/BitmapData.cs index c5a728914bcfe..105807d318676 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/BitmapData.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/BitmapData.cs @@ -98,6 +98,7 @@ public int Reserved internal ref int GetPinnableReference() => ref _width; #if NET7_0_OR_GREATER + [CustomTypeMarshaller(typeof(BitmapData))] internal unsafe struct PinningMarshaller { private readonly BitmapData _managed; diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/ColorMatrix.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/ColorMatrix.cs index 707ff68721669..7f78979c02e36 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/ColorMatrix.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/ColorMatrix.cs @@ -396,6 +396,7 @@ internal float[][] GetMatrix() internal ref float GetPinnableReference() => ref _matrix00; #if NET7_0_OR_GREATER + [CustomTypeMarshaller(typeof(ColorMatrix))] internal unsafe struct PinningMarshaller { private readonly ColorMatrix _managed; diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/MetafileHeaderEmf.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/MetafileHeaderEmf.cs index dfce8751ec4d2..73940fc2b35d4 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/MetafileHeaderEmf.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/MetafileHeaderEmf.cs @@ -34,6 +34,7 @@ internal sealed class MetafileHeaderEmf internal ref byte GetPinnableReference() => ref Unsafe.As(ref type); #if NET7_0_OR_GREATER + [CustomTypeMarshaller(typeof(MetafileHeaderEmf))] internal unsafe struct PinningMarshaller { private readonly MetafileHeaderEmf _managed; diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/MetafileHeaderWmf.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/MetafileHeaderWmf.cs index 6825f2d007b1e..9a9e31275f921 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/MetafileHeaderWmf.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/MetafileHeaderWmf.cs @@ -55,6 +55,7 @@ internal sealed class MetafileHeaderWmf public int LogicalDpiY; #if NET7_0_OR_GREATER + [CustomTypeMarshaller(typeof(MetafileHeaderWmf))] internal unsafe struct InPlaceMarshaller { [StructLayout(LayoutKind.Sequential, Pack = 8)] diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/WmfPlaceableFileHeader.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/WmfPlaceableFileHeader.cs index ab26edfdb1a48..e5cb014b2f4c5 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/WmfPlaceableFileHeader.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/WmfPlaceableFileHeader.cs @@ -106,6 +106,7 @@ public short Checksum internal ref int GetPinnableReference() => ref _key; #if NET7_0_OR_GREATER + [CustomTypeMarshaller(typeof(WmfPlaceableFileHeader))] internal unsafe struct PinningMarshaller { private readonly WmfPlaceableFileHeader _managed; diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicNativeMethods.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicNativeMethods.cs index 7b894cb803f04..5de546f20a9d3 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicNativeMethods.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicNativeMethods.cs @@ -97,6 +97,7 @@ internal struct RegistrationConfig internal string AppName; internal QUIC_EXECUTION_PROFILE ExecutionProfile; + [CustomTypeMarshaller(typeof(RegistrationConfig))] [StructLayout(LayoutKind.Sequential)] public struct Native { @@ -250,6 +251,7 @@ internal struct CredentialConfig // TODO: define delegate for AsyncHandler and make proper use of it. internal IntPtr AsyncHandler; + [CustomTypeMarshaller(typeof(CredentialConfig))] [StructLayout(LayoutKind.Sequential)] public struct Native { diff --git a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Analyzers/AnalyzerDiagnostics.cs b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Analyzers/AnalyzerDiagnostics.cs index 132ea3e21142e..9796daf5100e6 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Analyzers/AnalyzerDiagnostics.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Analyzers/AnalyzerDiagnostics.cs @@ -26,7 +26,7 @@ public static class Ids public const string ValuePropertyMustHaveGetter = Prefix + "009"; public const string GetPinnableReferenceShouldSupportAllocatingMarshallingFallback = Prefix + "010"; public const string CallerAllocMarshallingShouldSupportAllocatingMarshallingFallback = Prefix + "011"; - public const string CallerAllocConstructorMustHaveStackBufferSizeConstant = Prefix + "012"; + public const string CallerAllocConstructorMustHaveStackBufferSize = Prefix + "012"; public const string RefValuePropertyUnsupported = Prefix + "014"; public const string NativeGenericTypeMustBeClosedOrMatchArity = Prefix + "016"; public const string MarshallerGetPinnableReferenceRequiresValueProperty = Prefix + "018"; diff --git a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Analyzers/ManualTypeMarshallingAnalyzer.cs b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Analyzers/ManualTypeMarshallingAnalyzer.cs index e45e6008bb576..58aa42c332e92 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Analyzers/ManualTypeMarshallingAnalyzer.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Analyzers/ManualTypeMarshallingAnalyzer.cs @@ -138,15 +138,15 @@ public class ManualTypeMarshallingAnalyzer : DiagnosticAnalyzer isEnabledByDefault: true, description: GetResourceString(nameof(Resources.CallerAllocMarshallingShouldSupportAllocatingMarshallingFallbackDescription))); - public static readonly DiagnosticDescriptor CallerAllocConstructorMustHaveBufferSizeConstantRule = + public static readonly DiagnosticDescriptor CallerAllocConstructorMustHaveBufferSizeRule = new DiagnosticDescriptor( - Ids.CallerAllocConstructorMustHaveStackBufferSizeConstant, - "CallerAllocConstructorMustHaveBufferSizeConstant", - GetResourceString(nameof(Resources.CallerAllocConstructorMustHaveBufferSizeConstantMessage)), + Ids.CallerAllocConstructorMustHaveStackBufferSize, + "CallerAllocConstructorMustHaveBufferSize", + GetResourceString(nameof(Resources.CallerAllocConstructorMustHaveBufferSizeMessage)), Category, DiagnosticSeverity.Error, isEnabledByDefault: true, - description: GetResourceString(nameof(Resources.CallerAllocConstructorMustHaveBufferSizeConstantDescription))); + description: GetResourceString(nameof(Resources.CallerAllocConstructorMustHaveBufferSizeDescription))); public static readonly DiagnosticDescriptor RefValuePropertyUnsupportedRule = new DiagnosticDescriptor( @@ -192,7 +192,7 @@ public class ManualTypeMarshallingAnalyzer : DiagnosticAnalyzer ValuePropertyMustHaveGetterRule, GetPinnableReferenceShouldSupportAllocatingMarshallingFallbackRule, CallerAllocMarshallingShouldSupportAllocatingMarshallingFallbackRule, - CallerAllocConstructorMustHaveBufferSizeConstantRule, + CallerAllocConstructorMustHaveBufferSizeRule, RefValuePropertyUnsupportedRule, NativeGenericTypeMustBeClosedOrMatchArityRule, MarshallerGetPinnableReferenceRequiresValuePropertyRule); @@ -400,12 +400,11 @@ public void AnalyzeMarshallerType(SymbolAnalysisContext context) if (!hasCallerAllocSpanConstructor && ManualTypeMarshallingHelper.IsCallerAllocatedSpanConstructor(ctor, type, _spanOfByte, marshallerData.Value.Kind)) { hasCallerAllocSpanConstructor = true; - IFieldSymbol bufferSizeField = marshallerType.GetMembers(ManualTypeMarshallingHelper.BufferSizeFieldName).OfType().FirstOrDefault(); - if (bufferSizeField is null or { DeclaredAccessibility: not Accessibility.Public } or { IsConst: false } or { Type: not { SpecialType: SpecialType.System_Int32 } }) + if (marshallerData.Value.BufferSize == null) { context.ReportDiagnostic( ctor.CreateDiagnostic( - CallerAllocConstructorMustHaveBufferSizeConstantRule, + CallerAllocConstructorMustHaveBufferSizeRule, marshallerType.ToDisplayString())); } } diff --git a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Resources.Designer.cs b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Resources.Designer.cs index 221e4ad40b98c..b4a3ae3bc8f7c 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Resources.Designer.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Resources.Designer.cs @@ -61,20 +61,20 @@ internal Resources() { } /// - /// Looks up a localized string similar to When a constructor taking a Span<byte> is specified on the native type, the type must also have a public integer constant named BufferSize to provide the size of the caller-allocated buffer.. + /// Looks up a localized string similar to When a constructor taking a ''Span<byte> is specified on the native type, the type must set the BufferSize field on the 'System.Runtime.InteropServices.CustomTypeMarshallerAttribute' to provide the size of the caller-allocated buffer.. /// - internal static string CallerAllocConstructorMustHaveBufferSizeConstantDescription { + internal static string CallerAllocConstructorMustHaveBufferSizeDescription { get { - return ResourceManager.GetString("CallerAllocConstructorMustHaveBufferSizeConstantDescription", resourceCulture); + return ResourceManager.GetString("CallerAllocConstructorMustHaveBufferSizeDescription", resourceCulture); } } /// - /// Looks up a localized string similar to The native type '{0}' must have a 'public const int BufferSize' field that specifies the size of the stack buffer because it has a constructor that takes a caller-allocated Span<byte>. + /// Looks up a localized string similar to The native type '{0}' must set the 'BufferSize' field on the applied 'System.Runtime.InteropServices.CustomTypeMarshallerAttribute' to specify the size of the caller-allocated buffer because it has a constructor that takes a caller-allocated 'Span<byte>'. /// - internal static string CallerAllocConstructorMustHaveBufferSizeConstantMessage { + internal static string CallerAllocConstructorMustHaveBufferSizeMessage { get { - return ResourceManager.GetString("CallerAllocConstructorMustHaveBufferSizeConstantMessage", resourceCulture); + return ResourceManager.GetString("CallerAllocConstructorMustHaveBufferSizeMessage", resourceCulture); } } diff --git a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Resources.resx b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Resources.resx index 198521950b527..98d2f5810dc57 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Resources.resx +++ b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Resources.resx @@ -117,11 +117,11 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - When a constructor taking a Span<byte> is specified on the native type, the type must also have a public integer constant named BufferSize to provide the size of the caller-allocated buffer. + + When a constructor taking a ''Span<byte> is specified on the native type, the type must set the BufferSize field on the 'System.Runtime.InteropServices.CustomTypeMarshallerAttribute' to provide the size of the caller-allocated buffer. - - The native type '{0}' must have a 'public const int BufferSize' field that specifies the size of the stack buffer because it has a constructor that takes a caller-allocated Span<byte> + + The native type '{0}' must set the 'BufferSize' field on the applied 'System.Runtime.InteropServices.CustomTypeMarshallerAttribute' to specify the size of the caller-allocated buffer because it has a constructor that takes a caller-allocated 'Span<byte>' A type that supports marshalling from managed to native using a caller-allocated buffer should also support marshalling from managed to native where using a caller-allocated buffer is impossible. diff --git a/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/ManualTypeMarshallingAnalyzerTests.cs b/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/ManualTypeMarshallingAnalyzerTests.cs index 750af9b1e3909..482c5a3bec0ba 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/ManualTypeMarshallingAnalyzerTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/ManualTypeMarshallingAnalyzerTests.cs @@ -88,7 +88,7 @@ struct S public string s; } -[CustomTypeMarshaller(typeof(S))] +[{|CS0592:CustomTypeMarshaller|}(typeof(S))] class {|#0:Native|} { private IntPtr value; @@ -209,7 +209,7 @@ struct S public string s; } -[CustomTypeMarshaller(typeof(S))] +[{|CS0592:CustomTypeMarshaller|}(typeof(S))] class {|#0:Native|} { private string value; @@ -665,13 +665,11 @@ class S public byte c; } -[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.SpanCollection)] +[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.SpanCollection, BufferSize = 1)] ref struct {|#0:Native|} { public Native(S s, Span stackSpace) : this() {} - public const int BufferSize = 1; - public Span ManagedValues { get; set; } public Span NativeValueStorage { get; set; } @@ -695,13 +693,11 @@ class S public byte c; } -[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.SpanCollection)] +[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.SpanCollection, BufferSize = 1)] ref struct {|#0:Native|} { public Native(S s, Span stackSpace, int nativeElementSize) : this() {} - public const int BufferSize = 1; - public Span ManagedValues { get; set; } public Span NativeValueStorage { get; set; } @@ -823,12 +819,10 @@ class S public byte c; } -[CustomTypeMarshaller(typeof(S))] +[CustomTypeMarshaller(typeof(S), BufferSize = 0x100)] struct {|#0:Native|} { public Native(S s, Span buffer) {} - - public const int BufferSize = 0x100; }"; await VerifyCS.VerifyAnalyzerAsync(source, @@ -852,11 +846,9 @@ class {|#0:S|} [CustomTypeMarshaller(typeof(S))] struct {|#1:Native|} { - public Native(S s, Span buffer) {} + public Native(S s, Span buffer, BufferSize = 0x100) {} public IntPtr Value => IntPtr.Zero; - - public const int BufferSize = 0x100; }"; await VerifyCS.VerifyAnalyzerAsync(source, @@ -1225,7 +1217,7 @@ public Native(S s) {} }"; await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(CallerAllocConstructorMustHaveBufferSizeConstantRule).WithLocation(0).WithArguments("Native")); + VerifyCS.Diagnostic(CallerAllocConstructorMustHaveBufferSizeRule).WithLocation(0).WithArguments("Native")); } [ConditionalFact] From b398b64a0bb3e19d8b2c554b1ea703d2f73ea238 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 18 Feb 2022 14:40:58 -0800 Subject: [PATCH 05/24] Fix test build --- .../Ancillary.Interop/SpanMarshallers.cs | 52 ++++--------------- .../SetLastErrorTests.cs | 1 + 2 files changed, 11 insertions(+), 42 deletions(-) diff --git a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/SpanMarshallers.cs b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/SpanMarshallers.cs index 9afedb29fe7d1..59bc65d44f55b 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/SpanMarshallers.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/SpanMarshallers.cs @@ -6,7 +6,10 @@ namespace System.Runtime.InteropServices.GeneratedMarshalling { - [CustomTypeMarshaller(typeof(ReadOnlySpan<>), CustomTypeMarshallerKind.SpanCollection)] + // Stack-alloc threshold set to 256 bytes to enable small arrays to be passed on the stack. + // Number kept small to ensure that P/Invokes with a lot of array parameters doesn't + // blow the stack since this is a new optimization in the code-generated interop. + [CustomTypeMarshaller(typeof(ReadOnlySpan<>), CustomTypeMarshallerKind.SpanCollection, BufferSize = 0x200, RequiresStackBuffer = true)] public unsafe ref struct ReadOnlySpanMarshaller { private ReadOnlySpan _managedSpan; @@ -47,14 +50,6 @@ public ReadOnlySpanMarshaller(ReadOnlySpan managed, Span stackSpace, in } } - /// - /// Stack-alloc threshold set to 256 bytes to enable small arrays to be passed on the stack. - /// Number kept small to ensure that P/Invokes with a lot of array parameters doesn't - /// blow the stack since this is a new optimization in the code-generated interop. - /// - public const int BufferSize = 0x200; - public const bool RequiresStackBuffer = true; - public Span ManagedValues => MemoryMarshal.CreateSpan(ref MemoryMarshal.GetReference(_managedSpan), _managedSpan.Length); public Span NativeValueStorage { get; private set; } @@ -95,7 +90,7 @@ public void FreeNative() } } - [CustomTypeMarshaller(typeof(Span<>), CustomTypeMarshallerKind.SpanCollection)] + [CustomTypeMarshaller(typeof(Span<>), CustomTypeMarshallerKind.SpanCollection, BufferSize = 0x200, RequiresStackBuffer = true)] public unsafe ref struct SpanMarshaller { private ReadOnlySpanMarshaller _inner; @@ -116,14 +111,6 @@ public SpanMarshaller(Span managed, Span stackSpace, int sizeOfNativeEl _inner = new ReadOnlySpanMarshaller(managed, stackSpace, sizeOfNativeElement); } - /// - /// Stack-alloc threshold set to 256 bytes to enable small arrays to be passed on the stack. - /// Number kept small to ensure that P/Invokes with a lot of array parameters doesn't - /// blow the stack since this is a new optimization in the code-generated interop. - /// - public const int BufferSize = ReadOnlySpanMarshaller.BufferSize; - public const bool RequiresStackBuffer = ReadOnlySpanMarshaller.RequiresStackBuffer; - public Span ManagedValues => _inner.ManagedValues; public Span NativeValueStorage @@ -156,7 +143,7 @@ public void FreeNative() } } - [CustomTypeMarshaller(typeof(Span<>), CustomTypeMarshallerKind.SpanCollection)] + [CustomTypeMarshaller(typeof(Span<>), CustomTypeMarshallerKind.SpanCollection, BufferSize = 0x200, RequiresStackBuffer = true)] public unsafe ref struct NeverNullSpanMarshaller { private SpanMarshaller _inner; @@ -177,13 +164,6 @@ public NeverNullSpanMarshaller(Span managed, Span stackSpace, int sizeO _inner = new SpanMarshaller(managed, stackSpace, sizeOfNativeElement); } - /// - /// Stack-alloc threshold set to 256 bytes to enable small spans to be passed on the stack. - /// Number kept small to ensure that P/Invokes with a lot of span parameters doesn't - /// blow the stack. - /// - public const int BufferSize = SpanMarshaller.BufferSize; - public Span ManagedValues => _inner.ManagedValues; public Span NativeValueStorage @@ -227,7 +207,7 @@ public void FreeNative() } } - [CustomTypeMarshaller(typeof(ReadOnlySpan<>), CustomTypeMarshallerKind.SpanCollection)] + [CustomTypeMarshaller(typeof(ReadOnlySpan<>), CustomTypeMarshallerKind.SpanCollection, BufferSize = 0x200, RequiresStackBuffer = true)] public unsafe ref struct NeverNullReadOnlySpanMarshaller { private ReadOnlySpanMarshaller _inner; @@ -248,14 +228,6 @@ public NeverNullReadOnlySpanMarshaller(ReadOnlySpan managed, Span stack _inner = new ReadOnlySpanMarshaller(managed, stackSpace, sizeOfNativeElement); } - /// - /// Stack-alloc threshold set to 256 bytes to enable small spans to be passed on the stack. - /// Number kept small to ensure that P/Invokes with a lot of span parameters doesn't - /// blow the stack. - /// - public const int BufferSize = SpanMarshaller.BufferSize; - public const bool RequiresStackBuffer = SpanMarshaller.RequiresStackBuffer; - public Span ManagedValues => _inner.ManagedValues; public Span NativeValueStorage @@ -299,7 +271,8 @@ public void FreeNative() } } - [CustomTypeMarshaller(typeof(Span<>), CustomTypeMarshallerKind.SpanCollection)] + // Stack-alloc threshold set to 0 so that the generator can use the constructor that takes a stackSpace to let the marshaller know that the original data span can be used and safely pinned. + [CustomTypeMarshaller(typeof(Span<>), CustomTypeMarshallerKind.SpanCollection, BufferSize = 0)] public unsafe ref struct DirectSpanMarshaller where T : unmanaged { @@ -311,7 +284,7 @@ public DirectSpanMarshaller(int sizeOfNativeElement) :this() { // This check is not exhaustive, but it will catch the majority of cases. - if (typeof(T) == typeof(bool) || typeof(T) == typeof(char) || Unsafe.SizeOf() != sizeOfNativeElement) + if (RuntimeHelpers.IsReferenceOrContainsReferences()) { throw new ArgumentException("This marshaller only supports blittable element types. The provided type parameter must be blittable", nameof(T)); } @@ -337,11 +310,6 @@ public DirectSpanMarshaller(Span managed, Span stackSpace, int sizeOfNa _data = managed; } - /// - /// Stack-alloc threshold set to 0 so that the generator can use the constructor that takes a stackSpace to let the marshaller know that the original data span can be used and safely pinned. - /// - public const int BufferSize = 0; - public Span ManagedValues => _data; public Span NativeValueStorage => _allocatedMemory != null diff --git a/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.Tests/SetLastErrorTests.cs b/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.Tests/SetLastErrorTests.cs index a12cdd2245a30..849fe032b0c9d 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.Tests/SetLastErrorTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.Tests/SetLastErrorTests.cs @@ -8,6 +8,7 @@ namespace DllImportGenerator.IntegrationTests { + [CustomTypeMarshaller(typeof(int))] public struct SetLastErrorMarshaller { public int val; From b6261083981f18578ab0131fd130127d7c3535b4 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 18 Feb 2022 15:29:59 -0800 Subject: [PATCH 06/24] Fix net6.0 ldap build. --- src/libraries/Common/src/Interop/Interop.Ldap.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libraries/Common/src/Interop/Interop.Ldap.cs b/src/libraries/Common/src/Interop/Interop.Ldap.cs index 83617a5e91322..d7d3174769f3f 100644 --- a/src/libraries/Common/src/Interop/Interop.Ldap.cs +++ b/src/libraries/Common/src/Interop/Interop.Ldap.cs @@ -44,6 +44,7 @@ internal struct SEC_WINNT_AUTH_IDENTITY_EX public string packageList; public int packageListLength; +#if NET7_0_OR_GREATER [StructLayout(LayoutKind.Sequential)] [CustomTypeMarshaller(typeof(SEC_WINNT_AUTH_IDENTITY_EX))] internal struct Native @@ -83,6 +84,7 @@ public void FreeNative() Marshal.FreeCoTaskMem(packageList); } } +#endif } internal enum BindMethod : uint // Not Supported in Linux From 2ecc8990d5d42cf790a2133cf2d1968a537271f8 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 22 Feb 2022 10:07:16 -0800 Subject: [PATCH 07/24] Fix conditional --- src/libraries/Common/src/Interop/Interop.Ldap.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/Common/src/Interop/Interop.Ldap.cs b/src/libraries/Common/src/Interop/Interop.Ldap.cs index d7d3174769f3f..e8d7717a7d2d5 100644 --- a/src/libraries/Common/src/Interop/Interop.Ldap.cs +++ b/src/libraries/Common/src/Interop/Interop.Ldap.cs @@ -45,8 +45,9 @@ internal struct SEC_WINNT_AUTH_IDENTITY_EX public int packageListLength; #if NET7_0_OR_GREATER - [StructLayout(LayoutKind.Sequential)] [CustomTypeMarshaller(typeof(SEC_WINNT_AUTH_IDENTITY_EX))] +#endif + [StructLayout(LayoutKind.Sequential)] internal struct Native { public int version; @@ -84,7 +85,6 @@ public void FreeNative() Marshal.FreeCoTaskMem(packageList); } } -#endif } internal enum BindMethod : uint // Not Supported in Linux From 78e9abd38107491397b8c561ab853e0fade030aa Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 22 Feb 2022 12:28:52 -0800 Subject: [PATCH 08/24] Fix DllImportGenerator unit tests --- .../AdditionalAttributesOnStub.cs | 4 ++ .../AttributeForwarding.cs | 5 +++ .../CodeSnippets.cs | 45 +++++++------------ 3 files changed, 26 insertions(+), 28 deletions(-) diff --git a/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/AdditionalAttributesOnStub.cs b/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/AdditionalAttributesOnStub.cs index 640ee220f088b..883038ad362c6 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/AdditionalAttributesOnStub.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/AdditionalAttributesOnStub.cs @@ -31,6 +31,7 @@ struct S { } +[CustomTypeMarshaller(typeof(S))] struct Native { public Native(S s) { } @@ -120,6 +121,7 @@ struct S { } +[CustomTypeMarshaller(typeof(S))] struct Native { public Native(S s) { } @@ -152,6 +154,7 @@ struct S { } +[CustomTypeMarshaller(typeof(S))] struct Native { public Native(S s) { } @@ -184,6 +187,7 @@ struct S { } +[CustomTypeMarshaller(typeof(S))] struct Native { public Native(S s) { } diff --git a/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/AttributeForwarding.cs b/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/AttributeForwarding.cs index 978bf1c4ca8f7..d9396c6dbf392 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/AttributeForwarding.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/AttributeForwarding.cs @@ -38,6 +38,7 @@ struct S {{ }} +[CustomTypeMarshaller(typeof(S))] struct Native {{ public Native(S s) {{ }} @@ -79,6 +80,7 @@ struct S { } +[CustomTypeMarshaller(typeof(S))] struct Native { public Native(S s) { } @@ -122,6 +124,7 @@ struct S { } +[CustomTypeMarshaller(typeof(S))] struct Native { public Native(S s) { } @@ -169,6 +172,7 @@ struct S { } +[CustomTypeMarshaller(typeof(S))] struct Native { public Native(S s) { } @@ -224,6 +228,7 @@ struct S { } +[CustomTypeMarshaller(typeof(S))] struct Native { public Native(S s) { } diff --git a/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/CodeSnippets.cs b/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/CodeSnippets.cs index 71739d2a73647..b8fe99cf3b4a7 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/CodeSnippets.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/CodeSnippets.cs @@ -593,7 +593,7 @@ struct S public bool b; } -[CustomTypeMarshaller] +[CustomTypeMarshaller(typeof(S))] struct Native { private int i; @@ -612,6 +612,7 @@ struct S public bool b; } +[CustomTypeMarshaller(typeof(S))] struct Native { private int i; @@ -631,7 +632,7 @@ struct S public bool b; } -[CustomTypeMarshaller] +[CustomTypeMarshaller(typeof(S), BufferSize = 1)] struct Native { private int i; @@ -641,8 +642,6 @@ public Native(S s, System.Span b) } public S ToManaged() => new S { b = i != 0 }; - - public const int BufferSize = 1; } "; public static string CustomStructMarshallingStackallocOnlyRefParameter = BasicParameterWithByRefModifier("ref", "S", DisableRuntimeMarshalling) + @" @@ -652,7 +651,7 @@ struct S public bool b; } -[CustomTypeMarshaller] +[CustomTypeMarshaller(typeof(S), BufferSize = 1, RequiresStackBuffer = false)] struct Native { private int i; @@ -662,9 +661,6 @@ public Native(S s, System.Span b) } public S ToManaged() => new S { b = i != 0 }; - - public const int BufferSize = 1; - public const bool RequiresStackBuffer = false; } "; public static string CustomStructMarshallingOptionalStackallocParametersAndModifiers = BasicParametersAndModifiers("S", DisableRuntimeMarshalling) + @" @@ -674,7 +670,7 @@ struct S public bool b; } -[CustomTypeMarshaller] +[CustomTypeMarshaller(typeof(S), BufferSize = 1, RequiresStackBuffer = true)] struct Native { private int i; @@ -688,9 +684,6 @@ public Native(S s) } public S ToManaged() => new S { b = i != 0 }; - - public const int BufferSize = 1; - public const bool RequiresStackBuffer = true; } "; @@ -701,7 +694,7 @@ struct S public bool b; } -[CustomTypeMarshaller] +[CustomTypeMarshaller(typeof(S), BufferSize = 1)] struct Native { public Native(S s, System.Span b) @@ -712,8 +705,6 @@ public Native(S s, System.Span b) public S ToManaged() => new S { b = Value != 0 }; public int Value { get; set; } - - public const int BufferSize = 1; } "; public static string CustomStructMarshallingValuePropertyParametersAndModifiers = BasicParametersAndModifiers("S", DisableRuntimeMarshalling) + @" @@ -723,7 +714,7 @@ struct S public bool b; } -[CustomTypeMarshaller] +[CustomTypeMarshaller(typeof(S))] struct Native { public Native(S s) @@ -745,7 +736,7 @@ class S public ref int GetPinnableReference() => ref i; } -[CustomTypeMarshaller] +[CustomTypeMarshaller(typeof(S))] unsafe struct Native { private int* ptr; @@ -777,7 +768,7 @@ class S public byte c; } -[CustomTypeMarshaller] +[CustomTypeMarshaller(typeof(S), BufferSize = 1)] unsafe ref struct Native { private byte* ptr; @@ -815,8 +806,6 @@ public void FreeNative() Marshal.FreeCoTaskMem((IntPtr)ptr); } } - - public const int BufferSize = 1; } partial class Test @@ -835,7 +824,7 @@ class S public byte c = 0; } -[CustomTypeMarshaller] +[CustomTypeMarshaller(typeof(S))] unsafe struct Native { private S value; @@ -886,7 +875,7 @@ struct S public bool b; } -[CustomTypeMarshaller] +[CustomTypeMarshaller(typeof(S))] struct Native { private int i; @@ -905,7 +894,7 @@ struct S public bool b; } -[CustomTypeMarshaller] +[CustomTypeMarshaller(typeof(S))] struct Native { private int i; @@ -938,7 +927,7 @@ public struct IntStructWrapper public int Value; } -[CustomTypeMarshaller] +[CustomTypeMarshaller(typeof(IntStructWrapper))] public struct IntStructWrapperNative { public IntStructWrapperNative(IntStructWrapper managed) @@ -959,7 +948,7 @@ public struct IntStructWrapper public int Value; } -[CustomTypeMarshaller] +[CustomTypeMarshaller(typeof(IntStructWrapper))] public struct IntStructWrapperNative { private int value; @@ -1085,7 +1074,7 @@ public static string CollectionByValue(string elementType) => BasicParameterByVa [NativeMarshalling(typeof(Marshaller<>))] class TestCollection {} -[CustomTypeMarshaller(CustomTypeMarshallerKind.SpanCollection)] +[CustomTypeMarshaller(typeof(TestCollection<>), CustomTypeMarshallerKind.SpanCollection)] ref struct Marshaller { public Marshaller(TestCollection managed, int nativeElementSize) : this() {} @@ -1119,7 +1108,7 @@ public static string CustomCollectionWithMarshaller(bool enableDefaultMarshallin string nativeMarshallingAttribute = enableDefaultMarshalling ? "[NativeMarshalling(typeof(Marshaller<>))]" : string.Empty; return nativeMarshallingAttribute + @"class TestCollection {} -[CustomTypeMarshaller(CustomTypeMarshallerKind.SpanCollection)] +[CustomTypeMarshaller(typeof(TestCollection<>), CustomTypeMarshallerKind.SpanCollection)] ref struct Marshaller { public Marshaller(int nativeElementSize) : this() {} @@ -1216,7 +1205,7 @@ public static partial void Method( [NativeMarshalling(typeof(Marshaller<,>))] class TestCollection {} -[CustomTypeMarshaller(CustomTypeMarshallerKind.SpanCollection)] +[CustomTypeMarshaller(typeof(TestCollection<>), CustomTypeMarshallerKind.SpanCollection)] ref struct Marshaller { public Marshaller(TestCollection managed, int nativeElementSize) : this() {} From 3104a16d3359e3fe33b11f8ce62051de13e64727 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 22 Feb 2022 13:56:52 -0800 Subject: [PATCH 09/24] More test fixes --- .../tests/DllImportGenerator.UnitTests/AttributeForwarding.cs | 1 + .../ManualTypeMarshallingAnalyzerTests.cs | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/AttributeForwarding.cs b/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/AttributeForwarding.cs index f799f47446c1d..60d120eebe4e7 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/AttributeForwarding.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/AttributeForwarding.cs @@ -224,6 +224,7 @@ struct S {{ }} +[CustomTypeMarshaller(typeof(S))] struct Native {{ public Native(S s) {{ }} diff --git a/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/ManualTypeMarshallingAnalyzerTests.cs b/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/ManualTypeMarshallingAnalyzerTests.cs index 482c5a3bec0ba..bd3ffbbede06d 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/ManualTypeMarshallingAnalyzerTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/ManualTypeMarshallingAnalyzerTests.cs @@ -843,10 +843,10 @@ class {|#0:S|} public ref byte GetPinnableReference() => ref c; } -[CustomTypeMarshaller(typeof(S))] +[CustomTypeMarshaller(typeof(S), BufferSize = 0x100)] struct {|#1:Native|} { - public Native(S s, Span buffer, BufferSize = 0x100) {} + public Native(S s, Span buffer) {} public IntPtr Value => IntPtr.Zero; }"; From ec76402fc68f10bab9f995a61312eca8522d28ce Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 1 Mar 2022 09:46:49 -0800 Subject: [PATCH 10/24] Add missing attribute --- .../tests/TestAssets/SharedTypes/NonBlittable.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) 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 bd26bccace24a..6868b7f86f006 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/NonBlittable.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/NonBlittable.cs @@ -197,6 +197,7 @@ public void FreeNative() } } + [CustomTypeMarshaller(typeof(string), BufferSize = 0x100, RequiresStackBuffer = true)] public unsafe ref struct Utf8StringMarshaller { private byte* allocated; @@ -240,9 +241,6 @@ public byte* Value public string? ToManaged() => Marshal.PtrToStringUTF8((IntPtr)Value); public void FreeNative() => Marshal.FreeCoTaskMem((IntPtr)allocated); - - public const int BufferSize = 0x100; - public const bool RequiresStackBuffer = true; } [NativeMarshalling(typeof(IntStructWrapperNative))] From 91a585cb7e0085d76aefad06860e48ea18f2319d Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 2 Mar 2022 13:27:00 -0800 Subject: [PATCH 11/24] Do simple renames to match API review. --- .../InteropServices/ArrayMarshaller.cs | 4 +- .../GeneratedMarshallingAttribute.cs | 9 +- .../ManualTypeMarshallingAnalyzer.cs | 4 +- .../PInvokeStubCodeGenerator.cs | 2 +- .../DllImportGenerator/Resources.Designer.cs | 4 +- .../gen/DllImportGenerator/Resources.resx | 4 +- .../ManualTypeMarshallingHelper.cs | 10 +- ...ributedMarshallingModelGeneratorFactory.cs | 14 +- .../Marshalling/Forwarder.cs | 2 +- .../ICustomNativeTypeMarshallingStrategy.cs | 16 +- .../Marshalling/MarshallerHelpers.cs | 2 +- .../MarshallingAttributeInfo.cs | 18 +- .../Resources.Designer.cs | 4 +- .../Resources.resx | 4 +- ...CollectionElementMarshallingCodeContext.cs | 6 +- .../Ancillary.Interop/SpanMarshallers.cs | 10 +- .../DllImportGenerator.Tests/ArrayTests.cs | 2 +- .../CollectionTests.cs | 12 +- .../CodeSnippets.cs | 156 +++++++++--------- .../CompileFails.cs | 4 +- .../ManualTypeMarshallingAnalyzerTests.cs | 14 +- .../TestAssets/SharedTypes/NonBlittable.cs | 2 +- 22 files changed, 151 insertions(+), 152 deletions(-) diff --git a/src/libraries/Common/src/System/Runtime/InteropServices/ArrayMarshaller.cs b/src/libraries/Common/src/System/Runtime/InteropServices/ArrayMarshaller.cs index 7d43be3cd99f5..12c07de2e156a 100644 --- a/src/libraries/Common/src/System/Runtime/InteropServices/ArrayMarshaller.cs +++ b/src/libraries/Common/src/System/Runtime/InteropServices/ArrayMarshaller.cs @@ -15,7 +15,7 @@ namespace System.Runtime.InteropServices.GeneratedMarshalling // Stack-alloc threshold set to 256 bytes to enable small arrays to be passed on the stack. // Number kept small to ensure that P/Invokes with a lot of array parameters doesn't // blow the stack since this is a new optimization in the code-generated interop. - [CustomTypeMarshaller(typeof(CustomTypeMarshallerAttribute.GenericPlaceholder[]), CustomTypeMarshallerKind.SpanCollection, BufferSize = 0x200, RequiresStackBuffer = true)] + [CustomTypeMarshaller(typeof(CustomTypeMarshallerAttribute.GenericPlaceholder[]), CustomTypeMarshallerKind.LinearCollection, BufferSize = 0x200, RequiresStackBuffer = true)] #if DLLIMPORT_GENERATOR_TEST public #else @@ -105,7 +105,7 @@ public void FreeNative() // Stack-alloc threshold set to 256 bytes to enable small arrays to be passed on the stack. // Number kept small to ensure that P/Invokes with a lot of array parameters doesn't // blow the stack since this is a new optimization in the code-generated interop. - [CustomTypeMarshaller(typeof(CustomTypeMarshallerAttribute.GenericPlaceholder*[]), CustomTypeMarshallerKind.SpanCollection, BufferSize = 0x200, RequiresStackBuffer = true)] + [CustomTypeMarshaller(typeof(CustomTypeMarshallerAttribute.GenericPlaceholder*[]), CustomTypeMarshallerKind.LinearCollection, BufferSize = 0x200, RequiresStackBuffer = true)] #if DLLIMPORT_GENERATOR_TEST public #else diff --git a/src/libraries/Common/src/System/Runtime/InteropServices/GeneratedMarshallingAttribute.cs b/src/libraries/Common/src/System/Runtime/InteropServices/GeneratedMarshallingAttribute.cs index a760d468704b9..8608430277af5 100644 --- a/src/libraries/Common/src/System/Runtime/InteropServices/GeneratedMarshallingAttribute.cs +++ b/src/libraries/Common/src/System/Runtime/InteropServices/GeneratedMarshallingAttribute.cs @@ -18,7 +18,7 @@ sealed class GeneratedMarshallingAttribute : Attribute { } - [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class)] + [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class | AttributeTargets.Enum | AttributeTargets.Delegate)] #if DLLIMPORT_GENERATOR_TEST public #else @@ -34,7 +34,7 @@ public NativeMarshallingAttribute(Type nativeType) public Type NativeType { get; } } - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.Field, AllowMultiple = true)] + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.ReturnValue, AllowMultiple = true)] #if DLLIMPORT_GENERATOR_TEST public #else @@ -59,7 +59,7 @@ public MarshalUsingAttribute(Type nativeType) public int ConstantElementCount { get; set; } - public int ElementIndirectionLevel { get; set; } + public int ElementIndirectionDepth { get; set; } public const string ReturnsCountValue = "return-value"; } @@ -81,7 +81,6 @@ public CustomTypeMarshallerAttribute(Type managedType, CustomTypeMarshallerKind public Type ManagedType { get; } public CustomTypeMarshallerKind MarshallerKind { get; } public int BufferSize { get; set; } - public bool RequiresStackBuffer { get; set; } /// /// This type is used as a placeholder for the first generic parameter when generic parameters cannot be used @@ -100,6 +99,6 @@ public struct GenericPlaceholder enum CustomTypeMarshallerKind { Value, - SpanCollection + LinearCollection } } diff --git a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Analyzers/ManualTypeMarshallingAnalyzer.cs b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Analyzers/ManualTypeMarshallingAnalyzer.cs index 58aa42c332e92..61d0f30a59c2c 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Analyzers/ManualTypeMarshallingAnalyzer.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Analyzers/ManualTypeMarshallingAnalyzer.cs @@ -373,7 +373,7 @@ public void AnalyzeMarshallerType(SymbolAnalysisContext context) DiagnosticDescriptor requiredShapeRule = marshallerData.Value.Kind switch { CustomTypeMarshallerKind.Value => NativeTypeMustHaveRequiredShapeRule, - CustomTypeMarshallerKind.SpanCollection => CollectionNativeTypeMustHaveRequiredShapeRule, + CustomTypeMarshallerKind.LinearCollection => CollectionNativeTypeMustHaveRequiredShapeRule, _ => throw new InvalidOperationException() }; @@ -410,7 +410,7 @@ public void AnalyzeMarshallerType(SymbolAnalysisContext context) } } - if (marshallerData.Value.Kind == CustomTypeMarshallerKind.SpanCollection) + if (marshallerData.Value.Kind == CustomTypeMarshallerKind.LinearCollection) { requiredShapeRule = CollectionNativeTypeMustHaveRequiredShapeRule; if (!ManualTypeMarshallingHelper.TryGetManagedValuesProperty(marshallerType, out _) diff --git a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/PInvokeStubCodeGenerator.cs b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/PInvokeStubCodeGenerator.cs index 5da6deb9cc837..b7ee4ed9c3a86 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/PInvokeStubCodeGenerator.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/PInvokeStubCodeGenerator.cs @@ -145,7 +145,7 @@ public PInvokeStubCodeGenerator( // // [GeneratedDllImport(NativeExportsNE_Binary, EntryPoint = "transpose_matrix")] // [return: MarshalUsing(CountElementName = "numColumns")] - // [return: MarshalUsing(CountElementName = "numRows", ElementIndirectionLevel = 1)] + // [return: MarshalUsing(CountElementName = "numRows", ElementIndirectionDepth = 1)] // public static partial int[][] TransposeMatrix( // int[][] matrix, // [MarshalUsing(CountElementName="numColumns")] ref int[] numRows, diff --git a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Resources.Designer.cs b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Resources.Designer.cs index 197d8331ac7f0..1822e428710b6 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Resources.Designer.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Resources.Designer.cs @@ -142,7 +142,7 @@ internal static string CannotHaveMultipleMarshallingAttributesMessage { } /// - /// Looks up a localized string similar to A 'SpanCollection'-kind native type must have at least one of the two marshalling methods as well as a 'ManagedValues' property of type 'Span<T>' for some 'T' and a 'NativeValueStorage' property of type 'Span<byte>' to enable marshalling the managed type.. + /// Looks up a localized string similar to A 'LinearCollection'-kind native type must have at least one of the two marshalling methods as well as a 'ManagedValues' property of type 'Span<T>' for some 'T' and a 'NativeValueStorage' property of type 'Span<byte>' to enable marshalling the managed type.. /// internal static string CollectionNativeTypeMustHaveRequiredShapeDescription { get { @@ -223,7 +223,7 @@ internal static string ConfigurationNotSupportedTitle { } /// - /// Looks up a localized string similar to Only one of 'ConstantElementCount' or 'ElementCountInfo' may be used in a 'MarshalUsingAttribute' for a given 'ElementIndirectionLevel'. + /// Looks up a localized string similar to Only one of 'ConstantElementCount' or 'ElementCountInfo' may be used in a 'MarshalUsingAttribute' for a given 'ElementIndirectionDepth'. /// internal static string ConstantAndElementCountInfoDisallowed { get { diff --git a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Resources.resx b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Resources.resx index 23342ed1ae123..47d5100ad097f 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Resources.resx +++ b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Resources.resx @@ -139,7 +139,7 @@ Specified 'GeneratedDllImportAttribute' arguments cannot be forwarded to 'DllImportAttribute' - A 'SpanCollection'-kind native type must have at least one of the two marshalling methods as well as a 'ManagedValues' property of type 'Span<T>' for some 'T' and a 'NativeValueStorage' property of type 'Span<byte>' to enable marshalling the managed type. + A 'LinearCollection'-kind native type must have at least one of the two marshalling methods as well as a 'ManagedValues' property of type 'Span<T>' for some 'T' and a 'NativeValueStorage' property of type 'Span<byte>' to enable marshalling the managed type. The native type '{0}' must be a value type and have a constructor that takes two parameters, one of type '{1}' and an 'int', or have a parameterless instance method named 'ToManaged' that returns '{1}' as well as a 'ManagedValues' property of type 'Span<T>' for some 'T' and a 'NativeValueStorage' property of type 'Span<byte>' @@ -166,7 +166,7 @@ Specified configuration is not supported by source-generated P/Invokes. - Only one of 'ConstantElementCount' or 'ElementCountInfo' may be used in a 'MarshalUsingAttribute' for a given 'ElementIndirectionLevel' + Only one of 'ConstantElementCount' or 'ElementCountInfo' may be used in a 'MarshalUsingAttribute' for a given 'ElementIndirectionDepth' Automatically converting a P/Invoke with 'PreserveSig' set to 'false' to a source-generated P/Invoke may produce invalid code 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 bab0651781904..50015fd1c80ea 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 @@ -12,7 +12,7 @@ namespace Microsoft.Interop public enum CustomTypeMarshallerKind { Value, - SpanCollection + LinearCollection } public readonly record struct CustomTypeMarshallerData(CustomTypeMarshallerKind Kind, int? BufferSize, bool RequiresStackBuffer); public static class ManualTypeMarshallingHelper @@ -29,7 +29,7 @@ public static class ManualTypeMarshallingHelper public static class MarshalUsingProperties { - public const string ElementIndirectionLevel = nameof(ElementIndirectionLevel); + public const string ElementIndirectionDepth = nameof(ElementIndirectionDepth); public const string CountElementName = nameof(CountElementName); public const string ConstantElementCount = nameof(ConstantElementCount); } @@ -175,7 +175,7 @@ public static bool IsManagedToNativeConstructor( ITypeSymbol managedType, CustomTypeMarshallerKind variant) { - if (variant == CustomTypeMarshallerKind.SpanCollection) + if (variant == CustomTypeMarshallerKind.LinearCollection) { return ctor.Parameters.Length == 2 && SymbolEqualityComparer.Default.Equals(managedType, ctor.Parameters[0].Type) @@ -191,7 +191,7 @@ public static bool IsCallerAllocatedSpanConstructor( ITypeSymbol spanOfByte, CustomTypeMarshallerKind variant) { - if (variant == CustomTypeMarshallerKind.SpanCollection) + if (variant == CustomTypeMarshallerKind.LinearCollection) { return ctor.Parameters.Length == 3 && SymbolEqualityComparer.Default.Equals(managedType, ctor.Parameters[0].Type) @@ -238,7 +238,7 @@ public static bool TryGetManagedValuesProperty(ITypeSymbol type, out IPropertySy return managedValuesProperty is not null; } - public static bool TryGetElementTypeFromSpanCollectionMarshaller(ITypeSymbol type, out ITypeSymbol elementType) + public static bool TryGetElementTypeFromLinearCollectionMarshaller(ITypeSymbol type, out ITypeSymbol elementType) { if (!TryGetManagedValuesProperty(type, out IPropertySymbol managedValuesProperty)) { 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 c944bfabc38eb..e73293b87148e 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 @@ -93,7 +93,7 @@ ExpressionSyntax GetExpressionForParam(TypePositionInfo paramInfo) for (int i = 0; i < numIndirectionLevels; i++) { - if (marshallingInfo is NativeSpanCollectionMarshallingInfo collectionInfo) + if (marshallingInfo is NativeLinearCollectionMarshallingInfo collectionInfo) { type = collectionInfo.ElementType; marshallingInfo = collectionInfo.ElementMarshallingInfo; @@ -129,7 +129,7 @@ static ExpressionSyntax GetIndexedNumElementsExpression(StubCodeContext context, while (currentContext is not null) { - if (currentContext is SpanCollectionElementMarshallingCodeContext collectionContext) + if (currentContext is LinearCollectionElementMarshallingCodeContext collectionContext) { indexerStack.Push(collectionContext.IndexerIdentifier); } @@ -172,7 +172,7 @@ private IMarshallingGenerator CreateCustomNativeTypeMarshaller(TypePositionInfo } // Collections have extra configuration, so handle them here. - if (marshalInfo is NativeSpanCollectionMarshallingInfo collectionMarshallingInfo) + if (marshalInfo is NativeLinearCollectionMarshallingInfo collectionMarshallingInfo) { return CreateNativeCollectionMarshaller(info, context, collectionMarshallingInfo, marshallingStrategy); } @@ -255,24 +255,24 @@ private ICustomNativeTypeMarshallingStrategy DecorateWithValuePropertyStrategy(N private IMarshallingGenerator CreateNativeCollectionMarshaller( TypePositionInfo info, StubCodeContext context, - NativeSpanCollectionMarshallingInfo collectionInfo, + NativeLinearCollectionMarshallingInfo collectionInfo, ICustomNativeTypeMarshallingStrategy marshallingStrategy) { var elementInfo = new TypePositionInfo(collectionInfo.ElementType, collectionInfo.ElementMarshallingInfo) { ManagedIndex = info.ManagedIndex }; IMarshallingGenerator elementMarshaller = _elementMarshallingGenerator.Create( elementInfo, - new SpanCollectionElementMarshallingCodeContext(StubCodeContext.Stage.Setup, string.Empty, context)); + new LinearCollectionElementMarshallingCodeContext(StubCodeContext.Stage.Setup, string.Empty, context)); TypeSyntax elementType = elementMarshaller.AsNativeType(elementInfo); bool isBlittable = elementMarshaller is BlittableMarshaller; if (isBlittable) { - marshallingStrategy = new SpanCollectionWithBlittableElementsMarshalling(marshallingStrategy, collectionInfo.ElementType.Syntax); + marshallingStrategy = new LinearCollectionWithBlittableElementsMarshalling(marshallingStrategy, collectionInfo.ElementType.Syntax); } else { - marshallingStrategy = new SpanCollectionWithNonBlittableElementsMarshalling(marshallingStrategy, elementMarshaller, elementInfo); + marshallingStrategy = new LinearCollectionWithNonBlittableElementsMarshalling(marshallingStrategy, elementMarshaller, elementInfo); } // Explicitly insert the Value property handling here (before numElements handling) so that the numElements handling will be emitted before the Value property handling in unmarshalling. diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/Forwarder.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/Forwarder.cs index 79fd1c9066d3e..4563c9a58ecfd 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/Forwarder.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/Forwarder.cs @@ -41,7 +41,7 @@ private bool TryRehydrateMarshalAsAttribute(TypePositionInfo info, out Attribute { CountInfo countInfo; MarshallingInfo elementMarshallingInfo; - if (info.MarshallingAttributeInfo is NativeSpanCollectionMarshallingInfo collectionMarshalling + if (info.MarshallingAttributeInfo is NativeLinearCollectionMarshallingInfo collectionMarshalling && collectionMarshalling.UseDefaultMarshalling && collectionMarshalling.ElementCountInfo is NoCountInfo or SizeAndParamIndexInfo && collectionMarshalling.ElementMarshallingInfo is NoMarshallingInfo or MarshalAsInfo { UnmanagedType: not UnmanagedType.CustomMarshaler } diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ICustomNativeTypeMarshallingStrategy.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ICustomNativeTypeMarshallingStrategy.cs index 3378939dbfb63..3b912856136f5 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ICustomNativeTypeMarshallingStrategy.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ICustomNativeTypeMarshallingStrategy.cs @@ -706,14 +706,14 @@ public bool UsesNativeIdentifier(TypePositionInfo info, StubCodeContext context) } /// - /// Marshaller that enables support for marshalling blittable elements of a collection via a native type that implements the SpanCollection marshalling spec. + /// Marshaller that enables support for marshalling blittable elements of a collection via a native type that implements the LinearCollection marshalling spec. /// - internal sealed class SpanCollectionWithBlittableElementsMarshalling : ICustomNativeTypeMarshallingStrategy + internal sealed class LinearCollectionWithBlittableElementsMarshalling : ICustomNativeTypeMarshallingStrategy { private readonly ICustomNativeTypeMarshallingStrategy _innerMarshaller; private readonly TypeSyntax _elementType; - public SpanCollectionWithBlittableElementsMarshalling(ICustomNativeTypeMarshallingStrategy innerMarshaller, TypeSyntax elementType) + public LinearCollectionWithBlittableElementsMarshalling(ICustomNativeTypeMarshallingStrategy innerMarshaller, TypeSyntax elementType) { _innerMarshaller = innerMarshaller; _elementType = elementType; @@ -846,15 +846,15 @@ public bool UsesNativeIdentifier(TypePositionInfo info, StubCodeContext context) } /// - /// Marshaller that enables support for marshalling non-blittable elements of a collection via a native type that implements the SpanCollection marshalling spec. + /// Marshaller that enables support for marshalling non-blittable elements of a collection via a native type that implements the LinearCollection marshalling spec. /// - internal sealed class SpanCollectionWithNonBlittableElementsMarshalling : ICustomNativeTypeMarshallingStrategy + internal sealed class LinearCollectionWithNonBlittableElementsMarshalling : ICustomNativeTypeMarshallingStrategy { private readonly ICustomNativeTypeMarshallingStrategy _innerMarshaller; private readonly IMarshallingGenerator _elementMarshaller; private readonly TypePositionInfo _elementInfo; - public SpanCollectionWithNonBlittableElementsMarshalling(ICustomNativeTypeMarshallingStrategy innerMarshaller, + public LinearCollectionWithNonBlittableElementsMarshalling(ICustomNativeTypeMarshallingStrategy innerMarshaller, IMarshallingGenerator elementMarshaller, TypePositionInfo elementInfo) { @@ -900,11 +900,11 @@ private StatementSyntax GenerateContentsMarshallingStatement(TypePositionInfo in { string nativeIdentifier = context.GetIdentifiers(info).native; string nativeSpanIdentifier = MarshallerHelpers.GetNativeSpanIdentifier(info, context); - var elementSetupSubContext = new SpanCollectionElementMarshallingCodeContext( + var elementSetupSubContext = new LinearCollectionElementMarshallingCodeContext( StubCodeContext.Stage.Setup, nativeSpanIdentifier, context); - var elementSubContext = new SpanCollectionElementMarshallingCodeContext( + var elementSubContext = new LinearCollectionElementMarshallingCodeContext( context.CurrentStage, nativeSpanIdentifier, context); diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs index 2497c18e5d02d..f6e012c9ef3a0 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs @@ -232,7 +232,7 @@ public bool AnyIncomingEdge(int to) public static IEnumerable GetDependentElementsOfMarshallingInfo( MarshallingInfo elementMarshallingInfo) { - if (elementMarshallingInfo is NativeSpanCollectionMarshallingInfo nestedCollection) + if (elementMarshallingInfo is NativeLinearCollectionMarshallingInfo nestedCollection) { if (nestedCollection.ElementCountInfo is CountElementCountInfo { ElementInfo: TypePositionInfo nestedCountElement }) { 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 ece5f044c6285..79caded6111e0 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 @@ -163,7 +163,7 @@ public sealed record SafeHandleMarshallingInfo(bool AccessibleDefaultConstructor /// User-applied System.Runtime.InteropServices.NativeMarshallingAttribute /// with a contiguous collection marshaller /// - public sealed record NativeSpanCollectionMarshallingInfo( + public sealed record NativeLinearCollectionMarshallingInfo( ManagedTypeInfo NativeMarshallingType, ManagedTypeInfo? ValuePropertyType, CustomMarshallingFeatures MarshallingFeatures, @@ -577,7 +577,7 @@ private MarshallingInfo CreateInfoFromMarshalAs( ITypeSymbol? valuePropertyType = ManualTypeMarshallingHelper.FindValueProperty(arrayMarshaller)?.Type; - return new NativeSpanCollectionMarshallingInfo( + return new NativeLinearCollectionMarshallingInfo( NativeMarshallingType: ManagedTypeInfo.CreateTypeInfoForTypeSymbol(arrayMarshaller), ValuePropertyType: valuePropertyType is not null ? ManagedTypeInfo.CreateTypeInfoForTypeSymbol(valuePropertyType) : null, MarshallingFeatures: ~CustomMarshallingFeatures.ManagedTypePinning, @@ -661,7 +661,7 @@ private MarshallingInfo CreateNativeMarshallingInfo( // The constructor that takes only the native element size is required for collection marshallers // in the native-to-managed scenario. - if ((customTypeMarshallerData.Value.Kind != CustomTypeMarshallerKind.SpanCollection + if ((customTypeMarshallerData.Value.Kind != CustomTypeMarshallerKind.LinearCollection || (hasInt32Constructor && ManualTypeMarshallingHelper.HasSetUnmarshalledCollectionLengthMethod(nativeType))) && ManualTypeMarshallingHelper.HasToManagedMethod(nativeType, type) && (valueProperty is null or { SetMethod: not null })) @@ -673,7 +673,7 @@ private MarshallingInfo CreateNativeMarshallingInfo( { _diagnostics.ReportInvalidMarshallingAttributeInfo( attrData, - customTypeMarshallerData.Value.Kind == CustomTypeMarshallerKind.SpanCollection + customTypeMarshallerData.Value.Kind == CustomTypeMarshallerKind.LinearCollection ? nameof(Resources.CollectionNativeTypeMustHaveRequiredShapeMessage) : nameof(Resources.NativeTypeMustHaveRequiredShapeMessage), nativeType.ToDisplayString()); @@ -690,7 +690,7 @@ private MarshallingInfo CreateNativeMarshallingInfo( features |= CustomMarshallingFeatures.NativeTypePinning; } - if (customTypeMarshallerData.Value.Kind == CustomTypeMarshallerKind.SpanCollection) + if (customTypeMarshallerData.Value.Kind == CustomTypeMarshallerKind.LinearCollection) { if (!ManualTypeMarshallingHelper.HasNativeValueStorageProperty(nativeType, spanOfByte)) { @@ -698,13 +698,13 @@ private MarshallingInfo CreateNativeMarshallingInfo( return NoMarshallingInfo.Instance; } - if (!ManualTypeMarshallingHelper.TryGetElementTypeFromSpanCollectionMarshaller(nativeType, out ITypeSymbol elementType)) + if (!ManualTypeMarshallingHelper.TryGetElementTypeFromLinearCollectionMarshaller(nativeType, out ITypeSymbol elementType)) { _diagnostics.ReportInvalidMarshallingAttributeInfo(attrData, nameof(Resources.CollectionNativeTypeMustHaveRequiredShapeMessage), nativeType.ToDisplayString()); return NoMarshallingInfo.Instance; } - return new NativeSpanCollectionMarshallingInfo( + return new NativeLinearCollectionMarshallingInfo( ManagedTypeInfo.CreateTypeInfoForTypeSymbol(nativeType), valueProperty is not null ? ManagedTypeInfo.CreateTypeInfoForTypeSymbol(valueProperty.Type) : null, features, @@ -782,7 +782,7 @@ private bool TryCreateTypeBasedMarshallingInfo( var (_, _, customTypeMarshallerData) = ManualTypeMarshallingHelper.GetMarshallerShapeInfo(arrayMarshaller); ITypeSymbol? valuePropertyType = ManualTypeMarshallingHelper.FindValueProperty(arrayMarshaller)?.Type; - marshallingInfo = new NativeSpanCollectionMarshallingInfo( + marshallingInfo = new NativeLinearCollectionMarshallingInfo( NativeMarshallingType: ManagedTypeInfo.CreateTypeInfoForTypeSymbol(arrayMarshaller), ValuePropertyType: valuePropertyType is not null ? ManagedTypeInfo.CreateTypeInfoForTypeSymbol(valuePropertyType) : null, MarshallingFeatures: ~CustomMarshallingFeatures.ManagedTypePinning, @@ -863,7 +863,7 @@ private bool TryGetAttributeIndirectionLevel(AttributeData attrData, out int ind foreach (KeyValuePair arg in attrData.NamedArguments) { - if (arg.Key == ManualTypeMarshallingHelper.MarshalUsingProperties.ElementIndirectionLevel) + if (arg.Key == ManualTypeMarshallingHelper.MarshalUsingProperties.ElementIndirectionDepth) { indirectionLevel = (int)arg.Value.Value!; return true; diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources.Designer.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources.Designer.cs index 499ffc63bca17..973d92a2677b3 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources.Designer.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources.Designer.cs @@ -124,7 +124,7 @@ internal static string CollectionSizeParamTypeMustBeIntegral { } /// - /// Looks up a localized string similar to Only one of 'ConstantElementCount' or 'ElementCountInfo' may be used in a 'MarshalUsingAttribute' for a given 'ElementIndirectionLevel'. + /// Looks up a localized string similar to Only one of 'ConstantElementCount' or 'ElementCountInfo' may be used in a 'MarshalUsingAttribute' for a given 'ElementIndirectionDepth'. /// internal static string ConstantAndElementCountInfoDisallowed { get { @@ -178,7 +178,7 @@ internal static string DuplicateMarshallingInfo { } /// - /// Looks up a localized string similar to Marshalling info was specified for 'ElementIndirectionLevel' {0}, but marshalling info was only needed for {1} level(s) of indirection. + /// Looks up a localized string similar to Marshalling info was specified for 'ElementIndirectionDepth' {0}, but marshalling info was only needed for {1} level(s) of indirection. /// internal static string ExtraneousMarshallingInfo { get { diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources.resx b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources.resx index 3b0e3a125213a..5798ae8eff213 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources.resx +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources.resx @@ -199,7 +199,7 @@ Multiple marshalling attributes per element per indirection level is unsupported, but duplicate information was provided for indirection level {0} - Marshalling info was specified for 'ElementIndirectionLevel' {0}, but marshalling info was only needed for {1} level(s) of indirection + Marshalling info was specified for 'ElementIndirectionDepth' {0}, but marshalling info was only needed for {1} level(s) of indirection The native type '{0}' for managed type '{1}' must be a closed generic type or have the same arity as the managed type. @@ -217,7 +217,7 @@ The native type '{0}' must be a value type and have a constructor that takes two parameters, one of type '{1}' and an 'int', or have a parameterless instance method named 'ToManaged' that returns '{1}' as well as a 'ManagedValues' property of type 'Span<T>' for some 'T' and a 'NativeValueStorage' property of type 'Span<byte>' - Only one of 'ConstantElementCount' or 'ElementCountInfo' may be used in a 'MarshalUsingAttribute' for a given 'ElementIndirectionLevel' + Only one of 'ConstantElementCount' or 'ElementCountInfo' may be used in a 'MarshalUsingAttribute' for a given 'ElementIndirectionDepth' Runtime marshalling must be disabled in this project by applying the 'System.Runtime.InteropServices.DisableRuntimeMarshallingAttribute' to the assembly to enable marshalling this type. diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/SpanCollectionElementMarshallingCodeContext.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/SpanCollectionElementMarshallingCodeContext.cs index 2710ce98ba3cd..4f390113cae26 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/SpanCollectionElementMarshallingCodeContext.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/SpanCollectionElementMarshallingCodeContext.cs @@ -3,7 +3,7 @@ namespace Microsoft.Interop { - internal sealed class SpanCollectionElementMarshallingCodeContext : StubCodeContext + internal sealed class LinearCollectionElementMarshallingCodeContext : StubCodeContext { private readonly string _nativeSpanIdentifier; @@ -20,7 +20,7 @@ internal sealed class SpanCollectionElementMarshallingCodeContext : StubCodeCont /// The indexer in the loop to get the element to marshal from the collection. /// The identifier of the native value storage cast to the target element type. /// The parent context. - public SpanCollectionElementMarshallingCodeContext( + public LinearCollectionElementMarshallingCodeContext( Stage currentStage, string nativeSpanIdentifier, StubCodeContext parentContext) @@ -55,7 +55,7 @@ private static string CalculateIndexerIdentifierBasedOnParentContext(StubCodeCon int i = 0; while (parentContext is StubCodeContext context) { - if (context is SpanCollectionElementMarshallingCodeContext) + if (context is LinearCollectionElementMarshallingCodeContext) { i++; } diff --git a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/SpanMarshallers.cs b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/SpanMarshallers.cs index 59bc65d44f55b..7db20c212f382 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/SpanMarshallers.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/SpanMarshallers.cs @@ -9,7 +9,7 @@ namespace System.Runtime.InteropServices.GeneratedMarshalling // Stack-alloc threshold set to 256 bytes to enable small arrays to be passed on the stack. // Number kept small to ensure that P/Invokes with a lot of array parameters doesn't // blow the stack since this is a new optimization in the code-generated interop. - [CustomTypeMarshaller(typeof(ReadOnlySpan<>), CustomTypeMarshallerKind.SpanCollection, BufferSize = 0x200, RequiresStackBuffer = true)] + [CustomTypeMarshaller(typeof(ReadOnlySpan<>), CustomTypeMarshallerKind.LinearCollection, BufferSize = 0x200, RequiresStackBuffer = true)] public unsafe ref struct ReadOnlySpanMarshaller { private ReadOnlySpan _managedSpan; @@ -90,7 +90,7 @@ public void FreeNative() } } - [CustomTypeMarshaller(typeof(Span<>), CustomTypeMarshallerKind.SpanCollection, BufferSize = 0x200, RequiresStackBuffer = true)] + [CustomTypeMarshaller(typeof(Span<>), CustomTypeMarshallerKind.LinearCollection, BufferSize = 0x200, RequiresStackBuffer = true)] public unsafe ref struct SpanMarshaller { private ReadOnlySpanMarshaller _inner; @@ -143,7 +143,7 @@ public void FreeNative() } } - [CustomTypeMarshaller(typeof(Span<>), CustomTypeMarshallerKind.SpanCollection, BufferSize = 0x200, RequiresStackBuffer = true)] + [CustomTypeMarshaller(typeof(Span<>), CustomTypeMarshallerKind.LinearCollection, BufferSize = 0x200, RequiresStackBuffer = true)] public unsafe ref struct NeverNullSpanMarshaller { private SpanMarshaller _inner; @@ -207,7 +207,7 @@ public void FreeNative() } } - [CustomTypeMarshaller(typeof(ReadOnlySpan<>), CustomTypeMarshallerKind.SpanCollection, BufferSize = 0x200, RequiresStackBuffer = true)] + [CustomTypeMarshaller(typeof(ReadOnlySpan<>), CustomTypeMarshallerKind.LinearCollection, BufferSize = 0x200, RequiresStackBuffer = true)] public unsafe ref struct NeverNullReadOnlySpanMarshaller { private ReadOnlySpanMarshaller _inner; @@ -272,7 +272,7 @@ public void FreeNative() } // Stack-alloc threshold set to 0 so that the generator can use the constructor that takes a stackSpace to let the marshaller know that the original data span can be used and safely pinned. - [CustomTypeMarshaller(typeof(Span<>), CustomTypeMarshallerKind.SpanCollection, BufferSize = 0)] + [CustomTypeMarshaller(typeof(Span<>), CustomTypeMarshallerKind.LinearCollection, BufferSize = 0)] public unsafe ref struct DirectSpanMarshaller where T : unmanaged { diff --git a/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.Tests/ArrayTests.cs b/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.Tests/ArrayTests.cs index bfef66b13a258..9a9678f8896ed 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.Tests/ArrayTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.Tests/ArrayTests.cs @@ -77,7 +77,7 @@ public partial class Arrays [GeneratedDllImport(NativeExportsNE_Binary, EntryPoint = "transpose_matrix")] [return: MarshalUsing(CountElementName = "numColumns")] - [return: MarshalUsing(CountElementName = "numRows", ElementIndirectionLevel = 1)] + [return: MarshalUsing(CountElementName = "numRows", ElementIndirectionDepth = 1)] public static partial int[][] TransposeMatrix(int[][] matrix, int[] numRows, int numColumns); } } diff --git a/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.Tests/CollectionTests.cs b/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.Tests/CollectionTests.cs index e0b068ae5cb91..8f5f948f9ca6f 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.Tests/CollectionTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.Tests/CollectionTests.cs @@ -36,20 +36,20 @@ public partial class Collections public static partial void CreateRange_Out(int start, int end, out int numValues, [MarshalUsing(typeof(ListMarshaller), CountElementName = "numValues")] out List res); [GeneratedDllImport(NativeExportsNE_Binary, EntryPoint = "sum_string_lengths")] - public static partial int SumStringLengths([MarshalUsing(typeof(ListMarshaller)), MarshalUsing(typeof(Utf16StringMarshaller), ElementIndirectionLevel = 1)] List strArray); + public static partial int SumStringLengths([MarshalUsing(typeof(ListMarshaller)), MarshalUsing(typeof(Utf16StringMarshaller), ElementIndirectionDepth = 1)] List strArray); [GeneratedDllImport(NativeExportsNE_Binary, EntryPoint = "reverse_strings_replace")] - public static partial void ReverseStrings_Ref([MarshalUsing(typeof(ListMarshaller), CountElementName = "numElements"), MarshalUsing(typeof(Utf16StringMarshaller), ElementIndirectionLevel = 1)] ref List strArray, out int numElements); + public static partial void ReverseStrings_Ref([MarshalUsing(typeof(ListMarshaller), CountElementName = "numElements"), MarshalUsing(typeof(Utf16StringMarshaller), ElementIndirectionDepth = 1)] ref List strArray, out int numElements); [GeneratedDllImport(NativeExportsNE_Binary, EntryPoint = "reverse_strings_return")] - [return: MarshalUsing(typeof(ListMarshaller), CountElementName = "numElements"), MarshalUsing(typeof(Utf16StringMarshaller), ElementIndirectionLevel = 1)] - public static partial List ReverseStrings_Return([MarshalUsing(typeof(ListMarshaller), CountElementName = "numElements"), MarshalUsing(typeof(Utf16StringMarshaller), ElementIndirectionLevel = 1)] List strArray, out int numElements); + [return: MarshalUsing(typeof(ListMarshaller), CountElementName = "numElements"), MarshalUsing(typeof(Utf16StringMarshaller), ElementIndirectionDepth = 1)] + public static partial List ReverseStrings_Return([MarshalUsing(typeof(ListMarshaller), CountElementName = "numElements"), MarshalUsing(typeof(Utf16StringMarshaller), ElementIndirectionDepth = 1)] List strArray, out int numElements); [GeneratedDllImport(NativeExportsNE_Binary, EntryPoint = "reverse_strings_out")] public static partial void ReverseStrings_Out( - [MarshalUsing(typeof(ListMarshaller)), MarshalUsing(typeof(Utf16StringMarshaller), ElementIndirectionLevel = 1)] List strArray, + [MarshalUsing(typeof(ListMarshaller)), MarshalUsing(typeof(Utf16StringMarshaller), ElementIndirectionDepth = 1)] List strArray, out int numElements, - [MarshalUsing(typeof(ListMarshaller), CountElementName = "numElements"), MarshalUsing(typeof(Utf16StringMarshaller), ElementIndirectionLevel = 1)] out List res); + [MarshalUsing(typeof(ListMarshaller), CountElementName = "numElements"), MarshalUsing(typeof(Utf16StringMarshaller), ElementIndirectionDepth = 1)] out List res); [GeneratedDllImport(NativeExportsNE_Binary, EntryPoint = "get_long_bytes")] [return:MarshalUsing(typeof(ListMarshaller), ConstantElementCount = sizeof(long))] diff --git a/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/CodeSnippets.cs b/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/CodeSnippets.cs index f01191e15f5e2..8b52ce19d22de 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/CodeSnippets.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/CodeSnippets.cs @@ -1117,7 +1117,7 @@ public static string CollectionByValue(string elementType) => BasicParameterByVa [NativeMarshalling(typeof(Marshaller<>))] class TestCollection {} -[CustomTypeMarshaller(typeof(TestCollection<>), CustomTypeMarshallerKind.SpanCollection)] +[CustomTypeMarshaller(typeof(TestCollection<>), CustomTypeMarshallerKind.LinearCollection)] ref struct Marshaller { public Marshaller(TestCollection managed, int nativeElementSize) : this() {} @@ -1151,7 +1151,7 @@ public static string CustomCollectionWithMarshaller(bool enableDefaultMarshallin string nativeMarshallingAttribute = enableDefaultMarshalling ? "[NativeMarshalling(typeof(Marshaller<>))]" : string.Empty; return nativeMarshallingAttribute + @"class TestCollection {} -[CustomTypeMarshaller(typeof(TestCollection<>), CustomTypeMarshallerKind.SpanCollection)] +[CustomTypeMarshaller(typeof(TestCollection<>), CustomTypeMarshallerKind.LinearCollection)] ref struct Marshaller { public Marshaller(int nativeElementSize) : this() {} @@ -1248,7 +1248,7 @@ public static partial void Method( [NativeMarshalling(typeof(Marshaller<,>))] class TestCollection {} -[CustomTypeMarshaller(typeof(TestCollection<>), CustomTypeMarshallerKind.SpanCollection)] +[CustomTypeMarshaller(typeof(TestCollection<>), CustomTypeMarshallerKind.LinearCollection)] ref struct Marshaller { public Marshaller(TestCollection managed, int nativeElementSize) : this() {} @@ -1265,13 +1265,13 @@ partial class Test { [GeneratedDllImport(""DoesNotExist"")] [return:MarshalUsing(ConstantElementCount=10)] - [return:MarshalUsing(typeof(IntWrapper), ElementIndirectionLevel = 1)] + [return:MarshalUsing(typeof(IntWrapper), ElementIndirectionDepth = 1)] public static partial TestCollection Method( - [MarshalUsing(typeof(IntWrapper), ElementIndirectionLevel = 1)] TestCollection p, - [MarshalUsing(typeof(IntWrapper), ElementIndirectionLevel = 1)] in TestCollection pIn, + [MarshalUsing(typeof(IntWrapper), ElementIndirectionDepth = 1)] TestCollection p, + [MarshalUsing(typeof(IntWrapper), ElementIndirectionDepth = 1)] in TestCollection pIn, int pRefSize, - [MarshalUsing(CountElementName = ""pRefSize""), MarshalUsing(typeof(IntWrapper), ElementIndirectionLevel = 1)] ref TestCollection pRef, - [MarshalUsing(CountElementName = ""pOutSize"")][MarshalUsing(typeof(IntWrapper), ElementIndirectionLevel = 1)] out TestCollection pOut, + [MarshalUsing(CountElementName = ""pRefSize""), MarshalUsing(typeof(IntWrapper), ElementIndirectionDepth = 1)] ref TestCollection pRef, + [MarshalUsing(CountElementName = ""pOutSize"")][MarshalUsing(typeof(IntWrapper), ElementIndirectionDepth = 1)] out TestCollection pOut, out int pOutSize ); } @@ -1284,14 +1284,14 @@ public IntWrapper(int i){} " + CustomCollectionWithMarshaller(enableDefaultMarshalling: true); - public static string GenericCollectionWithCustomElementMarshallingDuplicateElementIndirectionLevel => @" + public static string GenericCollectionWithCustomElementMarshallingDuplicateElementIndirectionDepth => @" using System.Runtime.InteropServices; [assembly:System.Runtime.CompilerServices.DisableRuntimeMarshalling] partial class Test { [GeneratedDllImport(""DoesNotExist"")] public static partial void Method( - [MarshalUsing(typeof(IntWrapper), ElementIndirectionLevel = 1)] [MarshalUsing(typeof(IntWrapper), ElementIndirectionLevel = 1)] TestCollection p); + [MarshalUsing(typeof(IntWrapper), ElementIndirectionDepth = 1)] [MarshalUsing(typeof(IntWrapper), ElementIndirectionDepth = 1)] TestCollection p); } struct IntWrapper @@ -1302,14 +1302,14 @@ public IntWrapper(int i){} " + CustomCollectionWithMarshaller(enableDefaultMarshalling: true); - public static string GenericCollectionWithCustomElementMarshallingUnusedElementIndirectionLevel => @" + public static string GenericCollectionWithCustomElementMarshallingUnusedElementIndirectionDepth => @" using System.Runtime.InteropServices; [assembly:System.Runtime.CompilerServices.DisableRuntimeMarshalling] partial class Test { [GeneratedDllImport(""DoesNotExist"")] public static partial void Method( - [MarshalUsing(typeof(IntWrapper), ElementIndirectionLevel = 2)] TestCollection p); + [MarshalUsing(typeof(IntWrapper), ElementIndirectionDepth = 2)] TestCollection p); } struct IntWrapper @@ -1386,72 +1386,72 @@ partial class Test { [GeneratedDllImport(""DoesNotExist"")] public static partial void Method( - [MarshalUsing(CountElementName=""arr0"", ElementIndirectionLevel = 0)] - [MarshalUsing(CountElementName=""arr1"", ElementIndirectionLevel = 1)] - [MarshalUsing(CountElementName=""arr2"", ElementIndirectionLevel = 2)] - [MarshalUsing(CountElementName=""arr3"", ElementIndirectionLevel = 3)] - [MarshalUsing(CountElementName=""arr4"", ElementIndirectionLevel = 4)] - [MarshalUsing(CountElementName=""arr5"", ElementIndirectionLevel = 5)] - [MarshalUsing(CountElementName=""arr6"", ElementIndirectionLevel = 6)] - [MarshalUsing(CountElementName=""arr7"", ElementIndirectionLevel = 7)] - [MarshalUsing(CountElementName=""arr8"", ElementIndirectionLevel = 8)] - [MarshalUsing(CountElementName=""arr9"", ElementIndirectionLevel = 9)] - [MarshalUsing(CountElementName=""arr10"", ElementIndirectionLevel = 10)] ref int[][][][][][][][][][][] arr11, - [MarshalUsing(CountElementName=""arr0"", ElementIndirectionLevel = 0)] - [MarshalUsing(CountElementName=""arr1"", ElementIndirectionLevel = 1)] - [MarshalUsing(CountElementName=""arr2"", ElementIndirectionLevel = 2)] - [MarshalUsing(CountElementName=""arr3"", ElementIndirectionLevel = 3)] - [MarshalUsing(CountElementName=""arr4"", ElementIndirectionLevel = 4)] - [MarshalUsing(CountElementName=""arr5"", ElementIndirectionLevel = 5)] - [MarshalUsing(CountElementName=""arr6"", ElementIndirectionLevel = 6)] - [MarshalUsing(CountElementName=""arr7"", ElementIndirectionLevel = 7)] - [MarshalUsing(CountElementName=""arr8"", ElementIndirectionLevel = 8)] - [MarshalUsing(CountElementName=""arr9"", ElementIndirectionLevel = 9)]ref int[][][][][][][][][][] arr10, - [MarshalUsing(CountElementName=""arr0"", ElementIndirectionLevel = 0)] - [MarshalUsing(CountElementName=""arr1"", ElementIndirectionLevel = 1)] - [MarshalUsing(CountElementName=""arr2"", ElementIndirectionLevel = 2)] - [MarshalUsing(CountElementName=""arr3"", ElementIndirectionLevel = 3)] - [MarshalUsing(CountElementName=""arr4"", ElementIndirectionLevel = 4)] - [MarshalUsing(CountElementName=""arr5"", ElementIndirectionLevel = 5)] - [MarshalUsing(CountElementName=""arr6"", ElementIndirectionLevel = 6)] - [MarshalUsing(CountElementName=""arr7"", ElementIndirectionLevel = 7)] - [MarshalUsing(CountElementName=""arr8"", ElementIndirectionLevel = 8)]ref int[][][][][][][][][] arr9, - [MarshalUsing(CountElementName=""arr0"", ElementIndirectionLevel = 0)] - [MarshalUsing(CountElementName=""arr1"", ElementIndirectionLevel = 1)] - [MarshalUsing(CountElementName=""arr2"", ElementIndirectionLevel = 2)] - [MarshalUsing(CountElementName=""arr3"", ElementIndirectionLevel = 3)] - [MarshalUsing(CountElementName=""arr4"", ElementIndirectionLevel = 4)] - [MarshalUsing(CountElementName=""arr5"", ElementIndirectionLevel = 5)] - [MarshalUsing(CountElementName=""arr6"", ElementIndirectionLevel = 6)] - [MarshalUsing(CountElementName=""arr7"", ElementIndirectionLevel = 7)]ref int[][][][][][][][][] arr8, - [MarshalUsing(CountElementName=""arr0"", ElementIndirectionLevel = 0)] - [MarshalUsing(CountElementName=""arr1"", ElementIndirectionLevel = 1)] - [MarshalUsing(CountElementName=""arr2"", ElementIndirectionLevel = 2)] - [MarshalUsing(CountElementName=""arr3"", ElementIndirectionLevel = 3)] - [MarshalUsing(CountElementName=""arr4"", ElementIndirectionLevel = 4)] - [MarshalUsing(CountElementName=""arr5"", ElementIndirectionLevel = 5)] - [MarshalUsing(CountElementName=""arr6"", ElementIndirectionLevel = 6)]ref int[][][][][][][] arr7, - [MarshalUsing(CountElementName=""arr0"", ElementIndirectionLevel = 0)] - [MarshalUsing(CountElementName=""arr1"", ElementIndirectionLevel = 1)] - [MarshalUsing(CountElementName=""arr2"", ElementIndirectionLevel = 2)] - [MarshalUsing(CountElementName=""arr3"", ElementIndirectionLevel = 3)] - [MarshalUsing(CountElementName=""arr4"", ElementIndirectionLevel = 4)] - [MarshalUsing(CountElementName=""arr5"", ElementIndirectionLevel = 5)]ref int[][][][][][] arr6, - [MarshalUsing(CountElementName=""arr0"", ElementIndirectionLevel = 0)] - [MarshalUsing(CountElementName=""arr1"", ElementIndirectionLevel = 1)] - [MarshalUsing(CountElementName=""arr2"", ElementIndirectionLevel = 2)] - [MarshalUsing(CountElementName=""arr3"", ElementIndirectionLevel = 3)] - [MarshalUsing(CountElementName=""arr4"", ElementIndirectionLevel = 4)]ref int[][][][][] arr5, - [MarshalUsing(CountElementName=""arr0"", ElementIndirectionLevel = 0)] - [MarshalUsing(CountElementName=""arr1"", ElementIndirectionLevel = 1)] - [MarshalUsing(CountElementName=""arr2"", ElementIndirectionLevel = 2)] - [MarshalUsing(CountElementName=""arr3"", ElementIndirectionLevel = 3)]ref int[][][][] arr4, - [MarshalUsing(CountElementName=""arr0"", ElementIndirectionLevel = 0)] - [MarshalUsing(CountElementName=""arr1"", ElementIndirectionLevel = 1)] - [MarshalUsing(CountElementName=""arr2"", ElementIndirectionLevel = 2)]ref int[][][] arr3, - [MarshalUsing(CountElementName=""arr0"", ElementIndirectionLevel = 0)] - [MarshalUsing(CountElementName=""arr1"", ElementIndirectionLevel = 1)]ref int[][] arr2, - [MarshalUsing(CountElementName=""arr0"", ElementIndirectionLevel = 0)]ref int[] arr1, + [MarshalUsing(CountElementName=""arr0"", ElementIndirectionDepth = 0)] + [MarshalUsing(CountElementName=""arr1"", ElementIndirectionDepth = 1)] + [MarshalUsing(CountElementName=""arr2"", ElementIndirectionDepth = 2)] + [MarshalUsing(CountElementName=""arr3"", ElementIndirectionDepth = 3)] + [MarshalUsing(CountElementName=""arr4"", ElementIndirectionDepth = 4)] + [MarshalUsing(CountElementName=""arr5"", ElementIndirectionDepth = 5)] + [MarshalUsing(CountElementName=""arr6"", ElementIndirectionDepth = 6)] + [MarshalUsing(CountElementName=""arr7"", ElementIndirectionDepth = 7)] + [MarshalUsing(CountElementName=""arr8"", ElementIndirectionDepth = 8)] + [MarshalUsing(CountElementName=""arr9"", ElementIndirectionDepth = 9)] + [MarshalUsing(CountElementName=""arr10"", ElementIndirectionDepth = 10)] ref int[][][][][][][][][][][] arr11, + [MarshalUsing(CountElementName=""arr0"", ElementIndirectionDepth = 0)] + [MarshalUsing(CountElementName=""arr1"", ElementIndirectionDepth = 1)] + [MarshalUsing(CountElementName=""arr2"", ElementIndirectionDepth = 2)] + [MarshalUsing(CountElementName=""arr3"", ElementIndirectionDepth = 3)] + [MarshalUsing(CountElementName=""arr4"", ElementIndirectionDepth = 4)] + [MarshalUsing(CountElementName=""arr5"", ElementIndirectionDepth = 5)] + [MarshalUsing(CountElementName=""arr6"", ElementIndirectionDepth = 6)] + [MarshalUsing(CountElementName=""arr7"", ElementIndirectionDepth = 7)] + [MarshalUsing(CountElementName=""arr8"", ElementIndirectionDepth = 8)] + [MarshalUsing(CountElementName=""arr9"", ElementIndirectionDepth = 9)]ref int[][][][][][][][][][] arr10, + [MarshalUsing(CountElementName=""arr0"", ElementIndirectionDepth = 0)] + [MarshalUsing(CountElementName=""arr1"", ElementIndirectionDepth = 1)] + [MarshalUsing(CountElementName=""arr2"", ElementIndirectionDepth = 2)] + [MarshalUsing(CountElementName=""arr3"", ElementIndirectionDepth = 3)] + [MarshalUsing(CountElementName=""arr4"", ElementIndirectionDepth = 4)] + [MarshalUsing(CountElementName=""arr5"", ElementIndirectionDepth = 5)] + [MarshalUsing(CountElementName=""arr6"", ElementIndirectionDepth = 6)] + [MarshalUsing(CountElementName=""arr7"", ElementIndirectionDepth = 7)] + [MarshalUsing(CountElementName=""arr8"", ElementIndirectionDepth = 8)]ref int[][][][][][][][][] arr9, + [MarshalUsing(CountElementName=""arr0"", ElementIndirectionDepth = 0)] + [MarshalUsing(CountElementName=""arr1"", ElementIndirectionDepth = 1)] + [MarshalUsing(CountElementName=""arr2"", ElementIndirectionDepth = 2)] + [MarshalUsing(CountElementName=""arr3"", ElementIndirectionDepth = 3)] + [MarshalUsing(CountElementName=""arr4"", ElementIndirectionDepth = 4)] + [MarshalUsing(CountElementName=""arr5"", ElementIndirectionDepth = 5)] + [MarshalUsing(CountElementName=""arr6"", ElementIndirectionDepth = 6)] + [MarshalUsing(CountElementName=""arr7"", ElementIndirectionDepth = 7)]ref int[][][][][][][][][] arr8, + [MarshalUsing(CountElementName=""arr0"", ElementIndirectionDepth = 0)] + [MarshalUsing(CountElementName=""arr1"", ElementIndirectionDepth = 1)] + [MarshalUsing(CountElementName=""arr2"", ElementIndirectionDepth = 2)] + [MarshalUsing(CountElementName=""arr3"", ElementIndirectionDepth = 3)] + [MarshalUsing(CountElementName=""arr4"", ElementIndirectionDepth = 4)] + [MarshalUsing(CountElementName=""arr5"", ElementIndirectionDepth = 5)] + [MarshalUsing(CountElementName=""arr6"", ElementIndirectionDepth = 6)]ref int[][][][][][][] arr7, + [MarshalUsing(CountElementName=""arr0"", ElementIndirectionDepth = 0)] + [MarshalUsing(CountElementName=""arr1"", ElementIndirectionDepth = 1)] + [MarshalUsing(CountElementName=""arr2"", ElementIndirectionDepth = 2)] + [MarshalUsing(CountElementName=""arr3"", ElementIndirectionDepth = 3)] + [MarshalUsing(CountElementName=""arr4"", ElementIndirectionDepth = 4)] + [MarshalUsing(CountElementName=""arr5"", ElementIndirectionDepth = 5)]ref int[][][][][][] arr6, + [MarshalUsing(CountElementName=""arr0"", ElementIndirectionDepth = 0)] + [MarshalUsing(CountElementName=""arr1"", ElementIndirectionDepth = 1)] + [MarshalUsing(CountElementName=""arr2"", ElementIndirectionDepth = 2)] + [MarshalUsing(CountElementName=""arr3"", ElementIndirectionDepth = 3)] + [MarshalUsing(CountElementName=""arr4"", ElementIndirectionDepth = 4)]ref int[][][][][] arr5, + [MarshalUsing(CountElementName=""arr0"", ElementIndirectionDepth = 0)] + [MarshalUsing(CountElementName=""arr1"", ElementIndirectionDepth = 1)] + [MarshalUsing(CountElementName=""arr2"", ElementIndirectionDepth = 2)] + [MarshalUsing(CountElementName=""arr3"", ElementIndirectionDepth = 3)]ref int[][][][] arr4, + [MarshalUsing(CountElementName=""arr0"", ElementIndirectionDepth = 0)] + [MarshalUsing(CountElementName=""arr1"", ElementIndirectionDepth = 1)] + [MarshalUsing(CountElementName=""arr2"", ElementIndirectionDepth = 2)]ref int[][][] arr3, + [MarshalUsing(CountElementName=""arr0"", ElementIndirectionDepth = 0)] + [MarshalUsing(CountElementName=""arr1"", ElementIndirectionDepth = 1)]ref int[][] arr2, + [MarshalUsing(CountElementName=""arr0"", ElementIndirectionDepth = 0)]ref int[] arr1, ref int arr0 ); } diff --git a/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/CompileFails.cs b/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/CompileFails.cs index f71ae162ed088..2631fa2930dfe 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/CompileFails.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/CompileFails.cs @@ -104,8 +104,8 @@ public static IEnumerable CodeSnippetsToCompile() yield return new object[] { CodeSnippets.GenericCollectionMarshallingArityMismatch, 2, 0 }; yield return new object[] { CodeSnippets.MarshalAsAndMarshalUsingOnReturnValue, 2, 0 }; - yield return new object[] { CodeSnippets.GenericCollectionWithCustomElementMarshallingDuplicateElementIndirectionLevel, 2, 0 }; - yield return new object[] { CodeSnippets.GenericCollectionWithCustomElementMarshallingUnusedElementIndirectionLevel, 1, 0 }; + yield return new object[] { CodeSnippets.GenericCollectionWithCustomElementMarshallingDuplicateElementIndirectionDepth, 2, 0 }; + yield return new object[] { CodeSnippets.GenericCollectionWithCustomElementMarshallingUnusedElementIndirectionDepth, 1, 0 }; yield return new object[] { CodeSnippets.RecursiveCountElementNameOnReturnValue, 2, 0 }; yield return new object[] { CodeSnippets.RecursiveCountElementNameOnParameter, 2, 0 }; yield return new object[] { CodeSnippets.MutuallyRecursiveCountElementNameOnParameter, 4, 0 }; diff --git a/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/ManualTypeMarshallingAnalyzerTests.cs b/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/ManualTypeMarshallingAnalyzerTests.cs index bd3ffbbede06d..8d473b00be4d3 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/ManualTypeMarshallingAnalyzerTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/ManualTypeMarshallingAnalyzerTests.cs @@ -588,7 +588,7 @@ class S public byte c; } -[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.SpanCollection)] +[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection)] struct {|#0:Native|} { }"; @@ -610,7 +610,7 @@ class S public byte c; } -[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.SpanCollection)] +[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection)] ref struct {|#0:Native|} { public Native(S s) : this() {} @@ -638,7 +638,7 @@ class S public byte c; } -[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.SpanCollection)] +[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection)] ref struct Native { public Native(S s, int nativeElementSize) : this() {} @@ -665,7 +665,7 @@ class S public byte c; } -[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.SpanCollection, BufferSize = 1)] +[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection, BufferSize = 1)] ref struct {|#0:Native|} { public Native(S s, Span stackSpace) : this() {} @@ -693,7 +693,7 @@ class S public byte c; } -[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.SpanCollection, BufferSize = 1)] +[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection, BufferSize = 1)] ref struct {|#0:Native|} { public Native(S s, Span stackSpace, int nativeElementSize) : this() {} @@ -721,7 +721,7 @@ class S public byte c; } -[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.SpanCollection)] +[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection)] ref struct {|#0:Native|} { public Native(S s, int nativeElementSize) : this() {} @@ -748,7 +748,7 @@ class S public byte c; } -[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.SpanCollection)] +[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection)] ref struct {|#0:Native|} { public Native(S s, int nativeElementSize) : this() {} 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 6868b7f86f006..accc1719c70ba 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/NonBlittable.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/NonBlittable.cs @@ -261,7 +261,7 @@ public IntStructWrapperNative(IntStructWrapper managed) public IntStructWrapper ToManaged() => new IntStructWrapper { Value = value }; } - [CustomTypeMarshaller(typeof(List<>), CustomTypeMarshallerKind.SpanCollection, BufferSize = 0x200, RequiresStackBuffer = true)] + [CustomTypeMarshaller(typeof(List<>), CustomTypeMarshallerKind.LinearCollection, BufferSize = 0x200, RequiresStackBuffer = true)] public unsafe ref struct ListMarshaller { private List managedList; From dc5969fdefdf827eca7e45a7f8714eb187e3df84 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 4 Mar 2022 14:45:29 -0800 Subject: [PATCH 12/24] Get all tests green with the currently approved API design. --- eng/generators.targets | 1 + .../InteropServices/ArrayMarshaller.cs | 77 ++-- .../CustomTypeMarshallerKind.cs | 21 + .../GeneratedMarshallingAttribute.cs | 11 - .../System.Runtime.InteropServices.sln | 193 ++++---- .../ManualTypeMarshallingAnalyzer.cs | 127 ++++-- ...ollectionElementMarshallingCodeContext.cs} | 6 +- .../ManualTypeMarshallingHelper.cs | 125 +++--- ...ributedMarshallingModelGeneratorFactory.cs | 61 +-- .../ICustomNativeTypeMarshallingStrategy.cs | 422 +++++++++++++----- .../Marshalling/MarshallerHelpers.cs | 16 +- .../MarshallingAttributeInfo.cs | 175 +++++--- .../Microsoft.Interop.SourceGeneration.csproj | 5 +- .../TypeNames.cs | 2 + .../TypeSymbolExtensions.cs | 17 +- .../Ancillary.Interop.csproj | 1 + .../Ancillary.Interop/SpanMarshallers.cs | 224 +++------- .../CodeSnippets.cs | 81 ++-- .../ManualTypeMarshallingAnalyzerTests.cs | 147 +++--- .../TestAssets/SharedTypes/NonBlittable.cs | 109 ++--- 20 files changed, 1012 insertions(+), 809 deletions(-) create mode 100644 src/libraries/Common/src/System/Runtime/InteropServices/CustomTypeMarshallerKind.cs rename src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/{SpanCollectionElementMarshallingCodeContext.cs => LinearCollectionElementMarshallingCodeContext.cs} (92%) diff --git a/eng/generators.targets b/eng/generators.targets index 80e6e8f228e04..32f4298842cad 100644 --- a/eng/generators.targets +++ b/eng/generators.targets @@ -45,6 +45,7 @@ and @(EnabledGenerators->AnyHaveMetadataValue('Identity', 'DllImportGenerator')) and '$(IncludeDllImportGeneratorSources)' == 'true'"> + diff --git a/src/libraries/Common/src/System/Runtime/InteropServices/ArrayMarshaller.cs b/src/libraries/Common/src/System/Runtime/InteropServices/ArrayMarshaller.cs index 12c07de2e156a..a059cbcc38b6a 100644 --- a/src/libraries/Common/src/System/Runtime/InteropServices/ArrayMarshaller.cs +++ b/src/libraries/Common/src/System/Runtime/InteropServices/ArrayMarshaller.cs @@ -15,7 +15,7 @@ namespace System.Runtime.InteropServices.GeneratedMarshalling // Stack-alloc threshold set to 256 bytes to enable small arrays to be passed on the stack. // Number kept small to ensure that P/Invokes with a lot of array parameters doesn't // blow the stack since this is a new optimization in the code-generated interop. - [CustomTypeMarshaller(typeof(CustomTypeMarshallerAttribute.GenericPlaceholder[]), CustomTypeMarshallerKind.LinearCollection, BufferSize = 0x200, RequiresStackBuffer = true)] + [CustomTypeMarshaller(typeof(CustomTypeMarshallerAttribute.GenericPlaceholder[]), CustomTypeMarshallerKind.LinearCollection, BufferSize = 0x200)] #if DLLIMPORT_GENERATOR_TEST public #else @@ -62,36 +62,24 @@ public ArrayMarshaller(T[]? managed, Span stackSpace, int sizeOfNativeElem } } - public Span ManagedValues => _managedArray; + public ReadOnlySpan GetManagedValuesSource() => _managedArray; + public Span GetManagedValuesDestination(int length) => _allocatedMemory == IntPtr.Zero ? null : _managedArray = new T[length]; + public Span GetNativeValuesDestination() => NativeValueStorage; - public Span NativeValueStorage { get; private set; } + public ReadOnlySpan GetNativeValuesSource(int length) + { + return _allocatedMemory == IntPtr.Zero ? default : new Span((void*)_allocatedMemory, length * _sizeOfNativeElement); + } + private Span NativeValueStorage { get; set; } public ref byte GetPinnableReference() => ref MemoryMarshal.GetReference(NativeValueStorage); - public void SetUnmarshalledCollectionLength(int length) - { - _managedArray = new T[length]; - } - public byte* Value + public byte* ToNativeValue() => (byte*)Unsafe.AsPointer(ref GetPinnableReference()); + + public void FromNativeValue(byte* value) { - get - { - return (byte*)Unsafe.AsPointer(ref GetPinnableReference()); - } - set - { - if (value == null) - { - _managedArray = null; - NativeValueStorage = default; - } - else - { - _allocatedMemory = (IntPtr)value; - NativeValueStorage = new Span(value, (_managedArray?.Length ?? 0) * _sizeOfNativeElement); - } - } + _allocatedMemory = (IntPtr)value; } public T[]? ToManaged() => _managedArray; @@ -105,7 +93,7 @@ public void FreeNative() // Stack-alloc threshold set to 256 bytes to enable small arrays to be passed on the stack. // Number kept small to ensure that P/Invokes with a lot of array parameters doesn't // blow the stack since this is a new optimization in the code-generated interop. - [CustomTypeMarshaller(typeof(CustomTypeMarshallerAttribute.GenericPlaceholder*[]), CustomTypeMarshallerKind.LinearCollection, BufferSize = 0x200, RequiresStackBuffer = true)] + [CustomTypeMarshaller(typeof(CustomTypeMarshallerAttribute.GenericPlaceholder*[]), CustomTypeMarshallerKind.LinearCollection, BufferSize = 0x200)] #if DLLIMPORT_GENERATOR_TEST public #else @@ -152,37 +140,22 @@ public PtrArrayMarshaller(T*[]? managed, Span stackSpace, int sizeOfNative } } - public Span ManagedValues => Unsafe.As(_managedArray); - - public Span NativeValueStorage { get; private set; } + public ReadOnlySpan GetManagedValuesSource() => Unsafe.As(_managedArray); + public Span GetManagedValuesDestination(int length) => _allocatedMemory == IntPtr.Zero ? null : Unsafe.As(_managedArray = new T*[length]); + public Span GetNativeValuesDestination() => NativeValueStorage; - public ref byte GetPinnableReference() => ref MemoryMarshal.GetReference(NativeValueStorage); - - public void SetUnmarshalledCollectionLength(int length) + public ReadOnlySpan GetNativeValuesSource(int length) { - _managedArray = new T*[length]; + return _allocatedMemory == IntPtr.Zero ? default : new Span((void*)_allocatedMemory, length * _sizeOfNativeElement); } + private Span NativeValueStorage { get; set; } - public byte* Value - { - get - { - return (byte*)Unsafe.AsPointer(ref GetPinnableReference()); - } - set - { - if (value == null) - { - _managedArray = null; - NativeValueStorage = default; - } - else - { - _allocatedMemory = (IntPtr)value; - NativeValueStorage = new Span(value, (_managedArray?.Length ?? 0) * _sizeOfNativeElement); - } + public ref byte GetPinnableReference() => ref MemoryMarshal.GetReference(NativeValueStorage); + public byte* ToNativeValue() => (byte*)Unsafe.AsPointer(ref GetPinnableReference()); - } + public void FromNativeValue(byte* value) + { + _allocatedMemory = (IntPtr)value; } public T*[]? ToManaged() => _managedArray; diff --git a/src/libraries/Common/src/System/Runtime/InteropServices/CustomTypeMarshallerKind.cs b/src/libraries/Common/src/System/Runtime/InteropServices/CustomTypeMarshallerKind.cs new file mode 100644 index 0000000000000..a887330d2f6b1 --- /dev/null +++ b/src/libraries/Common/src/System/Runtime/InteropServices/CustomTypeMarshallerKind.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable enable + +// +// Types in this file are used for generated p/invokes (docs/design/features/source-generator-pinvokes.md). +// +namespace System.Runtime.InteropServices +{ +#if DLLIMPORT_GENERATOR_TEST + public +#else + internal +#endif + enum CustomTypeMarshallerKind + { + Value, + LinearCollection + } +} diff --git a/src/libraries/Common/src/System/Runtime/InteropServices/GeneratedMarshallingAttribute.cs b/src/libraries/Common/src/System/Runtime/InteropServices/GeneratedMarshallingAttribute.cs index 8608430277af5..94f929c464b48 100644 --- a/src/libraries/Common/src/System/Runtime/InteropServices/GeneratedMarshallingAttribute.cs +++ b/src/libraries/Common/src/System/Runtime/InteropServices/GeneratedMarshallingAttribute.cs @@ -90,15 +90,4 @@ public struct GenericPlaceholder { } } - -#if DLLIMPORT_GENERATOR_TEST - public -#else - internal -#endif - enum CustomTypeMarshallerKind - { - Value, - LinearCollection - } } diff --git a/src/libraries/System.Runtime.InteropServices/System.Runtime.InteropServices.sln b/src/libraries/System.Runtime.InteropServices/System.Runtime.InteropServices.sln index bfecc5e89c0c4..1cd6ca73a885a 100644 --- a/src/libraries/System.Runtime.InteropServices/System.Runtime.InteropServices.sln +++ b/src/libraries/System.Runtime.InteropServices/System.Runtime.InteropServices.sln @@ -1,4 +1,8 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.2.32222.421 +MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Private.CoreLib", "..\..\coreclr\System.Private.CoreLib\System.Private.CoreLib.csproj", "{94B59BA0-491F-4B59-ADFF-A057EC3EC835}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestUtilities", "..\Common\tests\TestUtilities\TestUtilities.csproj", "{1FF4CC8E-49C3-42A0-A6E0-2E5908455FBA}" @@ -36,18 +40,27 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{D893B9AA-57C5-49E3-97B1-12CC62D84307}" EndProject Global + GlobalSection(SharedMSBuildProjectFiles) = preSolution + ..\System.Private.CoreLib\src\System.Private.CoreLib.Shared.projitems*{94b59ba0-491f-4b59-adff-a057ec3ec835}*SharedItemsImports = 5 + EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution + Checked|Any CPU = Checked|Any CPU + Checked|x64 = Checked|x64 + Checked|x86 = Checked|x86 Debug|Any CPU = Debug|Any CPU Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU Release|x64 = Release|x64 Release|x86 = Release|x86 - Checked|Any CPU = Checked|Any CPU - Checked|x64 = Checked|x64 - Checked|x86 = Checked|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {94B59BA0-491F-4B59-ADFF-A057EC3EC835}.Checked|Any CPU.ActiveCfg = Checked|x64 + {94B59BA0-491F-4B59-ADFF-A057EC3EC835}.Checked|Any CPU.Build.0 = Checked|x64 + {94B59BA0-491F-4B59-ADFF-A057EC3EC835}.Checked|x64.ActiveCfg = Checked|x64 + {94B59BA0-491F-4B59-ADFF-A057EC3EC835}.Checked|x64.Build.0 = Checked|x64 + {94B59BA0-491F-4B59-ADFF-A057EC3EC835}.Checked|x86.ActiveCfg = Checked|x86 + {94B59BA0-491F-4B59-ADFF-A057EC3EC835}.Checked|x86.Build.0 = Checked|x86 {94B59BA0-491F-4B59-ADFF-A057EC3EC835}.Debug|Any CPU.ActiveCfg = Debug|x64 {94B59BA0-491F-4B59-ADFF-A057EC3EC835}.Debug|Any CPU.Build.0 = Debug|x64 {94B59BA0-491F-4B59-ADFF-A057EC3EC835}.Debug|x64.ActiveCfg = Debug|x64 @@ -60,12 +73,12 @@ Global {94B59BA0-491F-4B59-ADFF-A057EC3EC835}.Release|x64.Build.0 = Release|x64 {94B59BA0-491F-4B59-ADFF-A057EC3EC835}.Release|x86.ActiveCfg = Release|x86 {94B59BA0-491F-4B59-ADFF-A057EC3EC835}.Release|x86.Build.0 = Release|x86 - {94B59BA0-491F-4B59-ADFF-A057EC3EC835}.Checked|Any CPU.ActiveCfg = Checked|x64 - {94B59BA0-491F-4B59-ADFF-A057EC3EC835}.Checked|Any CPU.Build.0 = Checked|x64 - {94B59BA0-491F-4B59-ADFF-A057EC3EC835}.Checked|x64.ActiveCfg = Checked|x64 - {94B59BA0-491F-4B59-ADFF-A057EC3EC835}.Checked|x64.Build.0 = Checked|x64 - {94B59BA0-491F-4B59-ADFF-A057EC3EC835}.Checked|x86.ActiveCfg = Checked|x86 - {94B59BA0-491F-4B59-ADFF-A057EC3EC835}.Checked|x86.Build.0 = Checked|x86 + {1FF4CC8E-49C3-42A0-A6E0-2E5908455FBA}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {1FF4CC8E-49C3-42A0-A6E0-2E5908455FBA}.Checked|Any CPU.Build.0 = Debug|Any CPU + {1FF4CC8E-49C3-42A0-A6E0-2E5908455FBA}.Checked|x64.ActiveCfg = Debug|Any CPU + {1FF4CC8E-49C3-42A0-A6E0-2E5908455FBA}.Checked|x64.Build.0 = Debug|Any CPU + {1FF4CC8E-49C3-42A0-A6E0-2E5908455FBA}.Checked|x86.ActiveCfg = Debug|Any CPU + {1FF4CC8E-49C3-42A0-A6E0-2E5908455FBA}.Checked|x86.Build.0 = Debug|Any CPU {1FF4CC8E-49C3-42A0-A6E0-2E5908455FBA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1FF4CC8E-49C3-42A0-A6E0-2E5908455FBA}.Debug|Any CPU.Build.0 = Debug|Any CPU {1FF4CC8E-49C3-42A0-A6E0-2E5908455FBA}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -78,12 +91,12 @@ Global {1FF4CC8E-49C3-42A0-A6E0-2E5908455FBA}.Release|x64.Build.0 = Release|Any CPU {1FF4CC8E-49C3-42A0-A6E0-2E5908455FBA}.Release|x86.ActiveCfg = Release|Any CPU {1FF4CC8E-49C3-42A0-A6E0-2E5908455FBA}.Release|x86.Build.0 = Release|Any CPU - {1FF4CC8E-49C3-42A0-A6E0-2E5908455FBA}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {1FF4CC8E-49C3-42A0-A6E0-2E5908455FBA}.Checked|Any CPU.Build.0 = Debug|Any CPU - {1FF4CC8E-49C3-42A0-A6E0-2E5908455FBA}.Checked|x64.ActiveCfg = Debug|Any CPU - {1FF4CC8E-49C3-42A0-A6E0-2E5908455FBA}.Checked|x64.Build.0 = Debug|Any CPU - {1FF4CC8E-49C3-42A0-A6E0-2E5908455FBA}.Checked|x86.ActiveCfg = Debug|Any CPU - {1FF4CC8E-49C3-42A0-A6E0-2E5908455FBA}.Checked|x86.Build.0 = Debug|Any CPU + {1B248B4C-7584-4C04-850A-A50EB592052C}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {1B248B4C-7584-4C04-850A-A50EB592052C}.Checked|Any CPU.Build.0 = Debug|Any CPU + {1B248B4C-7584-4C04-850A-A50EB592052C}.Checked|x64.ActiveCfg = Debug|Any CPU + {1B248B4C-7584-4C04-850A-A50EB592052C}.Checked|x64.Build.0 = Debug|Any CPU + {1B248B4C-7584-4C04-850A-A50EB592052C}.Checked|x86.ActiveCfg = Debug|Any CPU + {1B248B4C-7584-4C04-850A-A50EB592052C}.Checked|x86.Build.0 = Debug|Any CPU {1B248B4C-7584-4C04-850A-A50EB592052C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1B248B4C-7584-4C04-850A-A50EB592052C}.Debug|Any CPU.Build.0 = Debug|Any CPU {1B248B4C-7584-4C04-850A-A50EB592052C}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -96,12 +109,12 @@ Global {1B248B4C-7584-4C04-850A-A50EB592052C}.Release|x64.Build.0 = Release|Any CPU {1B248B4C-7584-4C04-850A-A50EB592052C}.Release|x86.ActiveCfg = Release|Any CPU {1B248B4C-7584-4C04-850A-A50EB592052C}.Release|x86.Build.0 = Release|Any CPU - {1B248B4C-7584-4C04-850A-A50EB592052C}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {1B248B4C-7584-4C04-850A-A50EB592052C}.Checked|Any CPU.Build.0 = Debug|Any CPU - {1B248B4C-7584-4C04-850A-A50EB592052C}.Checked|x64.ActiveCfg = Debug|Any CPU - {1B248B4C-7584-4C04-850A-A50EB592052C}.Checked|x64.Build.0 = Debug|Any CPU - {1B248B4C-7584-4C04-850A-A50EB592052C}.Checked|x86.ActiveCfg = Debug|Any CPU - {1B248B4C-7584-4C04-850A-A50EB592052C}.Checked|x86.Build.0 = Debug|Any CPU + {07F19F91-D438-428D-99F0-61DAD87E78BA}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {07F19F91-D438-428D-99F0-61DAD87E78BA}.Checked|Any CPU.Build.0 = Debug|Any CPU + {07F19F91-D438-428D-99F0-61DAD87E78BA}.Checked|x64.ActiveCfg = Debug|Any CPU + {07F19F91-D438-428D-99F0-61DAD87E78BA}.Checked|x64.Build.0 = Debug|Any CPU + {07F19F91-D438-428D-99F0-61DAD87E78BA}.Checked|x86.ActiveCfg = Debug|Any CPU + {07F19F91-D438-428D-99F0-61DAD87E78BA}.Checked|x86.Build.0 = Debug|Any CPU {07F19F91-D438-428D-99F0-61DAD87E78BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {07F19F91-D438-428D-99F0-61DAD87E78BA}.Debug|Any CPU.Build.0 = Debug|Any CPU {07F19F91-D438-428D-99F0-61DAD87E78BA}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -114,12 +127,12 @@ Global {07F19F91-D438-428D-99F0-61DAD87E78BA}.Release|x64.Build.0 = Release|Any CPU {07F19F91-D438-428D-99F0-61DAD87E78BA}.Release|x86.ActiveCfg = Release|Any CPU {07F19F91-D438-428D-99F0-61DAD87E78BA}.Release|x86.Build.0 = Release|Any CPU - {07F19F91-D438-428D-99F0-61DAD87E78BA}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {07F19F91-D438-428D-99F0-61DAD87E78BA}.Checked|Any CPU.Build.0 = Debug|Any CPU - {07F19F91-D438-428D-99F0-61DAD87E78BA}.Checked|x64.ActiveCfg = Debug|Any CPU - {07F19F91-D438-428D-99F0-61DAD87E78BA}.Checked|x64.Build.0 = Debug|Any CPU - {07F19F91-D438-428D-99F0-61DAD87E78BA}.Checked|x86.ActiveCfg = Debug|Any CPU - {07F19F91-D438-428D-99F0-61DAD87E78BA}.Checked|x86.Build.0 = Debug|Any CPU + {768B77B0-EA45-469D-B39E-545EB72F5A43}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {768B77B0-EA45-469D-B39E-545EB72F5A43}.Checked|Any CPU.Build.0 = Debug|Any CPU + {768B77B0-EA45-469D-B39E-545EB72F5A43}.Checked|x64.ActiveCfg = Debug|Any CPU + {768B77B0-EA45-469D-B39E-545EB72F5A43}.Checked|x64.Build.0 = Debug|Any CPU + {768B77B0-EA45-469D-B39E-545EB72F5A43}.Checked|x86.ActiveCfg = Debug|Any CPU + {768B77B0-EA45-469D-B39E-545EB72F5A43}.Checked|x86.Build.0 = Debug|Any CPU {768B77B0-EA45-469D-B39E-545EB72F5A43}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {768B77B0-EA45-469D-B39E-545EB72F5A43}.Debug|Any CPU.Build.0 = Debug|Any CPU {768B77B0-EA45-469D-B39E-545EB72F5A43}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -132,12 +145,12 @@ Global {768B77B0-EA45-469D-B39E-545EB72F5A43}.Release|x64.Build.0 = Release|Any CPU {768B77B0-EA45-469D-B39E-545EB72F5A43}.Release|x86.ActiveCfg = Release|Any CPU {768B77B0-EA45-469D-B39E-545EB72F5A43}.Release|x86.Build.0 = Release|Any CPU - {768B77B0-EA45-469D-B39E-545EB72F5A43}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {768B77B0-EA45-469D-B39E-545EB72F5A43}.Checked|Any CPU.Build.0 = Debug|Any CPU - {768B77B0-EA45-469D-B39E-545EB72F5A43}.Checked|x64.ActiveCfg = Debug|Any CPU - {768B77B0-EA45-469D-B39E-545EB72F5A43}.Checked|x64.Build.0 = Debug|Any CPU - {768B77B0-EA45-469D-B39E-545EB72F5A43}.Checked|x86.ActiveCfg = Debug|Any CPU - {768B77B0-EA45-469D-B39E-545EB72F5A43}.Checked|x86.Build.0 = Debug|Any CPU + {8671F164-F78C-44FA-93B7-A310F67890FE}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {8671F164-F78C-44FA-93B7-A310F67890FE}.Checked|Any CPU.Build.0 = Debug|Any CPU + {8671F164-F78C-44FA-93B7-A310F67890FE}.Checked|x64.ActiveCfg = Debug|Any CPU + {8671F164-F78C-44FA-93B7-A310F67890FE}.Checked|x64.Build.0 = Debug|Any CPU + {8671F164-F78C-44FA-93B7-A310F67890FE}.Checked|x86.ActiveCfg = Debug|Any CPU + {8671F164-F78C-44FA-93B7-A310F67890FE}.Checked|x86.Build.0 = Debug|Any CPU {8671F164-F78C-44FA-93B7-A310F67890FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8671F164-F78C-44FA-93B7-A310F67890FE}.Debug|Any CPU.Build.0 = Debug|Any CPU {8671F164-F78C-44FA-93B7-A310F67890FE}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -150,12 +163,12 @@ Global {8671F164-F78C-44FA-93B7-A310F67890FE}.Release|x64.Build.0 = Release|Any CPU {8671F164-F78C-44FA-93B7-A310F67890FE}.Release|x86.ActiveCfg = Release|Any CPU {8671F164-F78C-44FA-93B7-A310F67890FE}.Release|x86.Build.0 = Release|Any CPU - {8671F164-F78C-44FA-93B7-A310F67890FE}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {8671F164-F78C-44FA-93B7-A310F67890FE}.Checked|Any CPU.Build.0 = Debug|Any CPU - {8671F164-F78C-44FA-93B7-A310F67890FE}.Checked|x64.ActiveCfg = Debug|Any CPU - {8671F164-F78C-44FA-93B7-A310F67890FE}.Checked|x64.Build.0 = Debug|Any CPU - {8671F164-F78C-44FA-93B7-A310F67890FE}.Checked|x86.ActiveCfg = Debug|Any CPU - {8671F164-F78C-44FA-93B7-A310F67890FE}.Checked|x86.Build.0 = Debug|Any CPU + {4FC33B9B-1BCF-4D16-B886-DCA8F2B823C1}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {4FC33B9B-1BCF-4D16-B886-DCA8F2B823C1}.Checked|Any CPU.Build.0 = Debug|Any CPU + {4FC33B9B-1BCF-4D16-B886-DCA8F2B823C1}.Checked|x64.ActiveCfg = Debug|Any CPU + {4FC33B9B-1BCF-4D16-B886-DCA8F2B823C1}.Checked|x64.Build.0 = Debug|Any CPU + {4FC33B9B-1BCF-4D16-B886-DCA8F2B823C1}.Checked|x86.ActiveCfg = Debug|Any CPU + {4FC33B9B-1BCF-4D16-B886-DCA8F2B823C1}.Checked|x86.Build.0 = Debug|Any CPU {4FC33B9B-1BCF-4D16-B886-DCA8F2B823C1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4FC33B9B-1BCF-4D16-B886-DCA8F2B823C1}.Debug|Any CPU.Build.0 = Debug|Any CPU {4FC33B9B-1BCF-4D16-B886-DCA8F2B823C1}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -168,12 +181,12 @@ Global {4FC33B9B-1BCF-4D16-B886-DCA8F2B823C1}.Release|x64.Build.0 = Release|Any CPU {4FC33B9B-1BCF-4D16-B886-DCA8F2B823C1}.Release|x86.ActiveCfg = Release|Any CPU {4FC33B9B-1BCF-4D16-B886-DCA8F2B823C1}.Release|x86.Build.0 = Release|Any CPU - {4FC33B9B-1BCF-4D16-B886-DCA8F2B823C1}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {4FC33B9B-1BCF-4D16-B886-DCA8F2B823C1}.Checked|Any CPU.Build.0 = Debug|Any CPU - {4FC33B9B-1BCF-4D16-B886-DCA8F2B823C1}.Checked|x64.ActiveCfg = Debug|Any CPU - {4FC33B9B-1BCF-4D16-B886-DCA8F2B823C1}.Checked|x64.Build.0 = Debug|Any CPU - {4FC33B9B-1BCF-4D16-B886-DCA8F2B823C1}.Checked|x86.ActiveCfg = Debug|Any CPU - {4FC33B9B-1BCF-4D16-B886-DCA8F2B823C1}.Checked|x86.Build.0 = Debug|Any CPU + {79F7BE0E-01AA-4AFB-B047-CF7C0B38F81E}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {79F7BE0E-01AA-4AFB-B047-CF7C0B38F81E}.Checked|Any CPU.Build.0 = Debug|Any CPU + {79F7BE0E-01AA-4AFB-B047-CF7C0B38F81E}.Checked|x64.ActiveCfg = Debug|Any CPU + {79F7BE0E-01AA-4AFB-B047-CF7C0B38F81E}.Checked|x64.Build.0 = Debug|Any CPU + {79F7BE0E-01AA-4AFB-B047-CF7C0B38F81E}.Checked|x86.ActiveCfg = Debug|Any CPU + {79F7BE0E-01AA-4AFB-B047-CF7C0B38F81E}.Checked|x86.Build.0 = Debug|Any CPU {79F7BE0E-01AA-4AFB-B047-CF7C0B38F81E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {79F7BE0E-01AA-4AFB-B047-CF7C0B38F81E}.Debug|Any CPU.Build.0 = Debug|Any CPU {79F7BE0E-01AA-4AFB-B047-CF7C0B38F81E}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -186,12 +199,12 @@ Global {79F7BE0E-01AA-4AFB-B047-CF7C0B38F81E}.Release|x64.Build.0 = Release|Any CPU {79F7BE0E-01AA-4AFB-B047-CF7C0B38F81E}.Release|x86.ActiveCfg = Release|Any CPU {79F7BE0E-01AA-4AFB-B047-CF7C0B38F81E}.Release|x86.Build.0 = Release|Any CPU - {79F7BE0E-01AA-4AFB-B047-CF7C0B38F81E}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {79F7BE0E-01AA-4AFB-B047-CF7C0B38F81E}.Checked|Any CPU.Build.0 = Debug|Any CPU - {79F7BE0E-01AA-4AFB-B047-CF7C0B38F81E}.Checked|x64.ActiveCfg = Debug|Any CPU - {79F7BE0E-01AA-4AFB-B047-CF7C0B38F81E}.Checked|x64.Build.0 = Debug|Any CPU - {79F7BE0E-01AA-4AFB-B047-CF7C0B38F81E}.Checked|x86.ActiveCfg = Debug|Any CPU - {79F7BE0E-01AA-4AFB-B047-CF7C0B38F81E}.Checked|x86.Build.0 = Debug|Any CPU + {57A1A6FD-9231-4DFB-8619-F0EDEDA208E3}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {57A1A6FD-9231-4DFB-8619-F0EDEDA208E3}.Checked|Any CPU.Build.0 = Debug|Any CPU + {57A1A6FD-9231-4DFB-8619-F0EDEDA208E3}.Checked|x64.ActiveCfg = Debug|Any CPU + {57A1A6FD-9231-4DFB-8619-F0EDEDA208E3}.Checked|x64.Build.0 = Debug|Any CPU + {57A1A6FD-9231-4DFB-8619-F0EDEDA208E3}.Checked|x86.ActiveCfg = Debug|Any CPU + {57A1A6FD-9231-4DFB-8619-F0EDEDA208E3}.Checked|x86.Build.0 = Debug|Any CPU {57A1A6FD-9231-4DFB-8619-F0EDEDA208E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {57A1A6FD-9231-4DFB-8619-F0EDEDA208E3}.Debug|Any CPU.Build.0 = Debug|Any CPU {57A1A6FD-9231-4DFB-8619-F0EDEDA208E3}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -204,12 +217,12 @@ Global {57A1A6FD-9231-4DFB-8619-F0EDEDA208E3}.Release|x64.Build.0 = Release|Any CPU {57A1A6FD-9231-4DFB-8619-F0EDEDA208E3}.Release|x86.ActiveCfg = Release|Any CPU {57A1A6FD-9231-4DFB-8619-F0EDEDA208E3}.Release|x86.Build.0 = Release|Any CPU - {57A1A6FD-9231-4DFB-8619-F0EDEDA208E3}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {57A1A6FD-9231-4DFB-8619-F0EDEDA208E3}.Checked|Any CPU.Build.0 = Debug|Any CPU - {57A1A6FD-9231-4DFB-8619-F0EDEDA208E3}.Checked|x64.ActiveCfg = Debug|Any CPU - {57A1A6FD-9231-4DFB-8619-F0EDEDA208E3}.Checked|x64.Build.0 = Debug|Any CPU - {57A1A6FD-9231-4DFB-8619-F0EDEDA208E3}.Checked|x86.ActiveCfg = Debug|Any CPU - {57A1A6FD-9231-4DFB-8619-F0EDEDA208E3}.Checked|x86.Build.0 = Debug|Any CPU + {4B516949-4AD4-44D6-AF86-C2E6058608D5}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {4B516949-4AD4-44D6-AF86-C2E6058608D5}.Checked|Any CPU.Build.0 = Debug|Any CPU + {4B516949-4AD4-44D6-AF86-C2E6058608D5}.Checked|x64.ActiveCfg = Debug|Any CPU + {4B516949-4AD4-44D6-AF86-C2E6058608D5}.Checked|x64.Build.0 = Debug|Any CPU + {4B516949-4AD4-44D6-AF86-C2E6058608D5}.Checked|x86.ActiveCfg = Debug|Any CPU + {4B516949-4AD4-44D6-AF86-C2E6058608D5}.Checked|x86.Build.0 = Debug|Any CPU {4B516949-4AD4-44D6-AF86-C2E6058608D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4B516949-4AD4-44D6-AF86-C2E6058608D5}.Debug|Any CPU.Build.0 = Debug|Any CPU {4B516949-4AD4-44D6-AF86-C2E6058608D5}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -222,12 +235,12 @@ Global {4B516949-4AD4-44D6-AF86-C2E6058608D5}.Release|x64.Build.0 = Release|Any CPU {4B516949-4AD4-44D6-AF86-C2E6058608D5}.Release|x86.ActiveCfg = Release|Any CPU {4B516949-4AD4-44D6-AF86-C2E6058608D5}.Release|x86.Build.0 = Release|Any CPU - {4B516949-4AD4-44D6-AF86-C2E6058608D5}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {4B516949-4AD4-44D6-AF86-C2E6058608D5}.Checked|Any CPU.Build.0 = Debug|Any CPU - {4B516949-4AD4-44D6-AF86-C2E6058608D5}.Checked|x64.ActiveCfg = Debug|Any CPU - {4B516949-4AD4-44D6-AF86-C2E6058608D5}.Checked|x64.Build.0 = Debug|Any CPU - {4B516949-4AD4-44D6-AF86-C2E6058608D5}.Checked|x86.ActiveCfg = Debug|Any CPU - {4B516949-4AD4-44D6-AF86-C2E6058608D5}.Checked|x86.Build.0 = Debug|Any CPU + {25D66424-2EAF-464D-8460-10C04EDEF3C3}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {25D66424-2EAF-464D-8460-10C04EDEF3C3}.Checked|Any CPU.Build.0 = Debug|Any CPU + {25D66424-2EAF-464D-8460-10C04EDEF3C3}.Checked|x64.ActiveCfg = Debug|Any CPU + {25D66424-2EAF-464D-8460-10C04EDEF3C3}.Checked|x64.Build.0 = Debug|Any CPU + {25D66424-2EAF-464D-8460-10C04EDEF3C3}.Checked|x86.ActiveCfg = Debug|Any CPU + {25D66424-2EAF-464D-8460-10C04EDEF3C3}.Checked|x86.Build.0 = Debug|Any CPU {25D66424-2EAF-464D-8460-10C04EDEF3C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {25D66424-2EAF-464D-8460-10C04EDEF3C3}.Debug|Any CPU.Build.0 = Debug|Any CPU {25D66424-2EAF-464D-8460-10C04EDEF3C3}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -240,12 +253,12 @@ Global {25D66424-2EAF-464D-8460-10C04EDEF3C3}.Release|x64.Build.0 = Release|Any CPU {25D66424-2EAF-464D-8460-10C04EDEF3C3}.Release|x86.ActiveCfg = Release|Any CPU {25D66424-2EAF-464D-8460-10C04EDEF3C3}.Release|x86.Build.0 = Release|Any CPU - {25D66424-2EAF-464D-8460-10C04EDEF3C3}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {25D66424-2EAF-464D-8460-10C04EDEF3C3}.Checked|Any CPU.Build.0 = Debug|Any CPU - {25D66424-2EAF-464D-8460-10C04EDEF3C3}.Checked|x64.ActiveCfg = Debug|Any CPU - {25D66424-2EAF-464D-8460-10C04EDEF3C3}.Checked|x64.Build.0 = Debug|Any CPU - {25D66424-2EAF-464D-8460-10C04EDEF3C3}.Checked|x86.ActiveCfg = Debug|Any CPU - {25D66424-2EAF-464D-8460-10C04EDEF3C3}.Checked|x86.Build.0 = Debug|Any CPU + {049B7FD4-ACEF-4BCD-A7A7-75C9BBEC4EBF}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {049B7FD4-ACEF-4BCD-A7A7-75C9BBEC4EBF}.Checked|Any CPU.Build.0 = Debug|Any CPU + {049B7FD4-ACEF-4BCD-A7A7-75C9BBEC4EBF}.Checked|x64.ActiveCfg = Debug|Any CPU + {049B7FD4-ACEF-4BCD-A7A7-75C9BBEC4EBF}.Checked|x64.Build.0 = Debug|Any CPU + {049B7FD4-ACEF-4BCD-A7A7-75C9BBEC4EBF}.Checked|x86.ActiveCfg = Debug|Any CPU + {049B7FD4-ACEF-4BCD-A7A7-75C9BBEC4EBF}.Checked|x86.Build.0 = Debug|Any CPU {049B7FD4-ACEF-4BCD-A7A7-75C9BBEC4EBF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {049B7FD4-ACEF-4BCD-A7A7-75C9BBEC4EBF}.Debug|Any CPU.Build.0 = Debug|Any CPU {049B7FD4-ACEF-4BCD-A7A7-75C9BBEC4EBF}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -258,12 +271,12 @@ Global {049B7FD4-ACEF-4BCD-A7A7-75C9BBEC4EBF}.Release|x64.Build.0 = Release|Any CPU {049B7FD4-ACEF-4BCD-A7A7-75C9BBEC4EBF}.Release|x86.ActiveCfg = Release|Any CPU {049B7FD4-ACEF-4BCD-A7A7-75C9BBEC4EBF}.Release|x86.Build.0 = Release|Any CPU - {049B7FD4-ACEF-4BCD-A7A7-75C9BBEC4EBF}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {049B7FD4-ACEF-4BCD-A7A7-75C9BBEC4EBF}.Checked|Any CPU.Build.0 = Debug|Any CPU - {049B7FD4-ACEF-4BCD-A7A7-75C9BBEC4EBF}.Checked|x64.ActiveCfg = Debug|Any CPU - {049B7FD4-ACEF-4BCD-A7A7-75C9BBEC4EBF}.Checked|x64.Build.0 = Debug|Any CPU - {049B7FD4-ACEF-4BCD-A7A7-75C9BBEC4EBF}.Checked|x86.ActiveCfg = Debug|Any CPU - {049B7FD4-ACEF-4BCD-A7A7-75C9BBEC4EBF}.Checked|x86.Build.0 = Debug|Any CPU + {866D295E-424A-4747-9417-CD7746936138}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {866D295E-424A-4747-9417-CD7746936138}.Checked|Any CPU.Build.0 = Debug|Any CPU + {866D295E-424A-4747-9417-CD7746936138}.Checked|x64.ActiveCfg = Debug|Any CPU + {866D295E-424A-4747-9417-CD7746936138}.Checked|x64.Build.0 = Debug|Any CPU + {866D295E-424A-4747-9417-CD7746936138}.Checked|x86.ActiveCfg = Debug|Any CPU + {866D295E-424A-4747-9417-CD7746936138}.Checked|x86.Build.0 = Debug|Any CPU {866D295E-424A-4747-9417-CD7746936138}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {866D295E-424A-4747-9417-CD7746936138}.Debug|Any CPU.Build.0 = Debug|Any CPU {866D295E-424A-4747-9417-CD7746936138}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -276,12 +289,12 @@ Global {866D295E-424A-4747-9417-CD7746936138}.Release|x64.Build.0 = Release|Any CPU {866D295E-424A-4747-9417-CD7746936138}.Release|x86.ActiveCfg = Release|Any CPU {866D295E-424A-4747-9417-CD7746936138}.Release|x86.Build.0 = Release|Any CPU - {866D295E-424A-4747-9417-CD7746936138}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {866D295E-424A-4747-9417-CD7746936138}.Checked|Any CPU.Build.0 = Debug|Any CPU - {866D295E-424A-4747-9417-CD7746936138}.Checked|x64.ActiveCfg = Debug|Any CPU - {866D295E-424A-4747-9417-CD7746936138}.Checked|x64.Build.0 = Debug|Any CPU - {866D295E-424A-4747-9417-CD7746936138}.Checked|x86.ActiveCfg = Debug|Any CPU - {866D295E-424A-4747-9417-CD7746936138}.Checked|x86.Build.0 = Debug|Any CPU + {D3A329E3-0FEB-4136-9CB6-B38319B0FFA5}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {D3A329E3-0FEB-4136-9CB6-B38319B0FFA5}.Checked|Any CPU.Build.0 = Debug|Any CPU + {D3A329E3-0FEB-4136-9CB6-B38319B0FFA5}.Checked|x64.ActiveCfg = Debug|Any CPU + {D3A329E3-0FEB-4136-9CB6-B38319B0FFA5}.Checked|x64.Build.0 = Debug|Any CPU + {D3A329E3-0FEB-4136-9CB6-B38319B0FFA5}.Checked|x86.ActiveCfg = Debug|Any CPU + {D3A329E3-0FEB-4136-9CB6-B38319B0FFA5}.Checked|x86.Build.0 = Debug|Any CPU {D3A329E3-0FEB-4136-9CB6-B38319B0FFA5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D3A329E3-0FEB-4136-9CB6-B38319B0FFA5}.Debug|Any CPU.Build.0 = Debug|Any CPU {D3A329E3-0FEB-4136-9CB6-B38319B0FFA5}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -294,20 +307,18 @@ Global {D3A329E3-0FEB-4136-9CB6-B38319B0FFA5}.Release|x64.Build.0 = Release|Any CPU {D3A329E3-0FEB-4136-9CB6-B38319B0FFA5}.Release|x86.ActiveCfg = Release|Any CPU {D3A329E3-0FEB-4136-9CB6-B38319B0FFA5}.Release|x86.Build.0 = Release|Any CPU - {D3A329E3-0FEB-4136-9CB6-B38319B0FFA5}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {D3A329E3-0FEB-4136-9CB6-B38319B0FFA5}.Checked|Any CPU.Build.0 = Debug|Any CPU - {D3A329E3-0FEB-4136-9CB6-B38319B0FFA5}.Checked|x64.ActiveCfg = Debug|Any CPU - {D3A329E3-0FEB-4136-9CB6-B38319B0FFA5}.Checked|x64.Build.0 = Debug|Any CPU - {D3A329E3-0FEB-4136-9CB6-B38319B0FFA5}.Checked|x86.ActiveCfg = Debug|Any CPU - {D3A329E3-0FEB-4136-9CB6-B38319B0FFA5}.Checked|x86.Build.0 = Debug|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {94B59BA0-491F-4B59-ADFF-A057EC3EC835} = {B1678CCD-95C8-4419-B9F9-14A03061BE4B} - {4FC33B9B-1BCF-4D16-B886-DCA8F2B823C1} = {B1678CCD-95C8-4419-B9F9-14A03061BE4B} {1FF4CC8E-49C3-42A0-A6E0-2E5908455FBA} = {FB99AC59-1744-4F12-A4B0-0D54FCA048BF} + {1B248B4C-7584-4C04-850A-A50EB592052C} = {E1AEBD5D-AE4E-4F61-B9ED-AEF950B0CC33} + {07F19F91-D438-428D-99F0-61DAD87E78BA} = {E1AEBD5D-AE4E-4F61-B9ED-AEF950B0CC33} + {768B77B0-EA45-469D-B39E-545EB72F5A43} = {E1AEBD5D-AE4E-4F61-B9ED-AEF950B0CC33} + {8671F164-F78C-44FA-93B7-A310F67890FE} = {D893B9AA-57C5-49E3-97B1-12CC62D84307} + {4FC33B9B-1BCF-4D16-B886-DCA8F2B823C1} = {B1678CCD-95C8-4419-B9F9-14A03061BE4B} {79F7BE0E-01AA-4AFB-B047-CF7C0B38F81E} = {FB99AC59-1744-4F12-A4B0-0D54FCA048BF} {57A1A6FD-9231-4DFB-8619-F0EDEDA208E3} = {FB99AC59-1744-4F12-A4B0-0D54FCA048BF} {4B516949-4AD4-44D6-AF86-C2E6058608D5} = {FB99AC59-1744-4F12-A4B0-0D54FCA048BF} @@ -315,10 +326,6 @@ Global {049B7FD4-ACEF-4BCD-A7A7-75C9BBEC4EBF} = {FB99AC59-1744-4F12-A4B0-0D54FCA048BF} {866D295E-424A-4747-9417-CD7746936138} = {FB99AC59-1744-4F12-A4B0-0D54FCA048BF} {D3A329E3-0FEB-4136-9CB6-B38319B0FFA5} = {FB99AC59-1744-4F12-A4B0-0D54FCA048BF} - {1B248B4C-7584-4C04-850A-A50EB592052C} = {E1AEBD5D-AE4E-4F61-B9ED-AEF950B0CC33} - {07F19F91-D438-428D-99F0-61DAD87E78BA} = {E1AEBD5D-AE4E-4F61-B9ED-AEF950B0CC33} - {768B77B0-EA45-469D-B39E-545EB72F5A43} = {E1AEBD5D-AE4E-4F61-B9ED-AEF950B0CC33} - {8671F164-F78C-44FA-93B7-A310F67890FE} = {D893B9AA-57C5-49E3-97B1-12CC62D84307} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {D4031401-FEB5-4CCF-91C1-38F5646B2BFD} diff --git a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Analyzers/ManualTypeMarshallingAnalyzer.cs b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Analyzers/ManualTypeMarshallingAnalyzer.cs index 61d0f30a59c2c..d54ee1bb466ce 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Analyzers/ManualTypeMarshallingAnalyzer.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/DllImportGenerator/Analyzers/ManualTypeMarshallingAnalyzer.cs @@ -4,7 +4,7 @@ using System; using System.Collections.Immutable; using System.Linq; - +using System.Runtime.InteropServices; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; @@ -207,11 +207,14 @@ public override void Initialize(AnalysisContext context) private void PrepareForAnalysis(CompilationStartAnalysisContext context) { - INamedTypeSymbol? spanOfByte = context.Compilation.GetTypeByMetadataName(TypeNames.System_Span_Metadata)!.Construct(context.Compilation.GetSpecialType(SpecialType.System_Byte)); + INamedTypeSymbol? spanOfT = context.Compilation.GetTypeByMetadataName(TypeNames.System_Span_Metadata); + INamedTypeSymbol? spanOfByte = spanOfT?.Construct(context.Compilation.GetSpecialType(SpecialType.System_Byte)); + INamedTypeSymbol? readOnlySpanOfT = context.Compilation.GetTypeByMetadataName(TypeNames.System_ReadOnlySpan_Metadata); + INamedTypeSymbol? readOnlySpanOfByte = spanOfT?.Construct(context.Compilation.GetSpecialType(SpecialType.System_Byte)); - if (spanOfByte is not null) + if (spanOfT is not null && readOnlySpanOfT is not null) { - var perCompilationAnalyzer = new PerCompilationAnalyzer(spanOfByte); + var perCompilationAnalyzer = new PerCompilationAnalyzer(spanOfT, spanOfByte, readOnlySpanOfT, readOnlySpanOfByte); // Analyze NativeMarshalling/MarshalUsing for correctness context.RegisterSymbolAction(perCompilationAnalyzer.AnalyzeTypeDefinition, SymbolKind.NamedType); @@ -225,11 +228,17 @@ private void PrepareForAnalysis(CompilationStartAnalysisContext context) private class PerCompilationAnalyzer { + private readonly INamedTypeSymbol _spanOfT; + private readonly INamedTypeSymbol _readOnlySpanOfT; private readonly INamedTypeSymbol _spanOfByte; + private readonly INamedTypeSymbol _readOnlySpanOfByte; - public PerCompilationAnalyzer(INamedTypeSymbol spanOfByte) + public PerCompilationAnalyzer(INamedTypeSymbol spanOfT, INamedTypeSymbol? spanOfByte, INamedTypeSymbol readOnlySpanOfT, INamedTypeSymbol? readOnlySpanOfByte) { + _spanOfT = spanOfT; _spanOfByte = spanOfByte; + _readOnlySpanOfT = readOnlySpanOfT; + _readOnlySpanOfByte = readOnlySpanOfByte; } public void AnalyzeTypeDefinition(SymbolAnalysisContext context) @@ -410,22 +419,43 @@ public void AnalyzeMarshallerType(SymbolAnalysisContext context) } } + bool hasToManaged = ManualTypeMarshallingHelper.HasToManagedMethod(marshallerType, type); + if (marshallerData.Value.Kind == CustomTypeMarshallerKind.LinearCollection) { requiredShapeRule = CollectionNativeTypeMustHaveRequiredShapeRule; - if (!ManualTypeMarshallingHelper.TryGetManagedValuesProperty(marshallerType, out _) - || !ManualTypeMarshallingHelper.HasNativeValueStorageProperty(marshallerType, _spanOfByte)) + IMethodSymbol? getManagedValuesSourceMethod = ManualTypeMarshallingHelper.FindGetManagedValuesSourceMethod(marshallerType, _readOnlySpanOfT); + IMethodSymbol? getManagedValuesDestinationMethod = ManualTypeMarshallingHelper.FindGetManagedValuesDestinationMethod(marshallerType, _spanOfT); + IMethodSymbol? getNativeValuesSourceMethod = ManualTypeMarshallingHelper.FindGetNativeValuesSourceMethod(marshallerType, _readOnlySpanOfByte); + IMethodSymbol? getNativeValuesDestinationMethod = ManualTypeMarshallingHelper.FindGetNativeValuesDestinationMethod(marshallerType, _spanOfByte); + if ((hasConstructor || hasCallerAllocSpanConstructor) && (getManagedValuesSourceMethod is null || getNativeValuesDestinationMethod is null)) + { + context.ReportDiagnostic( + marshallerType.CreateDiagnostic( + requiredShapeRule, + marshallerType.ToDisplayString(), + type.ToDisplayString())); + } + + if (hasToManaged && (getNativeValuesSourceMethod is null || getManagedValuesDestinationMethod is null)) { context.ReportDiagnostic( marshallerType.CreateDiagnostic( requiredShapeRule, marshallerType.ToDisplayString(), type.ToDisplayString())); - return; + } + + if (getManagedValuesSourceMethod is not null + && getManagedValuesDestinationMethod is not null + && !SymbolEqualityComparer.Default.Equals( + ((INamedTypeSymbol)getManagedValuesSourceMethod.ReturnType).TypeArguments[0], + ((INamedTypeSymbol)getManagedValuesDestinationMethod.ReturnType).TypeArguments[0])) + { + // TODO: Diagnostic for mismatched element collection type } } - bool hasToManaged = ManualTypeMarshallingHelper.HasToManagedMethod(marshallerType, type); // Validate that the native type has at least one marshalling method (either managed to native or native to managed) if (!hasConstructor && !hasCallerAllocSpanConstructor && !hasToManaged) @@ -446,39 +476,25 @@ public void AnalyzeMarshallerType(SymbolAnalysisContext context) marshallerType.ToDisplayString())); } - IPropertySymbol? valueProperty = ManualTypeMarshallingHelper.FindValueProperty(marshallerType); - bool valuePropertyIsRefReturn = valueProperty is { ReturnsByRef: true } or { ReturnsByRefReadonly: true }; + IMethodSymbol? toNativeValueMethod = ManualTypeMarshallingHelper.FindToNativeValueMethod(marshallerType); + IMethodSymbol? fromNativeValueMethod = ManualTypeMarshallingHelper.FindFromNativeValueMethod(marshallerType); + bool toNativeValueMethodIsRefReturn = toNativeValueMethod is { ReturnsByRef: true } or { ReturnsByRefReadonly: true }; ITypeSymbol nativeType = marshallerType; - if (valueProperty is not null) + + // If either ToNativeValue or FromNativeValue is provided, validate the scenarios where they are required. + ValidateTwoStageMarshalling(); + + if (toNativeValueMethod is not null) { - if (valuePropertyIsRefReturn) + if (toNativeValueMethodIsRefReturn) { context.ReportDiagnostic( - valueProperty.CreateDiagnostic( + toNativeValueMethod.CreateDiagnostic( RefValuePropertyUnsupportedRule, marshallerType.ToDisplayString())); } - nativeType = valueProperty.Type; - - // Validate that we don't have partial implementations. - // We error if either of the conditions below are partially met but not fully met: - // - a constructor and a Value property getter - // - a ToManaged method and a Value property setter - if ((hasConstructor || hasCallerAllocSpanConstructor) && valueProperty.GetMethod is null) - { - context.ReportDiagnostic( - valueProperty.CreateDiagnostic( - ValuePropertyMustHaveGetterRule, - marshallerType.ToDisplayString())); - } - if (hasToManaged && valueProperty.SetMethod is null) - { - context.ReportDiagnostic( - valueProperty.CreateDiagnostic( - ValuePropertyMustHaveSetterRule, - marshallerType.ToDisplayString())); - } + nativeType = toNativeValueMethod.ReturnType; } else if (ManualTypeMarshallingHelper.FindGetPinnableReference(marshallerType) is IMethodSymbol marshallerGetPinnableReferenceMethod) { @@ -495,7 +511,7 @@ public void AnalyzeMarshallerType(SymbolAnalysisContext context) if (!nativeType.IsConsideredBlittable()) { context.ReportDiagnostic( - (valueProperty ?? (ISymbol)marshallerType).CreateDiagnostic( + (toNativeValueMethod ?? (ISymbol)marshallerType).CreateDiagnostic( NativeTypeMustBeBlittableRule, nativeType.ToDisplayString(), type.ToDisplayString())); @@ -509,7 +525,7 @@ public void AnalyzeMarshallerType(SymbolAnalysisContext context) context.ReportDiagnostic(managedGetPinnableReferenceMethod.CreateDiagnostic(GetPinnableReferenceReturnTypeBlittableRule)); } // Validate that our marshaler supports scenarios where GetPinnableReference cannot be used. - if (!hasConstructor || valueProperty is { GetMethod: null }) + if (!hasConstructor || toNativeValueMethod is null) { context.ReportDiagnostic( type.CreateDiagnostic( @@ -519,19 +535,54 @@ public void AnalyzeMarshallerType(SymbolAnalysisContext context) // If the managed type has a GetPinnableReference method, make sure that the Value getter is also a pointer-sized primitive. // This ensures that marshalling via pinning the managed value and marshalling via the default marshaller will have the same ABI. - if (!valuePropertyIsRefReturn // Ref returns are already reported above as invalid, so don't issue another warning here about them + if (toNativeValueMethod is not null + && !toNativeValueMethodIsRefReturn // Ref returns are already reported above as invalid, so don't issue another warning here about them && nativeType is not ( IPointerTypeSymbol or { SpecialType: SpecialType.System_IntPtr } or { SpecialType: SpecialType.System_UIntPtr })) { context.ReportDiagnostic( - valueProperty.CreateDiagnostic( + toNativeValueMethod.CreateDiagnostic( NativeTypeMustBePointerSizedRule, nativeType.ToDisplayString(), managedGetPinnableReferenceMethod.ContainingType.ToDisplayString())); } } + + void ValidateTwoStageMarshalling() + { + bool hasTwoStageMarshalling = false; + if ((hasConstructor || hasCallerAllocSpanConstructor) && toNativeValueMethod is not null) + { + hasTwoStageMarshalling = true; + } + + if (hasToManaged && fromNativeValueMethod is not null) + { + hasTwoStageMarshalling = true; + } + + if (hasTwoStageMarshalling) + { + if ((hasConstructor || hasCallerAllocSpanConstructor) && toNativeValueMethod is null) + { + context.ReportDiagnostic(marshallerType.CreateDiagnostic(ValuePropertyMustHaveGetterRule, marshallerType.ToDisplayString())); + } + if (hasToManaged && fromNativeValueMethod is null) + { + context.ReportDiagnostic(marshallerType.CreateDiagnostic(ValuePropertyMustHaveSetterRule, marshallerType.ToDisplayString())); + } + } + + // ToNativeValue and FromNativeValue must be provided with the same type. + if (toNativeValueMethod is not null + && fromNativeValueMethod is not null + && !SymbolEqualityComparer.Default.Equals(toNativeValueMethod.ReturnType, fromNativeValueMethod.Parameters[0].Type)) + { + // TODO: Add diagnostic. + } + } } } } diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/SpanCollectionElementMarshallingCodeContext.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/LinearCollectionElementMarshallingCodeContext.cs similarity index 92% rename from src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/SpanCollectionElementMarshallingCodeContext.cs rename to src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/LinearCollectionElementMarshallingCodeContext.cs index 4f390113cae26..442c3ab55e372 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/SpanCollectionElementMarshallingCodeContext.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/LinearCollectionElementMarshallingCodeContext.cs @@ -5,6 +5,7 @@ namespace Microsoft.Interop { internal sealed class LinearCollectionElementMarshallingCodeContext : StubCodeContext { + private readonly string _managedSpanIdentifier; private readonly string _nativeSpanIdentifier; public override bool SingleFrameSpansNativeContext => false; @@ -22,11 +23,13 @@ internal sealed class LinearCollectionElementMarshallingCodeContext : StubCodeCo /// The parent context. public LinearCollectionElementMarshallingCodeContext( Stage currentStage, + string managedSpanIdentifier, string nativeSpanIdentifier, StubCodeContext parentContext) { CurrentStage = currentStage; IndexerIdentifier = CalculateIndexerIdentifierBasedOnParentContext(parentContext); + _managedSpanIdentifier = managedSpanIdentifier; _nativeSpanIdentifier = nativeSpanIdentifier; ParentContext = parentContext; } @@ -38,9 +41,8 @@ public LinearCollectionElementMarshallingCodeContext( /// Managed and native identifiers public override (string managed, string native) GetIdentifiers(TypePositionInfo info) { - (string _, string native) = ParentContext!.GetIdentifiers(info); return ( - $"{native}.ManagedValues[{IndexerIdentifier}]", + $"{_managedSpanIdentifier}[{IndexerIdentifier}]", $"{_nativeSpanIdentifier}[{IndexerIdentifier}]" ); } 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 50015fd1c80ea..0fb2cfaf394e8 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 @@ -5,27 +5,38 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; +using System.Runtime.InteropServices; using Microsoft.CodeAnalysis; namespace Microsoft.Interop { - public enum CustomTypeMarshallerKind + public readonly record struct CustomTypeMarshallerData(CustomTypeMarshallerKind Kind, int? BufferSize); + + public static class ShapeMemberNames { - Value, - LinearCollection + public abstract class Value + { + public const string ToNativeValue = nameof(ToNativeValue); + public const string FromNativeValue = nameof(FromNativeValue); + public const string GetPinnableReference = nameof(GetPinnableReference); + public const string FreeNative = nameof(FreeNative); + public const string ToManaged = nameof(ToManaged); + } + + public abstract class LinearCollection : Value + { + public const string GetManagedValuesDestination = nameof(GetManagedValuesDestination); + public const string GetManagedValuesSource = nameof(GetManagedValuesSource); + public const string GetNativeValuesDestination = nameof(GetNativeValuesDestination); + public const string GetNativeValuesSource = nameof(GetNativeValuesSource); + } } - public readonly record struct CustomTypeMarshallerData(CustomTypeMarshallerKind Kind, int? BufferSize, bool RequiresStackBuffer); public static class ManualTypeMarshallingHelper { - public const string ValuePropertyName = "Value"; - public const string GetPinnableReferenceName = "GetPinnableReference"; - public const string BufferSizeFieldName = "BufferSize"; - public const string RequiresStackBufferFieldName = "RequiresStackBuffer"; - public const string ToManagedMethodName = "ToManaged"; - public const string FreeNativeMethodName = "FreeNative"; - public const string ManagedValuesPropertyName = "ManagedValues"; - public const string NativeValueStoragePropertyName = "NativeValueStorage"; - public const string SetUnmarshalledCollectionLengthMethodName = "SetUnmarshalledCollectionLength"; + public static class CustomMarshallerAttributeFields + { + public const string BufferSize = nameof(BufferSize); + } public static class MarshalUsingProperties { @@ -57,16 +68,11 @@ public static (bool hasAttribute, ITypeSymbol? managedType, CustomTypeMarshaller } var namedArguments = attr.NamedArguments.ToImmutableDictionary(); int? bufferSize = null; - if (namedArguments.TryGetValue(BufferSizeFieldName, out TypedConstant bufferSizeConstant)) + if (namedArguments.TryGetValue(CustomMarshallerAttributeFields.BufferSize, out TypedConstant bufferSizeConstant)) { bufferSize = bufferSizeConstant.Value as int?; } - bool requiresStackBuffer = false; - if (namedArguments.TryGetValue(RequiresStackBufferFieldName, out TypedConstant requiresStackBufferConstant)) - { - requiresStackBuffer = bufferSizeConstant.Value as bool? ?? false; - } - return (true, managedType, new CustomTypeMarshallerData(kind, bufferSize, requiresStackBuffer)); + return (true, managedType, new CustomTypeMarshallerData(kind, bufferSize)); } /// @@ -161,7 +167,7 @@ public static (AttributeData? attribute, INamedTypeSymbol? marshallerType) GetDe public static bool HasToManagedMethod(ITypeSymbol nativeType, ITypeSymbol managedType) { - return nativeType.GetMembers(ToManagedMethodName) + return nativeType.GetMembers(ShapeMemberNames.Value.ToManaged) .OfType() .Any(m => m.Parameters.IsEmpty && !m.ReturnsByRef @@ -209,66 +215,81 @@ public static bool IsCallerAllocatedSpanConstructor( // fixed statement. We aren't supporting a GetPinnableReference extension method // (which is apparently supported in the compiler). // https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-7.3/pattern-based-fixed - return type.GetMembers(GetPinnableReferenceName) + return type.GetMembers(ShapeMemberNames.Value.GetPinnableReference) .OfType() - .FirstOrDefault(m => m is { Parameters: { Length: 0 } } and + .FirstOrDefault(m => m is { Parameters.Length: 0 } and ({ ReturnsByRef: true } or { ReturnsByRefReadonly: true })); } - public static IPropertySymbol? FindValueProperty(ITypeSymbol type) + public static bool HasFreeNativeMethod(ITypeSymbol type) { - return type.GetMembers(ValuePropertyName) - .OfType() - .FirstOrDefault(p => !p.IsStatic); + return type.GetMembers(ShapeMemberNames.Value.FreeNative) + .OfType() + .Any(m => m is { IsStatic: false, Parameters.Length: 0, ReturnType.SpecialType: SpecialType.System_Void }); } - public static bool HasFreeNativeMethod(ITypeSymbol type) + public static IMethodSymbol? FindToNativeValueMethod(ITypeSymbol type) { - return type.GetMembers(FreeNativeMethodName) + return type.GetMembers(ShapeMemberNames.Value.ToNativeValue) .OfType() - .Any(m => m is { IsStatic: false, Parameters: { Length: 0 }, ReturnType: { SpecialType: SpecialType.System_Void } }); + .FirstOrDefault(m => m is { IsStatic: false, Parameters.Length: 0 }); } - public static bool TryGetManagedValuesProperty(ITypeSymbol type, out IPropertySymbol managedValuesProperty) + public static IMethodSymbol? FindFromNativeValueMethod(ITypeSymbol type) { - managedValuesProperty = type - .GetMembers(ManagedValuesPropertyName) - .OfType() - .FirstOrDefault(p => p is { IsStatic: false, GetMethod: not null, ReturnsByRef: false, ReturnsByRefReadonly: false }); - return managedValuesProperty is not null; + return type.GetMembers(ShapeMemberNames.Value.FromNativeValue) + .OfType() + .FirstOrDefault(m => m is { IsStatic: false, Parameters.Length: 1, ReturnType.SpecialType: SpecialType.System_Void }); } - public static bool TryGetElementTypeFromLinearCollectionMarshaller(ITypeSymbol type, out ITypeSymbol elementType) + public static bool TryGetElementTypeFromLinearCollectionMarshaller(ITypeSymbol type, ITypeSymbol readOnlySpanOfT, out ITypeSymbol elementType) { - if (!TryGetManagedValuesProperty(type, out IPropertySymbol managedValuesProperty)) + if (FindGetManagedValuesSourceMethod(type, readOnlySpanOfT) is not IMethodSymbol managedValuesSourceMethod) { elementType = null!; return false; } - elementType = ((INamedTypeSymbol)managedValuesProperty.Type).TypeArguments[0]; + elementType = ((INamedTypeSymbol)managedValuesSourceMethod.ReturnType).TypeArguments[0]; return true; } - public static bool HasSetUnmarshalledCollectionLengthMethod(ITypeSymbol type) + public static IMethodSymbol? FindGetManagedValuesSourceMethod(ITypeSymbol type, ITypeSymbol readOnlySpanOfT) { - return type.GetMembers(SetUnmarshalledCollectionLengthMethodName) + return type + .GetMembers(ShapeMemberNames.LinearCollection.GetManagedValuesSource) .OfType() - .Any(m => m is - { - IsStatic: false, - Parameters: { Length: 1 }, - ReturnType: { SpecialType: SpecialType.System_Void } - } && m.Parameters[0].Type.SpecialType == SpecialType.System_Int32); + .FirstOrDefault(m => m is { IsStatic: false, ReturnsByRef: false, ReturnsByRefReadonly: false, Parameters.Length: 0, ReturnType: INamedTypeSymbol { ConstructedFrom: INamedTypeSymbol returnType } } + && SymbolEqualityComparer.Default.Equals(returnType, readOnlySpanOfT)); + } + + public static IMethodSymbol? FindGetManagedValuesDestinationMethod(ITypeSymbol type, ITypeSymbol spanOfT) + { + return type + .GetMembers(ShapeMemberNames.LinearCollection.GetManagedValuesDestination) + .OfType() + .FirstOrDefault(m => m is { IsStatic: false, ReturnsByRef: false, ReturnsByRefReadonly: false, Parameters.Length: 1, ReturnType: INamedTypeSymbol { ConstructedFrom: INamedTypeSymbol returnType } } + && m.Parameters[0].Type.SpecialType == SpecialType.System_Int32 + && SymbolEqualityComparer.Default.Equals(returnType, spanOfT)); } - public static bool HasNativeValueStorageProperty(ITypeSymbol type, ITypeSymbol spanOfByte) + public static IMethodSymbol? FindGetNativeValuesDestinationMethod(ITypeSymbol type, ITypeSymbol spanOfByte) { return type - .GetMembers(NativeValueStoragePropertyName) - .OfType() - .Any(p => p is { IsStatic: false, GetMethod: not null, ReturnsByRef: false, ReturnsByRefReadonly: false } - && SymbolEqualityComparer.Default.Equals(p.Type, spanOfByte)); + .GetMembers(ShapeMemberNames.LinearCollection.GetNativeValuesDestination) + .OfType() + .FirstOrDefault(m => m is { IsStatic: false, ReturnsByRef: false, ReturnsByRefReadonly: false, Parameters.Length: 0, ReturnType: INamedTypeSymbol returnType } + && SymbolEqualityComparer.Default.Equals(returnType, spanOfByte)); + } + + public static IMethodSymbol? FindGetNativeValuesSourceMethod(ITypeSymbol type, ITypeSymbol readOnlySpanOfByte) + { + return type + .GetMembers(ShapeMemberNames.LinearCollection.GetNativeValuesSource) + .OfType() + .FirstOrDefault(m => m is { IsStatic: false, ReturnsByRef: false, ReturnsByRefReadonly: false, Parameters.Length: 1, ReturnType: INamedTypeSymbol returnType } + && m.Parameters[0].Type.SpecialType == SpecialType.System_Int32 + && SymbolEqualityComparer.Default.Equals(returnType, readOnlySpanOfByte)); } } } 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 e73293b87148e..a7ddc91432b5e 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 @@ -47,8 +47,8 @@ public IMarshallingGenerator Create(TypePositionInfo info, StubCodeContext conte return info.MarshallingAttributeInfo switch { NativeMarshallingAttributeInfo marshalInfo when Options.RuntimeMarshallingDisabled => CreateCustomNativeTypeMarshaller(info, context, marshalInfo), - NativeMarshallingAttributeInfo { ValuePropertyType: SpecialTypeInfo specialType } marshalInfo when specialType.SpecialType.IsAlwaysBlittable() => CreateCustomNativeTypeMarshaller(info, context, marshalInfo), - NativeMarshallingAttributeInfo { ValuePropertyType: PointerTypeInfo } marshalInfo => CreateCustomNativeTypeMarshaller(info, context, marshalInfo), + NativeMarshallingAttributeInfo { NativeValueType: SpecialTypeInfo specialType } marshalInfo when specialType.SpecialType.IsAlwaysBlittable() => CreateCustomNativeTypeMarshaller(info, context, marshalInfo), + NativeMarshallingAttributeInfo { NativeValueType: PointerTypeInfo } marshalInfo => CreateCustomNativeTypeMarshaller(info, context, marshalInfo), UnmanagedBlittableMarshallingInfo when Options.RuntimeMarshallingDisabled => s_blittable, UnmanagedBlittableMarshallingInfo or NativeMarshallingAttributeInfo when !Options.RuntimeMarshallingDisabled => throw new MarshallingNotSupportedException(info, context) @@ -157,7 +157,7 @@ private IMarshallingGenerator CreateCustomNativeTypeMarshaller(TypePositionInfo ICustomNativeTypeMarshallingStrategy marshallingStrategy = new SimpleCustomNativeTypeMarshalling(marshalInfo.NativeMarshallingType.Syntax); - if ((marshalInfo.MarshallingFeatures & CustomMarshallingFeatures.ManagedToNativeStackalloc) != 0) + if ((marshalInfo.MarshallingFeatures & CustomTypeMarshallerFeatures.CallerAllocatedBuffer) != 0) { if (marshalInfo.BufferSize is null) { @@ -166,7 +166,7 @@ private IMarshallingGenerator CreateCustomNativeTypeMarshaller(TypePositionInfo marshallingStrategy = new StackallocOptimizationMarshalling(marshallingStrategy, marshalInfo.BufferSize.Value); } - if ((marshalInfo.MarshallingFeatures & CustomMarshallingFeatures.FreeNativeResources) != 0) + if ((marshalInfo.MarshallingFeatures & CustomTypeMarshallerFeatures.UnmanagedResources) != 0) { marshallingStrategy = new FreeNativeCleanupStrategy(marshallingStrategy); } @@ -177,14 +177,14 @@ private IMarshallingGenerator CreateCustomNativeTypeMarshaller(TypePositionInfo return CreateNativeCollectionMarshaller(info, context, collectionMarshallingInfo, marshallingStrategy); } - if (marshalInfo.ValuePropertyType is not null) + if (marshalInfo.NativeValueType is not null) { marshallingStrategy = DecorateWithValuePropertyStrategy(marshalInfo, marshallingStrategy); } IMarshallingGenerator marshallingGenerator = new CustomNativeTypeMarshallingGenerator(marshallingStrategy, enableByValueContentsMarshalling: false); - if ((marshalInfo.MarshallingFeatures & CustomMarshallingFeatures.ManagedTypePinning) != 0) + if (marshalInfo.PinningFeatures.HasFlag(CustomTypeMarshallerPinning.ManagedType)) { return new PinnableManagedValueMarshaller(marshallingGenerator); } @@ -197,7 +197,7 @@ private void ValidateCustomNativeTypeMarshallingSupported(TypePositionInfo info, // The marshalling method for this type doesn't support marshalling from native to managed, // but our scenario requires marshalling from native to managed. if ((info.RefKind == RefKind.Ref || info.RefKind == RefKind.Out || info.IsManagedReturnPosition) - && (marshalInfo.MarshallingFeatures & CustomMarshallingFeatures.NativeToManaged) == 0) + && !marshalInfo.Direction.HasFlag(CustomTypeMarshallerDirection.Out)) { throw new MarshallingNotSupportedException(info, context) { @@ -207,8 +207,10 @@ private void ValidateCustomNativeTypeMarshallingSupported(TypePositionInfo info, // The marshalling method for this type doesn't support marshalling from managed to native by value, // but our scenario requires marshalling from managed to native by value. else if (!info.IsByRef - && (marshalInfo.MarshallingFeatures & CustomMarshallingFeatures.ManagedToNative) == 0 - && (context.SingleFrameSpansNativeContext && (marshalInfo.MarshallingFeatures & (CustomMarshallingFeatures.ManagedTypePinning | CustomMarshallingFeatures.ManagedToNativeStackalloc)) == 0)) + && context.SingleFrameSpansNativeContext + && !(marshalInfo.PinningFeatures.HasFlag(CustomTypeMarshallerPinning.ManagedType) + || marshalInfo.MarshallingFeatures.HasFlag(CustomTypeMarshallerFeatures.CallerAllocatedBuffer) + || marshalInfo.Direction.HasFlag(CustomTypeMarshallerDirection.In))) { throw new MarshallingNotSupportedException(info, context) { @@ -219,8 +221,8 @@ private void ValidateCustomNativeTypeMarshallingSupported(TypePositionInfo info, // but our scenario requires marshalling from managed to native by reference. // "in" byref supports stack marshalling. else if (info.RefKind == RefKind.In - && (marshalInfo.MarshallingFeatures & CustomMarshallingFeatures.ManagedToNative) == 0 - && !(context.SingleFrameSpansNativeContext && (marshalInfo.MarshallingFeatures & CustomMarshallingFeatures.ManagedToNativeStackalloc) != 0)) + && !(context.SingleFrameSpansNativeContext && marshalInfo.MarshallingFeatures.HasFlag(CustomTypeMarshallerFeatures.CallerAllocatedBuffer)) + && !marshalInfo.Direction.HasFlag(CustomTypeMarshallerDirection.In)) { throw new MarshallingNotSupportedException(info, context) { @@ -230,8 +232,9 @@ private void ValidateCustomNativeTypeMarshallingSupported(TypePositionInfo info, // The marshalling method for this type doesn't support marshalling from managed to native by reference, // but our scenario requires marshalling from managed to native by reference. // "ref" byref marshalling doesn't support stack marshalling + // The "Out" direction for "ref" was checked above else if (info.RefKind == RefKind.Ref - && (marshalInfo.MarshallingFeatures & CustomMarshallingFeatures.ManagedToNative) == 0) + && !marshalInfo.Direction.HasFlag(CustomTypeMarshallerDirection.In)) { throw new MarshallingNotSupportedException(info, context) { @@ -242,14 +245,14 @@ private void ValidateCustomNativeTypeMarshallingSupported(TypePositionInfo info, private ICustomNativeTypeMarshallingStrategy DecorateWithValuePropertyStrategy(NativeMarshallingAttributeInfo marshalInfo, ICustomNativeTypeMarshallingStrategy nativeTypeMarshaller) { - TypeSyntax valuePropertyTypeSyntax = marshalInfo.ValuePropertyType!.Syntax; + TypeSyntax valuePropertyTypeSyntax = marshalInfo.NativeValueType!.Syntax; - if ((marshalInfo.MarshallingFeatures & CustomMarshallingFeatures.NativeTypePinning) != 0) + if (marshalInfo.PinningFeatures.HasFlag(CustomTypeMarshallerPinning.NativeType) && marshalInfo.MarshallingFeatures.HasFlag(CustomTypeMarshallerFeatures.TwoStageMarshalling)) { return new PinnableMarshallerTypeMarshalling(nativeTypeMarshaller, valuePropertyTypeSyntax); } - return new CustomNativeTypeWithValuePropertyMarshalling(nativeTypeMarshaller, valuePropertyTypeSyntax); + return new CustomNativeTypeWithToFromNativeValueMarshalling(nativeTypeMarshaller, valuePropertyTypeSyntax); } private IMarshallingGenerator CreateNativeCollectionMarshaller( @@ -261,36 +264,36 @@ private IMarshallingGenerator CreateNativeCollectionMarshaller( var elementInfo = new TypePositionInfo(collectionInfo.ElementType, collectionInfo.ElementMarshallingInfo) { ManagedIndex = info.ManagedIndex }; IMarshallingGenerator elementMarshaller = _elementMarshallingGenerator.Create( elementInfo, - new LinearCollectionElementMarshallingCodeContext(StubCodeContext.Stage.Setup, string.Empty, context)); + new LinearCollectionElementMarshallingCodeContext(StubCodeContext.Stage.Setup, string.Empty, string.Empty, context)); TypeSyntax elementType = elementMarshaller.AsNativeType(elementInfo); + + ExpressionSyntax numElementsExpression = LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(0)); + if (info.IsManagedReturnPosition || (info.IsByRef && info.RefKind != RefKind.In)) + { + // In this case, we need a numElementsExpression supplied from metadata, so we'll calculate it here. + numElementsExpression = GetNumElementsExpressionFromMarshallingInfo(info, collectionInfo.ElementCountInfo, context); + } + bool isBlittable = elementMarshaller is BlittableMarshaller; if (isBlittable) { - marshallingStrategy = new LinearCollectionWithBlittableElementsMarshalling(marshallingStrategy, collectionInfo.ElementType.Syntax); + marshallingStrategy = new LinearCollectionWithBlittableElementsMarshalling(marshallingStrategy, collectionInfo.ElementType.Syntax, numElementsExpression); } else { - marshallingStrategy = new LinearCollectionWithNonBlittableElementsMarshalling(marshallingStrategy, elementMarshaller, elementInfo); + marshallingStrategy = new LinearCollectionWithNonBlittableElementsMarshalling(marshallingStrategy, elementMarshaller, elementInfo, numElementsExpression); } // Explicitly insert the Value property handling here (before numElements handling) so that the numElements handling will be emitted before the Value property handling in unmarshalling. - if (collectionInfo.ValuePropertyType is not null) + if (collectionInfo.NativeValueType is not null) { marshallingStrategy = DecorateWithValuePropertyStrategy(collectionInfo, marshallingStrategy); } - ExpressionSyntax numElementsExpression = LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(0)); - if (info.IsManagedReturnPosition || (info.IsByRef && info.RefKind != RefKind.In)) - { - // In this case, we need a numElementsExpression supplied from metadata, so we'll calculate it here. - numElementsExpression = GetNumElementsExpressionFromMarshallingInfo(info, collectionInfo.ElementCountInfo, context); - } - - marshallingStrategy = new NumElementsExpressionMarshalling( + marshallingStrategy = new SizeOfElementMarshalling( marshallingStrategy, - numElementsExpression, SizeOfExpression(elementType)); if (collectionInfo.UseDefaultMarshalling && info.ManagedType is SzArrayType) @@ -303,7 +306,7 @@ private IMarshallingGenerator CreateNativeCollectionMarshaller( IMarshallingGenerator marshallingGenerator = new CustomNativeTypeMarshallingGenerator(marshallingStrategy, enableByValueContentsMarshalling: false); - if ((collectionInfo.MarshallingFeatures & CustomMarshallingFeatures.ManagedTypePinning) != 0) + if (collectionInfo.PinningFeatures.HasFlag(CustomTypeMarshallerPinning.ManagedType)) { return new PinnableManagedValueMarshaller(marshallingGenerator); } diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ICustomNativeTypeMarshallingStrategy.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ICustomNativeTypeMarshallingStrategy.cs index 3b912856136f5..205e3ae2fcb8d 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ICustomNativeTypeMarshallingStrategy.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ICustomNativeTypeMarshallingStrategy.cs @@ -105,7 +105,7 @@ public IEnumerable GenerateUnmarshalStatements(TypePositionInfo InvocationExpression( MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, IdentifierName(nativeIdentifier), - IdentifierName(ManualTypeMarshallingHelper.ToManagedMethodName))))); + IdentifierName(ShapeMemberNames.Value.ToManaged))))); } public IEnumerable GetNativeTypeConstructorArguments(TypePositionInfo info, StubCodeContext context) @@ -127,9 +127,9 @@ public IEnumerable GeneratePinStatements(TypePositionInfo info, /// /// A context that redefines the 'native' identifier for a TypePositionInfo to be the marshaller identifier. /// - internal class CustomNativeTypeWithValuePropertyStubContext : StubCodeContext + internal class CustomNativeTypeWithToFromNativeValueContext : StubCodeContext { - public CustomNativeTypeWithValuePropertyStubContext(StubCodeContext parentContext) + public CustomNativeTypeWithToFromNativeValueContext(StubCodeContext parentContext) { ParentContext = parentContext; CurrentStage = parentContext.CurrentStage; @@ -148,15 +148,15 @@ public override (string managed, string native) GetIdentifiers(TypePositionInfo /// /// Marshaller that enables support of a Value property on a native type. /// - internal sealed class CustomNativeTypeWithValuePropertyMarshalling : ICustomNativeTypeMarshallingStrategy + internal sealed class CustomNativeTypeWithToFromNativeValueMarshalling : ICustomNativeTypeMarshallingStrategy { private readonly ICustomNativeTypeMarshallingStrategy _innerMarshaller; - private readonly TypeSyntax _valuePropertyType; + private readonly TypeSyntax _nativeValueTypeSyntax; - public CustomNativeTypeWithValuePropertyMarshalling(ICustomNativeTypeMarshallingStrategy innerMarshaller, TypeSyntax valuePropertyType) + public CustomNativeTypeWithToFromNativeValueMarshalling(ICustomNativeTypeMarshallingStrategy innerMarshaller, TypeSyntax nativeValueTypeSyntax) { _innerMarshaller = innerMarshaller; - _valuePropertyType = valuePropertyType; + _nativeValueTypeSyntax = nativeValueTypeSyntax; } public ArgumentSyntax AsArgument(TypePositionInfo info, StubCodeContext context) @@ -175,7 +175,7 @@ public ArgumentSyntax AsArgument(TypePositionInfo info, StubCodeContext context) public TypeSyntax AsNativeType(TypePositionInfo info) { - return _valuePropertyType; + return _nativeValueTypeSyntax; } public bool UsesNativeIdentifier(TypePositionInfo info, StubCodeContext context) @@ -185,14 +185,14 @@ public bool UsesNativeIdentifier(TypePositionInfo info, StubCodeContext context) public IEnumerable GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context) { - var subContext = new CustomNativeTypeWithValuePropertyStubContext(context); + var subContext = new CustomNativeTypeWithToFromNativeValueContext(context); // When temporary state does not live across stages, the marshaller state is uninitialized // in any stage other than Marshal and Unmarshal. So, we need to reinitialize it here in Cleanup // from the native value so we can safely run any cleanup functionality in the marshaller. if (!context.AdditionalTemporaryStateLivesAcrossStages) { - yield return GenerateValuePropertyAssignment(info, context, subContext); + yield return GenerateFromNativeValueInvocation(info, context, subContext); } foreach (StatementSyntax statement in _innerMarshaller.GenerateCleanupStatements(info, subContext)) @@ -203,41 +203,42 @@ public IEnumerable GenerateCleanupStatements(TypePositionInfo i public IEnumerable GenerateMarshalStatements(TypePositionInfo info, StubCodeContext context, IEnumerable nativeTypeConstructorArguments) { - var subContext = new CustomNativeTypeWithValuePropertyStubContext(context); + var subContext = new CustomNativeTypeWithToFromNativeValueContext(context); foreach (StatementSyntax statement in _innerMarshaller.GenerateMarshalStatements(info, subContext, nativeTypeConstructorArguments)) { yield return statement; } - // = .Value; + // = .ToNativeValue(); yield return ExpressionStatement( AssignmentExpression( SyntaxKind.SimpleAssignmentExpression, IdentifierName(context.GetIdentifiers(info).native), - MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, - IdentifierName(subContext.GetIdentifiers(info).native), - IdentifierName(ManualTypeMarshallingHelper.ValuePropertyName)))); + InvocationExpression( + MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, + IdentifierName(subContext.GetIdentifiers(info).native), + IdentifierName(ShapeMemberNames.Value.ToNativeValue)), + ArgumentList()))); } - private StatementSyntax GenerateValuePropertyAssignment(TypePositionInfo info, StubCodeContext context, CustomNativeTypeWithValuePropertyStubContext subContext) + private StatementSyntax GenerateFromNativeValueInvocation(TypePositionInfo info, StubCodeContext context, CustomNativeTypeWithToFromNativeValueContext subContext) { - // .Value = ; + // .FromNativeValue(); return ExpressionStatement( - AssignmentExpression( - SyntaxKind.SimpleAssignmentExpression, + InvocationExpression( MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, IdentifierName(subContext.GetIdentifiers(info).native), - IdentifierName(ManualTypeMarshallingHelper.ValuePropertyName)), - IdentifierName(context.GetIdentifiers(info).native))); + IdentifierName(ShapeMemberNames.Value.FromNativeValue)), + ArgumentList(SingletonSeparatedList(Argument(IdentifierName(context.GetIdentifiers(info).native)))))); } public IEnumerable GenerateUnmarshalStatements(TypePositionInfo info, StubCodeContext context) { - var subContext = new CustomNativeTypeWithValuePropertyStubContext(context); + var subContext = new CustomNativeTypeWithToFromNativeValueContext(context); if (info.IsManagedReturnPosition || (info.IsByRef && info.RefKind != RefKind.In)) { - yield return GenerateValuePropertyAssignment(info, context, subContext); + yield return GenerateFromNativeValueInvocation(info, context, subContext); } foreach (StatementSyntax statement in _innerMarshaller.GenerateUnmarshalStatements(info, subContext)) @@ -248,13 +249,13 @@ public IEnumerable GenerateUnmarshalStatements(TypePositionInfo public IEnumerable GetNativeTypeConstructorArguments(TypePositionInfo info, StubCodeContext context) { - var subContext = new CustomNativeTypeWithValuePropertyStubContext(context); + var subContext = new CustomNativeTypeWithToFromNativeValueContext(context); return _innerMarshaller.GetNativeTypeConstructorArguments(info, subContext); } public IEnumerable GenerateSetupStatements(TypePositionInfo info, StubCodeContext context) { - var subContext = new CustomNativeTypeWithValuePropertyStubContext(context); + var subContext = new CustomNativeTypeWithToFromNativeValueContext(context); yield return LocalDeclarationStatement( VariableDeclaration( _innerMarshaller.AsNativeType(info), @@ -270,7 +271,7 @@ public IEnumerable GenerateSetupStatements(TypePositionInfo inf public IEnumerable GeneratePinStatements(TypePositionInfo info, StubCodeContext context) { - var subContext = new CustomNativeTypeWithValuePropertyStubContext(context); + var subContext = new CustomNativeTypeWithToFromNativeValueContext(context); return _innerMarshaller.GeneratePinStatements(info, subContext); } } @@ -416,7 +417,7 @@ public IEnumerable GenerateCleanupStatements(TypePositionInfo i InvocationExpression( MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, IdentifierName(context.GetIdentifiers(info).native), - IdentifierName(ManualTypeMarshallingHelper.FreeNativeMethodName)))); + IdentifierName(ShapeMemberNames.Value.FreeNative)))); } public IEnumerable GenerateMarshalStatements(TypePositionInfo info, StubCodeContext context, IEnumerable nativeTypeConstructorArguments) @@ -451,17 +452,17 @@ public bool UsesNativeIdentifier(TypePositionInfo info, StubCodeContext context) } /// - /// Marshaller that calls the GetPinnableReference method on the marshaller value and enables support for the Value property. + /// Marshaller that calls the GetPinnableReference method on the marshaller value and enables support for the ToNativeValue and FromNativeValue methods. /// internal sealed class PinnableMarshallerTypeMarshalling : ICustomNativeTypeMarshallingStrategy { private readonly ICustomNativeTypeMarshallingStrategy _innerMarshaller; - private readonly TypeSyntax _valuePropertyType; + private readonly TypeSyntax _nativeValueType; - public PinnableMarshallerTypeMarshalling(ICustomNativeTypeMarshallingStrategy innerMarshaller, TypeSyntax valuePropertyType) + public PinnableMarshallerTypeMarshalling(ICustomNativeTypeMarshallingStrategy innerMarshaller, TypeSyntax nativeValueType) { _innerMarshaller = innerMarshaller; - _valuePropertyType = valuePropertyType; + _nativeValueType = nativeValueType; } private bool CanPinMarshaller(TypePositionInfo info, StubCodeContext context) @@ -476,17 +477,17 @@ public ArgumentSyntax AsArgument(TypePositionInfo info, StubCodeContext context) public TypeSyntax AsNativeType(TypePositionInfo info) { - return _valuePropertyType; + return _nativeValueType; } public IEnumerable GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context) { - var subContext = new CustomNativeTypeWithValuePropertyStubContext(context); + var subContext = new CustomNativeTypeWithToFromNativeValueContext(context); if (!context.AdditionalTemporaryStateLivesAcrossStages) { // .Value = ; - yield return GenerateValuePropertyAssignment(info, context, subContext); + yield return GenerateFromNativeValueInvocation(info, context, subContext); } foreach (StatementSyntax statement in _innerMarshaller.GenerateCleanupStatements(info, subContext)) @@ -497,46 +498,48 @@ public IEnumerable GenerateCleanupStatements(TypePositionInfo i public IEnumerable GenerateMarshalStatements(TypePositionInfo info, StubCodeContext context, IEnumerable nativeTypeConstructorArguments) { - var subContext = new CustomNativeTypeWithValuePropertyStubContext(context); + var subContext = new CustomNativeTypeWithToFromNativeValueContext(context); foreach (StatementSyntax statement in _innerMarshaller.GenerateMarshalStatements(info, subContext, nativeTypeConstructorArguments)) { yield return statement; } if (!CanPinMarshaller(info, context)) - yield return GenerateNativeAssignmentFromValueProperty(info, context, subContext); + yield return GenerateToNativeValueInvocation(info, context, subContext); } - private static StatementSyntax GenerateNativeAssignmentFromValueProperty(TypePositionInfo info, StubCodeContext context, CustomNativeTypeWithValuePropertyStubContext subContext) + private static StatementSyntax GenerateToNativeValueInvocation(TypePositionInfo info, StubCodeContext context, CustomNativeTypeWithToFromNativeValueContext subContext) { - // = .Value; + // = .ToNativeValue(); return ExpressionStatement( AssignmentExpression( SyntaxKind.SimpleAssignmentExpression, IdentifierName(context.GetIdentifiers(info).native), - MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, - IdentifierName(subContext.GetIdentifiers(info).native), - IdentifierName(ManualTypeMarshallingHelper.ValuePropertyName)))); + InvocationExpression( + MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, + IdentifierName(subContext.GetIdentifiers(info).native), + IdentifierName(ShapeMemberNames.Value.ToNativeValue)), + ArgumentList()))); } public IEnumerable GeneratePinStatements(TypePositionInfo info, StubCodeContext context) { // fixed (<_nativeTypeSyntax> = &) // - var subContext = new CustomNativeTypeWithValuePropertyStubContext(context); + var subContext = new CustomNativeTypeWithToFromNativeValueContext(context); yield return FixedStatement( VariableDeclaration( - _valuePropertyType, + _nativeValueType, SingletonSeparatedList( VariableDeclarator(Identifier(context.GetAdditionalIdentifier(info, "ignored"))) .WithInitializer(EqualsValueClause( IdentifierName(subContext.GetIdentifiers(info).native))))), - GenerateNativeAssignmentFromValueProperty(info, context, subContext)); + GenerateToNativeValueInvocation(info, context, subContext)); } public IEnumerable GenerateSetupStatements(TypePositionInfo info, StubCodeContext context) { - var subContext = new CustomNativeTypeWithValuePropertyStubContext(context); + var subContext = new CustomNativeTypeWithToFromNativeValueContext(context); yield return LocalDeclarationStatement( VariableDeclaration( _innerMarshaller.AsNativeType(info), @@ -550,26 +553,25 @@ public IEnumerable GenerateSetupStatements(TypePositionInfo inf } } - private StatementSyntax GenerateValuePropertyAssignment(TypePositionInfo info, StubCodeContext context, CustomNativeTypeWithValuePropertyStubContext subContext) + private StatementSyntax GenerateFromNativeValueInvocation(TypePositionInfo info, StubCodeContext context, CustomNativeTypeWithToFromNativeValueContext subContext) { - // .Value = ; + // .FromNativeValue(); return ExpressionStatement( - AssignmentExpression( - SyntaxKind.SimpleAssignmentExpression, + InvocationExpression( MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, IdentifierName(subContext.GetIdentifiers(info).native), - IdentifierName(ManualTypeMarshallingHelper.ValuePropertyName)), - IdentifierName(context.GetIdentifiers(info).native))); + IdentifierName(ShapeMemberNames.Value.FromNativeValue)), + ArgumentList(SingletonSeparatedList(Argument(IdentifierName(context.GetIdentifiers(info).native)))))); } public IEnumerable GenerateUnmarshalStatements(TypePositionInfo info, StubCodeContext context) { - var subContext = new CustomNativeTypeWithValuePropertyStubContext(context); + var subContext = new CustomNativeTypeWithToFromNativeValueContext(context); if (info.IsManagedReturnPosition || (info.IsByRef && info.RefKind != RefKind.In)) { // .Value = ; - yield return GenerateValuePropertyAssignment(info, context, subContext); + yield return GenerateFromNativeValueInvocation(info, context, subContext); } foreach (StatementSyntax statement in _innerMarshaller.GenerateUnmarshalStatements(info, subContext)) @@ -580,7 +582,7 @@ public IEnumerable GenerateUnmarshalStatements(TypePositionInfo public IEnumerable GetNativeTypeConstructorArguments(TypePositionInfo info, StubCodeContext context) { - var subContext = new CustomNativeTypeWithValuePropertyStubContext(context); + var subContext = new CustomNativeTypeWithToFromNativeValueContext(context); return _innerMarshaller.GetNativeTypeConstructorArguments(info, subContext); } @@ -591,18 +593,16 @@ public bool UsesNativeIdentifier(TypePositionInfo info, StubCodeContext context) } /// - /// Marshaller that enables support for native types with the constructor variants that take a sizeOfElement int parameter and that have a SetUnmarshalledCollectionLength method. + /// Marshaller that enables support for native types with the constructor variants that take a sizeOfElement int parameter. /// - internal sealed class NumElementsExpressionMarshalling : ICustomNativeTypeMarshallingStrategy + internal sealed class SizeOfElementMarshalling : ICustomNativeTypeMarshallingStrategy { private readonly ICustomNativeTypeMarshallingStrategy _innerMarshaller; - private readonly ExpressionSyntax _numElementsExpression; private readonly ExpressionSyntax _sizeOfElementExpression; - public NumElementsExpressionMarshalling(ICustomNativeTypeMarshallingStrategy innerMarshaller, ExpressionSyntax numElementsExpression, ExpressionSyntax sizeOfElementExpression) + public SizeOfElementMarshalling(ICustomNativeTypeMarshallingStrategy innerMarshaller, ExpressionSyntax sizeOfElementExpression) { _innerMarshaller = innerMarshaller; - _numElementsExpression = numElementsExpression; _sizeOfElementExpression = sizeOfElementExpression; } @@ -659,17 +659,6 @@ private IEnumerable GenerateUnmarshallerCollectionInitializatio IdentifierName(marshalerIdentifier), ImplicitObjectCreationExpression().AddArgumentListArguments(Argument(_sizeOfElementExpression)))); } - - if (info.IsByRef || !info.ByValueContentsMarshalKind.HasFlag(ByValueContentsMarshalKind.Out)) - { - yield return ExpressionStatement( - InvocationExpression( - MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - IdentifierName(marshalerIdentifier), - IdentifierName(ManualTypeMarshallingHelper.SetUnmarshalledCollectionLengthMethodName))) - .AddArgumentListArguments(Argument(_numElementsExpression))); - } } public IEnumerable GenerateUnmarshalStatements(TypePositionInfo info, StubCodeContext context) @@ -712,11 +701,13 @@ internal sealed class LinearCollectionWithBlittableElementsMarshalling : ICustom { private readonly ICustomNativeTypeMarshallingStrategy _innerMarshaller; private readonly TypeSyntax _elementType; + private readonly ExpressionSyntax _numElementsExpression; - public LinearCollectionWithBlittableElementsMarshalling(ICustomNativeTypeMarshallingStrategy innerMarshaller, TypeSyntax elementType) + public LinearCollectionWithBlittableElementsMarshalling(ICustomNativeTypeMarshallingStrategy innerMarshaller, TypeSyntax elementType, ExpressionSyntax numElementsExpression) { _innerMarshaller = innerMarshaller; _elementType = elementType; + _numElementsExpression = numElementsExpression; } public ArgumentSyntax AsArgument(TypePositionInfo info, StubCodeContext context) @@ -748,15 +739,17 @@ public IEnumerable GenerateMarshalStatements(TypePositionInfo i yield break; } - // .ManagedValues.CopyTo(MemoryMarshal.Cast>(.NativeValueStorage)); + // .GetManagedValuesSource().CopyTo(MemoryMarshal.Cast>(.GetNativeValuesDestination())); yield return ExpressionStatement( InvocationExpression( MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, - MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - IdentifierName(nativeIdentifier), - IdentifierName(ManualTypeMarshallingHelper.ManagedValuesPropertyName)), + InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + IdentifierName(nativeIdentifier), + IdentifierName(ShapeMemberNames.LinearCollection.GetManagedValuesSource)), + ArgumentList()), IdentifierName("CopyTo"))) .AddArgumentListArguments( Argument( @@ -776,10 +769,12 @@ public IEnumerable GenerateMarshalStatements(TypePositionInfo i }))))) .AddArgumentListArguments( Argument( - MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - IdentifierName(nativeIdentifier), - IdentifierName(ManualTypeMarshallingHelper.NativeValueStoragePropertyName))))))); + InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + IdentifierName(nativeIdentifier), + IdentifierName(ShapeMemberNames.LinearCollection.GetNativeValuesDestination)), + ArgumentList())))))); } public IEnumerable GeneratePinStatements(TypePositionInfo info, StubCodeContext context) @@ -795,7 +790,13 @@ public IEnumerable GenerateSetupStatements(TypePositionInfo inf public IEnumerable GenerateUnmarshalStatements(TypePositionInfo info, StubCodeContext context) { string nativeIdentifier = context.GetIdentifiers(info).native; - // MemoryMarshal.Cast>(.NativeValueStorage).CopyTo(.ManagedValues); + string numElementsIdentifier = context.GetAdditionalIdentifier(info, "numElements"); + yield return LocalDeclarationStatement( + VariableDeclaration( + PredefinedType(Token(SyntaxKind.IntKeyword)), + SingletonSeparatedList( + VariableDeclarator(numElementsIdentifier).WithInitializer(EqualsValueClause(_numElementsExpression))))); + // MemoryMarshal.Cast>(.GetNativeValuesSource()).CopyTo(.GetManagedValuesDestination()); yield return ExpressionStatement( InvocationExpression( MemberAccessExpression( @@ -816,17 +817,21 @@ public IEnumerable GenerateUnmarshalStatements(TypePositionInfo }))))) .AddArgumentListArguments( Argument( - MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - IdentifierName(nativeIdentifier), - IdentifierName(ManualTypeMarshallingHelper.NativeValueStoragePropertyName)))), + InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + IdentifierName(nativeIdentifier), + IdentifierName(ShapeMemberNames.LinearCollection.GetNativeValuesSource)), + ArgumentList(SingletonSeparatedList(Argument(IdentifierName(numElementsIdentifier))))))), IdentifierName("CopyTo"))) .AddArgumentListArguments( Argument( - MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - IdentifierName(nativeIdentifier), - IdentifierName(ManualTypeMarshallingHelper.ManagedValuesPropertyName))))); + InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + IdentifierName(nativeIdentifier), + IdentifierName(ShapeMemberNames.LinearCollection.GetManagedValuesDestination)), + ArgumentList(SingletonSeparatedList(Argument(IdentifierName(numElementsIdentifier)))))))); foreach (StatementSyntax statement in _innerMarshaller.GenerateUnmarshalStatements(info, context)) { @@ -853,17 +858,20 @@ internal sealed class LinearCollectionWithNonBlittableElementsMarshalling : ICus private readonly ICustomNativeTypeMarshallingStrategy _innerMarshaller; private readonly IMarshallingGenerator _elementMarshaller; private readonly TypePositionInfo _elementInfo; + private readonly ExpressionSyntax _numElementsExpression; public LinearCollectionWithNonBlittableElementsMarshalling(ICustomNativeTypeMarshallingStrategy innerMarshaller, IMarshallingGenerator elementMarshaller, - TypePositionInfo elementInfo) + TypePositionInfo elementInfo, + ExpressionSyntax numElementsExpression) { _innerMarshaller = innerMarshaller; _elementMarshaller = elementMarshaller; _elementInfo = elementInfo; + _numElementsExpression = numElementsExpression; } - private LocalDeclarationStatementSyntax GenerateNativeSpanDeclaration(TypePositionInfo info, StubCodeContext context) + private LocalDeclarationStatementSyntax GenerateNativeValuesDestinationDeclaration(TypePositionInfo info, StubCodeContext context) { string nativeIdentifier = context.GetIdentifiers(info).native; string nativeSpanIdentifier = MarshallerHelpers.GetNativeSpanIdentifier(info, context); @@ -891,28 +899,105 @@ private LocalDeclarationStatementSyntax GenerateNativeSpanDeclaration(TypePositi _elementMarshaller.AsNativeType(_elementInfo).GetCompatibleGenericTypeParameterSyntax() }))))) .AddArgumentListArguments( - Argument(MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, + Argument( + InvocationExpression( + MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, + IdentifierName(nativeIdentifier), + IdentifierName(ShapeMemberNames.LinearCollection.GetNativeValuesDestination)), + ArgumentList())))))))); + } + + private LocalDeclarationStatementSyntax GenerateNativeValuesSourceDeclaration(TypePositionInfo info, StubCodeContext context, string numElementsIdentifier) + { + string nativeIdentifier = context.GetIdentifiers(info).native; + string nativeSpanIdentifier = MarshallerHelpers.GetNativeSpanIdentifier(info, context); + return LocalDeclarationStatement(VariableDeclaration( + GenericName( + Identifier(TypeNames.System_ReadOnlySpan), + TypeArgumentList( + SingletonSeparatedList(_elementMarshaller.AsNativeType(_elementInfo).GetCompatibleGenericTypeParameterSyntax())) + ), + SingletonSeparatedList( + VariableDeclarator(Identifier(nativeSpanIdentifier)) + .WithInitializer(EqualsValueClause( + InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + ParseTypeName(TypeNames.System_Runtime_InteropServices_MemoryMarshal), + GenericName( + Identifier("Cast")) + .WithTypeArgumentList( + TypeArgumentList( + SeparatedList( + new[] + { + PredefinedType(Token(SyntaxKind.ByteKeyword)), + _elementMarshaller.AsNativeType(_elementInfo).GetCompatibleGenericTypeParameterSyntax() + }))))) + .AddArgumentListArguments( + Argument( + InvocationExpression( + MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, + IdentifierName(nativeIdentifier), + IdentifierName(ShapeMemberNames.LinearCollection.GetNativeValuesSource)), + ArgumentList(SingletonSeparatedList(Argument(IdentifierName(numElementsIdentifier)))))))))))); + } + + private LocalDeclarationStatementSyntax GeneratedManagedValuesSourceDeclaration(TypePositionInfo info, StubCodeContext context) + { + string nativeIdentifier = context.GetIdentifiers(info).native; + string managedSpanIdentifier = MarshallerHelpers.GetManagedSpanIdentifier(info, context); + return LocalDeclarationStatement(VariableDeclaration( + GenericName( + Identifier(TypeNames.System_ReadOnlySpan), + TypeArgumentList( + SingletonSeparatedList(_elementInfo.ManagedType.Syntax)) + ), + SingletonSeparatedList( + VariableDeclarator(Identifier(managedSpanIdentifier)) + .WithInitializer(EqualsValueClause( + InvocationExpression( + MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, IdentifierName(nativeIdentifier), - IdentifierName(ManualTypeMarshallingHelper.NativeValueStoragePropertyName))))))))); + IdentifierName(ShapeMemberNames.LinearCollection.GetManagedValuesSource)), + ArgumentList())))))); } - private StatementSyntax GenerateContentsMarshallingStatement(TypePositionInfo info, StubCodeContext context, bool useManagedSpanForLength) + private LocalDeclarationStatementSyntax GeneratedManagedValuesDestinationDeclaration(TypePositionInfo info, StubCodeContext context, string numElementsIdentifier) { string nativeIdentifier = context.GetIdentifiers(info).native; + string managedSpanIdentifier = MarshallerHelpers.GetManagedSpanIdentifier(info, context); + return LocalDeclarationStatement(VariableDeclaration( + GenericName( + Identifier(TypeNames.System_Span), + TypeArgumentList( + SingletonSeparatedList(_elementInfo.ManagedType.Syntax)) + ), + SingletonSeparatedList( + VariableDeclarator(Identifier(managedSpanIdentifier)) + .WithInitializer(EqualsValueClause( + InvocationExpression( + MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, + IdentifierName(nativeIdentifier), + IdentifierName(ShapeMemberNames.LinearCollection.GetManagedValuesDestination)), + ArgumentList(SingletonSeparatedList(Argument(IdentifierName(numElementsIdentifier)))))))))); + } + + private StatementSyntax GenerateContentsMarshallingStatement(TypePositionInfo info, StubCodeContext context, ExpressionSyntax lengthExpression) + { + string managedSpanIdentifier = MarshallerHelpers.GetManagedSpanIdentifier(info, context); string nativeSpanIdentifier = MarshallerHelpers.GetNativeSpanIdentifier(info, context); var elementSetupSubContext = new LinearCollectionElementMarshallingCodeContext( StubCodeContext.Stage.Setup, + managedSpanIdentifier, nativeSpanIdentifier, context); var elementSubContext = new LinearCollectionElementMarshallingCodeContext( context.CurrentStage, + managedSpanIdentifier, nativeSpanIdentifier, context); - string collectionIdentifierForLength = useManagedSpanForLength - ? $"{nativeIdentifier}.{ManualTypeMarshallingHelper.ManagedValuesPropertyName}" - : nativeSpanIdentifier; - TypePositionInfo localElementInfo = _elementInfo with { InstanceIdentifier = info.InstanceIdentifier, @@ -936,10 +1021,8 @@ private StatementSyntax GenerateContentsMarshallingStatement(TypePositionInfo in } // Iterate through the elements of the native collection to unmarshal them - return Block( - GenerateNativeSpanDeclaration(info, context), - MarshallerHelpers.GetForLoop(collectionIdentifierForLength, elementSubContext.IndexerIdentifier) - .WithStatement(marshallingStatement)); + return MarshallerHelpers.GetForLoop(lengthExpression, elementSubContext.IndexerIdentifier) + .WithStatement(marshallingStatement); } return EmptyStatement(); } @@ -956,7 +1039,18 @@ public TypeSyntax AsNativeType(TypePositionInfo info) public IEnumerable GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context) { - yield return GenerateContentsMarshallingStatement(info, context, useManagedSpanForLength: false); + StatementSyntax contentsCleanupStatements = GenerateContentsMarshallingStatement(info, context, + MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, + IdentifierName(MarshallerHelpers.GetNativeSpanIdentifier(info, context)), + IdentifierName("Length"))); + + if (!contentsCleanupStatements.IsKind(SyntaxKind.EmptyStatement)) + { + yield return Block( + GenerateNativeValuesDestinationDeclaration(info, context), + contentsCleanupStatements); + } + foreach (StatementSyntax statement in _innerMarshaller.GenerateCleanupStatements(info, context)) { yield return statement; @@ -974,21 +1068,29 @@ public IEnumerable GenerateMarshalStatements(TypePositionInfo i { // If the parameter is marshalled by-value [Out], then we don't marshal the contents of the collection. // We do clear the span, so that if the invoke target doesn't fill it, we aren't left with undefined content. - // .NativeValueStorage.Clear(); + // .GetNativeValuesDestination().Clear(); string nativeIdentifier = context.GetIdentifiers(info).native; yield return ExpressionStatement( InvocationExpression( MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, - MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - IdentifierName(nativeIdentifier), - IdentifierName(ManualTypeMarshallingHelper.NativeValueStoragePropertyName)), + InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + IdentifierName(nativeIdentifier), + IdentifierName(ShapeMemberNames.LinearCollection.GetNativeValuesDestination)), + ArgumentList()), IdentifierName("Clear")))); yield break; } - yield return GenerateContentsMarshallingStatement(info, context, useManagedSpanForLength: true); + yield return Block( + GeneratedManagedValuesSourceDeclaration(info, context), + GenerateNativeValuesDestinationDeclaration(info, context), + GenerateContentsMarshallingStatement(info, context, + MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, + IdentifierName(MarshallerHelpers.GetManagedSpanIdentifier(info, context)), + IdentifierName("Length")))); } public IEnumerable GeneratePinStatements(TypePositionInfo info, StubCodeContext context) @@ -1001,9 +1103,99 @@ public IEnumerable GenerateSetupStatements(TypePositionInfo inf return _innerMarshaller.GenerateSetupStatements(info, context); } + private StatementSyntax GenerateByValueUnmarshalStatement(TypePositionInfo info, StubCodeContext context) + { + // Use ManagedSource and NativeDestination spans for by-value marshalling since we're just marshalling back the contents, + // not the array itself. + // This code is ugly since we're now enforcing readonly safety with ReadOnlySpan for all other scenarios, + // but this is an uncommon case so we don't want to design the API around enabling just it. + var (_, nativeIdentifier) = context.GetIdentifiers(info); + string numElementsIdentifier = context.GetAdditionalIdentifier(info, "numElements"); + LocalDeclarationStatementSyntax numElementsDeclaration = LocalDeclarationStatement( + VariableDeclaration( + PredefinedType(Token(SyntaxKind.IntKeyword)), + SingletonSeparatedList( + VariableDeclarator(numElementsIdentifier).WithInitializer(EqualsValueClause( + MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, + InvocationExpression( + MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, + IdentifierName(nativeIdentifier), + IdentifierName(ShapeMemberNames.LinearCollection.GetManagedValuesSource)), + ArgumentList()), + IdentifierName("Length"))))))); + + string managedSpanIdentifier = MarshallerHelpers.GetManagedSpanIdentifier(info, context); + LocalDeclarationStatementSyntax managedValuesDeclaration = LocalDeclarationStatement(VariableDeclaration( + GenericName( + Identifier(TypeNames.System_Span), + TypeArgumentList( + SingletonSeparatedList(_elementInfo.ManagedType.Syntax)) + ), + SingletonSeparatedList(VariableDeclarator(managedSpanIdentifier).WithInitializer(EqualsValueClause( + InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + ParseName(TypeNames.System_Runtime_InteropServices_MemoryMarshal), + IdentifierName("CreateSpan"))) + .WithArgumentList( + ArgumentList( + SeparatedList( + new[] + { + Argument( + InvocationExpression( + MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, + ParseName(TypeNames.System_Runtime_CompilerServices_Unsafe), + IdentifierName("AsRef")), + ArgumentList(SingletonSeparatedList( + Argument( + InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + InvocationExpression( + MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, + IdentifierName(nativeIdentifier), + IdentifierName(ShapeMemberNames.LinearCollection.GetManagedValuesSource)), + ArgumentList()), + IdentifierName("GetPinnableReference")), + ArgumentList())) + .WithRefKindKeyword( + Token(SyntaxKind.InKeyword)))))) + .WithRefKindKeyword( + Token(SyntaxKind.RefKeyword)), + Argument( + IdentifierName(numElementsIdentifier)) + })))))))); + + LocalDeclarationStatementSyntax nativeValuesDeclaration = GenerateNativeValuesDestinationDeclaration(info, context); + + return Block(numElementsDeclaration, + managedValuesDeclaration, + nativeValuesDeclaration, + GenerateContentsMarshallingStatement(info, context, + IdentifierName(numElementsIdentifier))); + } + public IEnumerable GenerateUnmarshalStatements(TypePositionInfo info, StubCodeContext context) { - yield return GenerateContentsMarshallingStatement(info, context, useManagedSpanForLength: false); + string numElementsIdentifier = context.GetAdditionalIdentifier(info, "numElements"); + if (!info.IsByRef && info.ByValueContentsMarshalKind.HasFlag(ByValueContentsMarshalKind.Out)) + { + yield return GenerateByValueUnmarshalStatement(info, context); + } + else + { + yield return Block(LocalDeclarationStatement( + VariableDeclaration( + PredefinedType(Token(SyntaxKind.IntKeyword)), + SingletonSeparatedList( + VariableDeclarator(numElementsIdentifier).WithInitializer(EqualsValueClause(_numElementsExpression))))), + GeneratedManagedValuesDestinationDeclaration(info, context, numElementsIdentifier), + GenerateNativeValuesSourceDeclaration(info, context, numElementsIdentifier), + GenerateContentsMarshallingStatement(info, context, + IdentifierName(numElementsIdentifier))); + } + foreach (StatementSyntax statement in _innerMarshaller.GenerateUnmarshalStatements(info, context)) { yield return statement; @@ -1050,6 +1242,16 @@ public override SyntaxNode VisitAssignmentExpression(AssignmentExpressionSyntax return node; } + + public override SyntaxNode? VisitArgument(ArgumentSyntax node) + { + if (node.Expression.ToString() == _nativeIdentifier) + { + return node.WithExpression( + CastExpression(_nativeType, node.Expression)); + } + return node; + } } } } diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs index f6e012c9ef3a0..dc1a04c698110 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs @@ -22,9 +22,9 @@ public static class MarshallerHelpers public static readonly TypeSyntax SystemIntPtrType = ParseTypeName(TypeNames.System_IntPtr); - public static ForStatementSyntax GetForLoop(string collectionIdentifier, string indexerIdentifier) + public static ForStatementSyntax GetForLoop(ExpressionSyntax lengthExpression, string indexerIdentifier) { - // for(int = 0; < .Length; ++) + // for(int = 0; < ; ++) // ; return ForStatement(EmptyStatement()) .WithDeclaration( @@ -32,7 +32,7 @@ public static ForStatementSyntax GetForLoop(string collectionIdentifier, string PredefinedType( Token(SyntaxKind.IntKeyword))) .WithVariables( - SingletonSeparatedList( + SingletonSeparatedList( VariableDeclarator( Identifier(indexerIdentifier)) .WithInitializer( @@ -44,10 +44,7 @@ public static ForStatementSyntax GetForLoop(string collectionIdentifier, string BinaryExpression( SyntaxKind.LessThanExpression, IdentifierName(indexerIdentifier), - MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - IdentifierName(collectionIdentifier), - IdentifierName("Length")))) + lengthExpression)) .WithIncrementors( SingletonSeparatedList( PrefixUnaryExpression( @@ -102,6 +99,11 @@ public static string GetMarshallerIdentifier(TypePositionInfo info, StubCodeCont return context.GetAdditionalIdentifier(info, "marshaller"); } + public static string GetManagedSpanIdentifier(TypePositionInfo info, StubCodeContext context) + { + return context.GetAdditionalIdentifier(info, "managedSpan"); + } + public static string GetNativeSpanIdentifier(TypePositionInfo info, StubCodeContext context) { return context.GetAdditionalIdentifier(info, "nativeSpan"); 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 79caded6111e0..ffbb72dd3e079 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 @@ -98,15 +98,29 @@ public sealed record MarshalAsInfo( public sealed record UnmanagedBlittableMarshallingInfo : MarshallingInfo; [Flags] - public enum CustomMarshallingFeatures + public enum CustomTypeMarshallerDirection { None = 0, - ManagedToNative = 0x1, - NativeToManaged = 0x2, - ManagedToNativeStackalloc = 0x4, - ManagedTypePinning = 0x8, - NativeTypePinning = 0x10, - FreeNativeResources = 0x20, + In = 0x1, + Out = 0x2, + Ref = In | Out, + } + + [Flags] + public enum CustomTypeMarshallerFeatures + { + None = 0, + UnmanagedResources = 0x20, + CallerAllocatedBuffer = 0x4, + TwoStageMarshalling = 0x8 + } + + [Flags] + public enum CustomTypeMarshallerPinning + { + None = 0, + ManagedType = 0x1, + NativeType = 0x2 } public abstract record CountInfo @@ -142,8 +156,10 @@ public sealed record SizeAndParamIndexInfo(int ConstSize, TypePositionInfo? Para /// public record NativeMarshallingAttributeInfo( ManagedTypeInfo NativeMarshallingType, - ManagedTypeInfo? ValuePropertyType, - CustomMarshallingFeatures MarshallingFeatures, + ManagedTypeInfo? NativeValueType, + CustomTypeMarshallerDirection Direction, + CustomTypeMarshallerFeatures MarshallingFeatures, + CustomTypeMarshallerPinning PinningFeatures, bool UseDefaultMarshalling, int? BufferSize) : MarshallingInfo; @@ -165,16 +181,20 @@ public sealed record SafeHandleMarshallingInfo(bool AccessibleDefaultConstructor /// public sealed record NativeLinearCollectionMarshallingInfo( ManagedTypeInfo NativeMarshallingType, - ManagedTypeInfo? ValuePropertyType, - CustomMarshallingFeatures MarshallingFeatures, + ManagedTypeInfo? NativeValueType, + CustomTypeMarshallerDirection Direction, + CustomTypeMarshallerFeatures MarshallingFeatures, + CustomTypeMarshallerPinning PinningFeatures, bool UseDefaultMarshalling, int? BufferSize, CountInfo ElementCountInfo, ManagedTypeInfo ElementType, MarshallingInfo ElementMarshallingInfo) : NativeMarshallingAttributeInfo( NativeMarshallingType, - ValuePropertyType, + NativeValueType, + Direction, MarshallingFeatures, + PinningFeatures, UseDefaultMarshalling, BufferSize ); @@ -226,36 +246,36 @@ private MarshallingInfo ParseMarshallingInfo( IEnumerable useSiteAttributes, ImmutableHashSet inspectedElements) { - Dictionary marshallingAttributesByIndirectionLevel = new(); + Dictionary marshallingAttributesByIndirectionDepth = new(); int maxIndirectionLevelDataProvided = 0; foreach (AttributeData attribute in useSiteAttributes) { if (TryGetAttributeIndirectionLevel(attribute, out int indirectionLevel)) { - if (marshallingAttributesByIndirectionLevel.ContainsKey(indirectionLevel)) + if (marshallingAttributesByIndirectionDepth.ContainsKey(indirectionLevel)) { _diagnostics.ReportInvalidMarshallingAttributeInfo(attribute, nameof(Resources.DuplicateMarshallingInfo), indirectionLevel.ToString()); return NoMarshallingInfo.Instance; } - marshallingAttributesByIndirectionLevel.Add(indirectionLevel, attribute); + marshallingAttributesByIndirectionDepth.Add(indirectionLevel, attribute); maxIndirectionLevelDataProvided = Math.Max(maxIndirectionLevelDataProvided, indirectionLevel); } } - int maxIndirectionLevelUsed = 0; + int maxIndirectionDepthUsed = 0; MarshallingInfo info = GetMarshallingInfo( managedType, - marshallingAttributesByIndirectionLevel, + marshallingAttributesByIndirectionDepth, indirectionLevel: 0, inspectedElements, - ref maxIndirectionLevelUsed); - if (maxIndirectionLevelUsed < maxIndirectionLevelDataProvided) + ref maxIndirectionDepthUsed); + if (maxIndirectionDepthUsed < maxIndirectionLevelDataProvided) { _diagnostics.ReportInvalidMarshallingAttributeInfo( - marshallingAttributesByIndirectionLevel[maxIndirectionLevelDataProvided], + marshallingAttributesByIndirectionDepth[maxIndirectionLevelDataProvided], nameof(Resources.ExtraneousMarshallingInfo), maxIndirectionLevelDataProvided.ToString(), - maxIndirectionLevelUsed.ToString()); + maxIndirectionDepthUsed.ToString()); } return info; } @@ -265,9 +285,9 @@ private MarshallingInfo GetMarshallingInfo( Dictionary useSiteAttributes, int indirectionLevel, ImmutableHashSet inspectedElements, - ref int maxIndirectionLevelUsed) + ref int maxIndirectionDepthUsed) { - maxIndirectionLevelUsed = Math.Max(indirectionLevel, maxIndirectionLevelUsed); + maxIndirectionDepthUsed = Math.Max(indirectionLevel, maxIndirectionDepthUsed); CountInfo parsedCountInfo = NoCountInfo.Instance; if (useSiteAttributes.TryGetValue(indirectionLevel, out AttributeData useSiteAttribute)) @@ -278,7 +298,7 @@ private MarshallingInfo GetMarshallingInfo( && SymbolEqualityComparer.Default.Equals(_compilation.GetTypeByMetadataName(TypeNames.System_Runtime_InteropServices_MarshalAsAttribute), attributeClass)) { // https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.marshalasattribute - return CreateInfoFromMarshalAs(type, useSiteAttribute, inspectedElements, ref maxIndirectionLevelUsed); + return CreateInfoFromMarshalAs(type, useSiteAttribute, inspectedElements, ref maxIndirectionDepthUsed); } else if (SymbolEqualityComparer.Default.Equals(_compilation.GetTypeByMetadataName(TypeNames.MarshalUsingAttribute), attributeClass)) { @@ -299,7 +319,7 @@ private MarshallingInfo GetMarshallingInfo( parsedCountInfo, useSiteAttributes, inspectedElements, - ref maxIndirectionLevelUsed); + ref maxIndirectionDepthUsed); } } } @@ -321,7 +341,7 @@ private MarshallingInfo GetMarshallingInfo( parsedCountInfo, useSiteAttributes, inspectedElements, - ref maxIndirectionLevelUsed); + ref maxIndirectionDepthUsed); } else if (attributeClass.ToDisplayString() == TypeNames.GeneratedMarshallingAttribute) { @@ -337,7 +357,7 @@ private MarshallingInfo GetMarshallingInfo( indirectionLevel, useSiteAttributes, inspectedElements, - ref maxIndirectionLevelUsed, + ref maxIndirectionDepthUsed, out MarshallingInfo infoMaybe)) { return infoMaybe; @@ -470,7 +490,7 @@ private MarshallingInfo CreateInfoFromMarshalAs( ITypeSymbol type, AttributeData attrData, ImmutableHashSet inspectedElements, - ref int maxIndirectionLevelUsed) + ref int maxIndirectionDepthUsed) { object unmanagedTypeObj = attrData.ConstructorArguments[0].Value!; UnmanagedType unmanagedType = unmanagedTypeObj is short unmanagedTypeAsShort @@ -550,8 +570,8 @@ private MarshallingInfo CreateInfoFromMarshalAs( } else { - maxIndirectionLevelUsed = 1; - elementMarshallingInfo = GetMarshallingInfo(elementType, new Dictionary(), 1, ImmutableHashSet.Empty, ref maxIndirectionLevelUsed); + maxIndirectionDepthUsed = 1; + elementMarshallingInfo = GetMarshallingInfo(elementType, new Dictionary(), 1, ImmutableHashSet.Empty, ref maxIndirectionDepthUsed); } INamedTypeSymbol? arrayMarshaller; @@ -575,12 +595,14 @@ private MarshallingInfo CreateInfoFromMarshalAs( Debug.Assert(customTypeMarshallerData is not null); - ITypeSymbol? valuePropertyType = ManualTypeMarshallingHelper.FindValueProperty(arrayMarshaller)?.Type; + ITypeSymbol? nativeValueType = ManualTypeMarshallingHelper.FindToNativeValueMethod(arrayMarshaller)?.ReturnType; return new NativeLinearCollectionMarshallingInfo( NativeMarshallingType: ManagedTypeInfo.CreateTypeInfoForTypeSymbol(arrayMarshaller), - ValuePropertyType: valuePropertyType is not null ? ManagedTypeInfo.CreateTypeInfoForTypeSymbol(valuePropertyType) : null, - MarshallingFeatures: ~CustomMarshallingFeatures.ManagedTypePinning, + NativeValueType: nativeValueType is not null ? ManagedTypeInfo.CreateTypeInfoForTypeSymbol(nativeValueType) : null, + Direction: CustomTypeMarshallerDirection.Ref, + MarshallingFeatures: CustomTypeMarshallerFeatures.TwoStageMarshalling | CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.CallerAllocatedBuffer, + PinningFeatures: CustomTypeMarshallerPinning.NativeType, UseDefaultMarshalling: true, BufferSize: customTypeMarshallerData.Value.BufferSize, ElementCountInfo: arraySizeInfo, @@ -597,16 +619,13 @@ private MarshallingInfo CreateNativeMarshallingInfo( CountInfo parsedCountInfo, Dictionary useSiteAttributes, ImmutableHashSet inspectedElements, - ref int maxIndirectionLevelUsed) + ref int maxIndirectionDepthUsed) { - CustomMarshallingFeatures features = CustomMarshallingFeatures.None; + INamedTypeSymbol spanOfT = _compilation.GetTypeByMetadataName(TypeNames.System_Span_Metadata)!; + ITypeSymbol spanOfByte = spanOfT.Construct(_compilation.GetSpecialType(SpecialType.System_Byte)); - if (!isMarshalUsingAttribute && ManualTypeMarshallingHelper.FindGetPinnableReference(type) is not null) - { - features |= CustomMarshallingFeatures.ManagedTypePinning; - } - - ITypeSymbol spanOfByte = _compilation.GetTypeByMetadataName(TypeNames.System_Span_Metadata)!.Construct(_compilation.GetSpecialType(SpecialType.System_Byte)); + INamedTypeSymbol readOnlySpanOfT = _compilation.GetTypeByMetadataName(TypeNames.System_ReadOnlySpan_Metadata)!; + ITypeSymbol readOnlySpanOfByte = readOnlySpanOfT.Construct(_compilation.GetSpecialType(SpecialType.System_Byte)); if (nativeType.IsUnboundGenericType) { @@ -639,19 +658,26 @@ private MarshallingInfo CreateNativeMarshallingInfo( { return NoMarshallingInfo.Instance; } - IPropertySymbol? valueProperty = ManualTypeMarshallingHelper.FindValueProperty(nativeType); + + CustomTypeMarshallerDirection direction = CustomTypeMarshallerDirection.None; + CustomTypeMarshallerPinning pinning = CustomTypeMarshallerPinning.None; + CustomTypeMarshallerFeatures features = CustomTypeMarshallerFeatures.None; + + if (!isMarshalUsingAttribute && ManualTypeMarshallingHelper.FindGetPinnableReference(type) is not null) + { + pinning |= CustomTypeMarshallerPinning.ManagedType; + } bool hasInt32Constructor = false; foreach (IMethodSymbol ctor in nativeType.Constructors) { - if (ManualTypeMarshallingHelper.IsManagedToNativeConstructor(ctor, type, customTypeMarshallerData.Value.Kind) && (valueProperty is null or { GetMethod: not null })) + if (ManualTypeMarshallingHelper.IsManagedToNativeConstructor(ctor, type, customTypeMarshallerData.Value.Kind)) { - features |= CustomMarshallingFeatures.ManagedToNative; + direction |= CustomTypeMarshallerDirection.In; } - else if (ManualTypeMarshallingHelper.IsCallerAllocatedSpanConstructor(ctor, type, spanOfByte, customTypeMarshallerData.Value.Kind) - && (valueProperty is null or { GetMethod: not null })) + else if (ManualTypeMarshallingHelper.IsCallerAllocatedSpanConstructor(ctor, type, spanOfByte, customTypeMarshallerData.Value.Kind)) { - features |= CustomMarshallingFeatures.ManagedToNativeStackalloc; + features |= CustomTypeMarshallerFeatures.CallerAllocatedBuffer; } else if (ctor.Parameters.Length == 1 && ctor.Parameters[0].Type.SpecialType == SpecialType.System_Int32) { @@ -662,14 +688,13 @@ private MarshallingInfo CreateNativeMarshallingInfo( // The constructor that takes only the native element size is required for collection marshallers // in the native-to-managed scenario. if ((customTypeMarshallerData.Value.Kind != CustomTypeMarshallerKind.LinearCollection - || (hasInt32Constructor && ManualTypeMarshallingHelper.HasSetUnmarshalledCollectionLengthMethod(nativeType))) - && ManualTypeMarshallingHelper.HasToManagedMethod(nativeType, type) - && (valueProperty is null or { SetMethod: not null })) + || hasInt32Constructor) + && ManualTypeMarshallingHelper.HasToManagedMethod(nativeType, type)) { - features |= CustomMarshallingFeatures.NativeToManaged; + direction |= CustomTypeMarshallerDirection.Out; } - if (features == CustomMarshallingFeatures.None) + if (direction == CustomTypeMarshallerDirection.None) { _diagnostics.ReportInvalidMarshallingAttributeInfo( attrData, @@ -682,23 +707,35 @@ private MarshallingInfo CreateNativeMarshallingInfo( if (ManualTypeMarshallingHelper.HasFreeNativeMethod(nativeType)) { - features |= CustomMarshallingFeatures.FreeNativeResources; + features |= CustomTypeMarshallerFeatures.UnmanagedResources; } if (ManualTypeMarshallingHelper.FindGetPinnableReference(nativeType) is not null) { - features |= CustomMarshallingFeatures.NativeTypePinning; + pinning |= CustomTypeMarshallerPinning.NativeType; } + IMethodSymbol? toNativeValueMethod = ManualTypeMarshallingHelper.FindToNativeValueMethod(nativeType); + if (customTypeMarshallerData.Value.Kind == CustomTypeMarshallerKind.LinearCollection) { - if (!ManualTypeMarshallingHelper.HasNativeValueStorageProperty(nativeType, spanOfByte)) + if (direction.HasFlag(CustomTypeMarshallerDirection.In) + && (ManualTypeMarshallingHelper.FindGetManagedValuesSourceMethod(nativeType, readOnlySpanOfT) is null + || ManualTypeMarshallingHelper.FindGetNativeValuesDestinationMethod(nativeType, spanOfByte) is null)) + { + _diagnostics.ReportInvalidMarshallingAttributeInfo(attrData, nameof(Resources.CollectionNativeTypeMustHaveRequiredShapeMessage), nativeType.ToDisplayString()); + return NoMarshallingInfo.Instance; + } + + if (direction.HasFlag(CustomTypeMarshallerDirection.Out) + && (ManualTypeMarshallingHelper.FindGetManagedValuesDestinationMethod(nativeType, spanOfT) is null + || ManualTypeMarshallingHelper.FindGetNativeValuesSourceMethod(nativeType, readOnlySpanOfByte) is null)) { _diagnostics.ReportInvalidMarshallingAttributeInfo(attrData, nameof(Resources.CollectionNativeTypeMustHaveRequiredShapeMessage), nativeType.ToDisplayString()); return NoMarshallingInfo.Instance; } - if (!ManualTypeMarshallingHelper.TryGetElementTypeFromLinearCollectionMarshaller(nativeType, out ITypeSymbol elementType)) + if (!ManualTypeMarshallingHelper.TryGetElementTypeFromLinearCollectionMarshaller(nativeType, readOnlySpanOfT, out ITypeSymbol elementType)) { _diagnostics.ReportInvalidMarshallingAttributeInfo(attrData, nameof(Resources.CollectionNativeTypeMustHaveRequiredShapeMessage), nativeType.ToDisplayString()); return NoMarshallingInfo.Instance; @@ -706,19 +743,23 @@ private MarshallingInfo CreateNativeMarshallingInfo( return new NativeLinearCollectionMarshallingInfo( ManagedTypeInfo.CreateTypeInfoForTypeSymbol(nativeType), - valueProperty is not null ? ManagedTypeInfo.CreateTypeInfoForTypeSymbol(valueProperty.Type) : null, + toNativeValueMethod is not null ? ManagedTypeInfo.CreateTypeInfoForTypeSymbol(toNativeValueMethod.ReturnType) : null, + direction, features, + pinning, UseDefaultMarshalling: !isMarshalUsingAttribute, customTypeMarshallerData.Value.BufferSize, parsedCountInfo, ManagedTypeInfo.CreateTypeInfoForTypeSymbol(elementType), - GetMarshallingInfo(elementType, useSiteAttributes, indirectionLevel + 1, inspectedElements, ref maxIndirectionLevelUsed)); + GetMarshallingInfo(elementType, useSiteAttributes, indirectionLevel + 1, inspectedElements, ref maxIndirectionDepthUsed)); } return new NativeMarshallingAttributeInfo( ManagedTypeInfo.CreateTypeInfoForTypeSymbol(nativeType), - valueProperty is not null ? ManagedTypeInfo.CreateTypeInfoForTypeSymbol(valueProperty.Type) : null, + toNativeValueMethod is not null ? ManagedTypeInfo.CreateTypeInfoForTypeSymbol(toNativeValueMethod.ReturnType) : null, + direction, features, + pinning, UseDefaultMarshalling: !isMarshalUsingAttribute, customTypeMarshallerData.Value.BufferSize); } @@ -729,7 +770,7 @@ private bool TryCreateTypeBasedMarshallingInfo( int indirectionLevel, Dictionary useSiteAttributes, ImmutableHashSet inspectedElements, - ref int maxIndirectionLevelUsed, + ref int maxIndirectionDepthUsed, out MarshallingInfo marshallingInfo) { // Check for an implicit SafeHandle conversion. @@ -775,22 +816,24 @@ private bool TryCreateTypeBasedMarshallingInfo( if (arrayMarshaller is null) { // If the array marshaler type is not available, then we cannot marshal arrays but indicate it is missing. - marshallingInfo = new MissingSupportCollectionMarshallingInfo(parsedCountInfo, GetMarshallingInfo(elementType, useSiteAttributes, indirectionLevel + 1, inspectedElements, ref maxIndirectionLevelUsed)); + marshallingInfo = new MissingSupportCollectionMarshallingInfo(parsedCountInfo, GetMarshallingInfo(elementType, useSiteAttributes, indirectionLevel + 1, inspectedElements, ref maxIndirectionDepthUsed)); return true; } var (_, _, customTypeMarshallerData) = ManualTypeMarshallingHelper.GetMarshallerShapeInfo(arrayMarshaller); - ITypeSymbol? valuePropertyType = ManualTypeMarshallingHelper.FindValueProperty(arrayMarshaller)?.Type; + ITypeSymbol? valuePropertyType = ManualTypeMarshallingHelper.FindToNativeValueMethod(arrayMarshaller)?.ReturnType; marshallingInfo = new NativeLinearCollectionMarshallingInfo( NativeMarshallingType: ManagedTypeInfo.CreateTypeInfoForTypeSymbol(arrayMarshaller), - ValuePropertyType: valuePropertyType is not null ? ManagedTypeInfo.CreateTypeInfoForTypeSymbol(valuePropertyType) : null, - MarshallingFeatures: ~CustomMarshallingFeatures.ManagedTypePinning, + NativeValueType: valuePropertyType is not null ? ManagedTypeInfo.CreateTypeInfoForTypeSymbol(valuePropertyType) : null, + Direction: CustomTypeMarshallerDirection.Ref, + MarshallingFeatures: CustomTypeMarshallerFeatures.TwoStageMarshalling | CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.CallerAllocatedBuffer, + PinningFeatures: CustomTypeMarshallerPinning.NativeType, UseDefaultMarshalling: true, customTypeMarshallerData.Value.BufferSize, ElementCountInfo: parsedCountInfo, ElementType: ManagedTypeInfo.CreateTypeInfoForTypeSymbol(elementType), - ElementMarshallingInfo: GetMarshallingInfo(elementType, useSiteAttributes, indirectionLevel + 1, inspectedElements, ref maxIndirectionLevelUsed)); + ElementMarshallingInfo: GetMarshallingInfo(elementType, useSiteAttributes, indirectionLevel + 1, inspectedElements, ref maxIndirectionDepthUsed)); return true; } @@ -803,7 +846,7 @@ private bool TryCreateTypeBasedMarshallingInfo( AttributeData attrData = _contextSymbol is IMethodSymbol ? _contextSymbol.GetAttributes().First(a => a.AttributeClass.ToDisplayString() == TypeNames.GeneratedDllImportAttribute) : default; - marshallingInfo = CreateNativeMarshallingInfo(type, _defaultInfo.StringMarshallingCustomType, attrData, true, indirectionLevel, parsedCountInfo, useSiteAttributes, inspectedElements, ref maxIndirectionLevelUsed); + marshallingInfo = CreateNativeMarshallingInfo(type, _defaultInfo.StringMarshallingCustomType, attrData, true, indirectionLevel, parsedCountInfo, useSiteAttributes, inspectedElements, ref maxIndirectionDepthUsed); return true; } 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 a542f34e72174..568acc00cc7ba 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,10 +1,11 @@ - + netstandard2.0 enable Microsoft.Interop true + DLLIMPORT_GENERATOR_TEST @@ -12,6 +13,8 @@ DependentUpon="Resources.resx" DesignTime="True" AutoGen="True" /> + 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 295ae5e49cce2..06de51acd1675 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 @@ -26,6 +26,8 @@ public static class TypeNames public const string UnmanagedCallConvAttribute = "System.Runtime.InteropServices.UnmanagedCallConvAttribute"; public const string System_Span_Metadata = "System.Span`1"; public const string System_Span = "System.Span"; + public const string System_ReadOnlySpan_Metadata = "System.ReadOnlySpan`1"; + public const string System_ReadOnlySpan = "System.ReadOnlySpan"; public const string System_IntPtr = "System.IntPtr"; diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypeSymbolExtensions.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypeSymbolExtensions.cs index c03e880f124b6..bb10cf31c122d 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypeSymbolExtensions.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypeSymbolExtensions.cs @@ -28,16 +28,7 @@ private static bool HasOnlyBlittableFields(this ITypeSymbol type, ImmutableHashS { if (!field.IsStatic) { - bool fieldBlittable = field switch - { - // Assume that type parameters that can be blittable are blittable. - // We'll re-evaluate blittability for generic fields of generic types at instantation time. - { Type: ITypeParameterSymbol { IsReferenceType: false } } => true, - { Type.IsUnmanagedType: false } => false, - _ => IsConsideredBlittable(field.Type, seenTypes.Add(type)) - }; - - if (!fieldBlittable) + if (!IsConsideredBlittable(field.Type, seenTypes.Add(type))) { return false; } @@ -51,6 +42,12 @@ private static bool HasOnlyBlittableFields(this ITypeSymbol type, ImmutableHashS private static bool IsConsideredBlittable(this ITypeSymbol type, ImmutableHashSet seenTypes) { + // Assume that type parameters that can be blittable are blittable. + // We'll re-evaluate blittability for generic fields of generic types at instantation time. + if (type.TypeKind == TypeKind.TypeParameter && !type.IsReferenceType) + { + return true; + } if (!type.IsUnmanagedType || type.IsAutoLayout()) { return false; diff --git a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/Ancillary.Interop.csproj b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/Ancillary.Interop.csproj index 8c35cd9ad018f..dde67835461d4 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/Ancillary.Interop.csproj +++ b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/Ancillary.Interop.csproj @@ -13,6 +13,7 @@ + diff --git a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/SpanMarshallers.cs b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/SpanMarshallers.cs index 7db20c212f382..de6a586b406b6 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/SpanMarshallers.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/SpanMarshallers.cs @@ -9,7 +9,7 @@ namespace System.Runtime.InteropServices.GeneratedMarshalling // Stack-alloc threshold set to 256 bytes to enable small arrays to be passed on the stack. // Number kept small to ensure that P/Invokes with a lot of array parameters doesn't // blow the stack since this is a new optimization in the code-generated interop. - [CustomTypeMarshaller(typeof(ReadOnlySpan<>), CustomTypeMarshallerKind.LinearCollection, BufferSize = 0x200, RequiresStackBuffer = true)] + [CustomTypeMarshaller(typeof(ReadOnlySpan<>), CustomTypeMarshallerKind.LinearCollection, BufferSize = 0x200)] public unsafe ref struct ReadOnlySpanMarshaller { private ReadOnlySpan _managedSpan; @@ -50,39 +50,15 @@ public ReadOnlySpanMarshaller(ReadOnlySpan managed, Span stackSpace, in } } - public Span ManagedValues => MemoryMarshal.CreateSpan(ref MemoryMarshal.GetReference(_managedSpan), _managedSpan.Length); + public ReadOnlySpan GetManagedValuesSource() => _managedSpan; - public Span NativeValueStorage { get; private set; } + public Span GetNativeValuesDestination() => NativeValueStorage; - public ref byte GetPinnableReference() => ref MemoryMarshal.GetReference(NativeValueStorage); - - public void SetUnmarshalledCollectionLength(int length) - { - _managedSpan = new T[length]; - } + private Span NativeValueStorage { get; } - public byte* Value - { - get - { - return (byte*)Unsafe.AsPointer(ref GetPinnableReference()); - } - set - { - if (value == null) - { - _managedSpan = null; - NativeValueStorage = default; - } - else - { - _allocatedMemory = (IntPtr)value; - NativeValueStorage = new Span(value, _managedSpan.Length * _sizeOfNativeElement); - } - } - } + public ref byte GetPinnableReference() => ref MemoryMarshal.GetReference(NativeValueStorage); - public ReadOnlySpan ToManaged() => _managedSpan; + public byte* ToNativeValue() => (byte*)Unsafe.AsPointer(ref GetPinnableReference()); public void FreeNative() { @@ -90,60 +66,69 @@ public void FreeNative() } } - [CustomTypeMarshaller(typeof(Span<>), CustomTypeMarshallerKind.LinearCollection, BufferSize = 0x200, RequiresStackBuffer = true)] + [CustomTypeMarshaller(typeof(Span<>), CustomTypeMarshallerKind.LinearCollection, BufferSize = 0x200)] public unsafe ref struct SpanMarshaller { - private ReadOnlySpanMarshaller _inner; + private Span _managedSpan; + private readonly int _sizeOfNativeElement; + private IntPtr _allocatedMemory; public SpanMarshaller(int sizeOfNativeElement) : this() { - _inner = new ReadOnlySpanMarshaller(sizeOfNativeElement); + _sizeOfNativeElement = sizeOfNativeElement; } public SpanMarshaller(Span managed, int sizeOfNativeElement) + :this(managed, Span.Empty, sizeOfNativeElement) { - _inner = new ReadOnlySpanMarshaller(managed, sizeOfNativeElement); } public SpanMarshaller(Span managed, Span stackSpace, int sizeOfNativeElement) { - _inner = new ReadOnlySpanMarshaller(managed, stackSpace, sizeOfNativeElement); - } - - public Span ManagedValues => _inner.ManagedValues; - - public Span NativeValueStorage - { - get => _inner.NativeValueStorage; + _allocatedMemory = default; + _sizeOfNativeElement = sizeOfNativeElement; + if (managed.Length == 0) + { + _managedSpan = default; + NativeValueStorage = default; + return; + } + _managedSpan = managed; + int spaceToAllocate = managed.Length * sizeOfNativeElement; + if (spaceToAllocate <= stackSpace.Length) + { + NativeValueStorage = stackSpace[0..spaceToAllocate]; + } + else + { + _allocatedMemory = Marshal.AllocCoTaskMem(spaceToAllocate); + NativeValueStorage = new Span((void*)_allocatedMemory, spaceToAllocate); + } } - public ref byte GetPinnableReference() => ref _inner.GetPinnableReference(); - - public void SetUnmarshalledCollectionLength(int length) - { - _inner.SetUnmarshalledCollectionLength(length); - } + private Span NativeValueStorage { get; set; } - public byte* Value - { - get => _inner.Value; - set => _inner.Value = value; - } + public ReadOnlySpan GetManagedValuesSource() => _managedSpan; + public Span GetManagedValuesDestination(int length) => _managedSpan = new T[length]; + public Span GetNativeValuesDestination() => NativeValueStorage; + public ReadOnlySpan GetNativeValuesSource(int length) => NativeValueStorage = new Span((void*)_allocatedMemory, length * _sizeOfNativeElement); + public ref byte GetPinnableReference() => ref NativeValueStorage.GetPinnableReference(); + public byte* ToNativeValue() => (byte*)Unsafe.AsPointer(ref GetPinnableReference()); + public void FromNativeValue(byte* value) => _allocatedMemory = (IntPtr)value; public Span ToManaged() { - ReadOnlySpan managedInner = _inner.ToManaged(); - return MemoryMarshal.CreateSpan(ref MemoryMarshal.GetReference(managedInner), managedInner.Length); + return _managedSpan; } public void FreeNative() { - _inner.FreeNative(); + Marshal.FreeCoTaskMem(_allocatedMemory); } } - [CustomTypeMarshaller(typeof(Span<>), CustomTypeMarshallerKind.LinearCollection, BufferSize = 0x200, RequiresStackBuffer = true)] + [CustomTypeMarshaller(typeof(Span<>), CustomTypeMarshallerKind.LinearCollection, BufferSize = 0x200)] public unsafe ref struct NeverNullSpanMarshaller { private SpanMarshaller _inner; @@ -164,40 +149,14 @@ public NeverNullSpanMarshaller(Span managed, Span stackSpace, int sizeO _inner = new SpanMarshaller(managed, stackSpace, sizeOfNativeElement); } - public Span ManagedValues => _inner.ManagedValues; - - public Span NativeValueStorage - { - get => _inner.NativeValueStorage; - } - - public ref byte GetPinnableReference() - { - if (_inner.ManagedValues.Length == 0) - { - return ref *(byte*)0xa5a5a5a5; - } - return ref _inner.GetPinnableReference(); - } - - public void SetUnmarshalledCollectionLength(int length) - { - _inner.SetUnmarshalledCollectionLength(length); - } - - public byte* Value - { - get - { - if (_inner.ManagedValues.Length == 0) - { - return (byte*)0xa5a5a5a5; - } - return _inner.Value; - } - set => _inner.Value = value; - } + public ReadOnlySpan GetManagedValuesSource() => _inner.GetManagedValuesSource(); + public Span GetManagedValuesDestination(int length) => _inner.GetManagedValuesDestination(length); + public Span GetNativeValuesDestination() => _inner.GetNativeValuesDestination(); + public ReadOnlySpan GetNativeValuesSource(int length) => _inner.GetNativeValuesSource(length); + public ref byte GetPinnableReference() => ref _inner.GetPinnableReference(); + public byte* ToNativeValue() => _inner.GetManagedValuesSource().Length == 0 ? (byte*)0xa5a5a5a5 : (byte*)Unsafe.AsPointer(ref GetPinnableReference()); + public void FromNativeValue(byte* value) => _inner.FromNativeValue(value); public Span ToManaged() => _inner.ToManaged(); @@ -207,7 +166,7 @@ public void FreeNative() } } - [CustomTypeMarshaller(typeof(ReadOnlySpan<>), CustomTypeMarshallerKind.LinearCollection, BufferSize = 0x200, RequiresStackBuffer = true)] + [CustomTypeMarshaller(typeof(ReadOnlySpan<>), CustomTypeMarshallerKind.LinearCollection, BufferSize = 0x200)] public unsafe ref struct NeverNullReadOnlySpanMarshaller { private ReadOnlySpanMarshaller _inner; @@ -228,42 +187,10 @@ public NeverNullReadOnlySpanMarshaller(ReadOnlySpan managed, Span stack _inner = new ReadOnlySpanMarshaller(managed, stackSpace, sizeOfNativeElement); } - public Span ManagedValues => _inner.ManagedValues; - - public Span NativeValueStorage - { - get => _inner.NativeValueStorage; - } - - public ref byte GetPinnableReference() - { - if (_inner.ManagedValues.Length == 0) - { - return ref *(byte*)0xa5a5a5a5; - } - return ref _inner.GetPinnableReference(); - } - - public void SetUnmarshalledCollectionLength(int length) - { - _inner.SetUnmarshalledCollectionLength(length); - } - - public byte* Value - { - get - { - if (_inner.ManagedValues.Length == 0) - { - return (byte*)0xa5a5a5a5; - } - return _inner.Value; - } - - set => _inner.Value = value; - } - - public ReadOnlySpan ToManaged() => _inner.ToManaged(); + public ReadOnlySpan GetManagedValuesSource() => _inner.GetManagedValuesSource(); + public Span GetNativeValuesDestination() => _inner.GetNativeValuesDestination(); + public ref byte GetPinnableReference() => ref _inner.GetPinnableReference(); + public byte* ToNativeValue() => _inner.GetManagedValuesSource().Length == 0 ? (byte*)0xa5a5a5a5 : (byte*)Unsafe.AsPointer(ref GetPinnableReference()); public void FreeNative() { @@ -276,18 +203,13 @@ public void FreeNative() public unsafe ref struct DirectSpanMarshaller where T : unmanaged { - private int _unmarshalledLength; private T* _allocatedMemory; + private T* _nativeValue; private Span _data; public DirectSpanMarshaller(int sizeOfNativeElement) :this() { - // This check is not exhaustive, but it will catch the majority of cases. - if (RuntimeHelpers.IsReferenceOrContainsReferences()) - { - throw new ArgumentException("This marshaller only supports blittable element types. The provided type parameter must be blittable", nameof(T)); - } } public DirectSpanMarshaller(Span managed, int sizeOfNativeElement) @@ -310,37 +232,29 @@ public DirectSpanMarshaller(Span managed, Span stackSpace, int sizeOfNa _data = managed; } - public Span ManagedValues => _data; - - public Span NativeValueStorage => _allocatedMemory != null + public ReadOnlySpan GetManagedValuesSource() => _data; + public Span GetManagedValuesDestination(int length) => _data = new Span(_nativeValue, length); + public Span GetNativeValuesDestination() => _allocatedMemory != null ? new Span(_allocatedMemory, _data.Length * Unsafe.SizeOf()) : MemoryMarshal.Cast(_data); + public ReadOnlySpan GetNativeValuesSource(int length) => new ReadOnlySpan(_nativeValue, length * sizeof(T)); + public ref T GetPinnableReference() => ref _data.GetPinnableReference(); - public void SetUnmarshalledCollectionLength(int length) + public T* ToNativeValue() { - _unmarshalledLength = length; + if (_allocatedMemory != null) + { + return _allocatedMemory; + } + return (T*)Unsafe.AsPointer(ref GetPinnableReference()); } - public T* Value + public void FromNativeValue(T* value) { - get - { - if (_allocatedMemory != null) - { - return _allocatedMemory; - } - return (T*)Unsafe.AsPointer(ref GetPinnableReference()); - } - set - { - // We don't save the pointer assigned here to be freed - // since this marshaller passes back the actual memory span from native code - // back to managed code. - _allocatedMemory = null; - _data = new Span(value, _unmarshalledLength); - } + _allocatedMemory = null; + _nativeValue = value; } public Span ToManaged() diff --git a/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/CodeSnippets.cs b/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/CodeSnippets.cs index 8b52ce19d22de..b062d4f4811ee 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/CodeSnippets.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/CodeSnippets.cs @@ -381,6 +381,7 @@ public static string CustomStringMarshallingParametersAndModifiers() { string typeName = typeof(T).ToString(); return BasicParametersAndModifiersWithStringMarshallingCustomType(typeName, "Native", DisableRuntimeMarshalling) + @$" +[CustomTypeMarshaller(typeof({typeName}))] struct Native {{ public Native({typeName} s) {{ }} @@ -694,7 +695,7 @@ struct S public bool b; } -[CustomTypeMarshaller(typeof(S), BufferSize = 1, RequiresStackBuffer = false)] +[CustomTypeMarshaller(typeof(S), BufferSize = 1)] struct Native { private int i; @@ -713,7 +714,7 @@ struct S public bool b; } -[CustomTypeMarshaller(typeof(S), BufferSize = 1, RequiresStackBuffer = true)] +[CustomTypeMarshaller(typeof(S), BufferSize = 1)] struct Native { private int i; @@ -742,12 +743,12 @@ struct Native { public Native(S s, System.Span b) { - Value = s.b ? 1 : 0; } - public S ToManaged() => new S { b = Value != 0 }; + public S ToManaged() => new S { b = true }; - public int Value { get; set; } + public int ToNativeValue() => throw null; + public void FromNativeValue(int value) => throw null; } "; public static string CustomStructMarshallingValuePropertyParametersAndModifiers = BasicParametersAndModifiers("S", DisableRuntimeMarshalling) + @" @@ -762,12 +763,12 @@ struct Native { public Native(S s) { - Value = s.b ? 1 : 0; } - public S ToManaged() => new S { b = Value != 0 }; + public S ToManaged() => new S { b = true }; - public int Value { get; set; } + public int ToNativeValue() => throw null; + public void FromNativeValue(int value) => throw null; } "; public static string CustomStructMarshallingPinnableParametersAndModifiers = BasicParametersAndModifiers("S", DisableRuntimeMarshalling) + @" @@ -791,15 +792,14 @@ public Native(S s) public S ToManaged() => new S { i = *ptr }; - public nint Value - { - get => (nint)ptr; - set => ptr = (int*)value; - } + public nint ToNativeValue() => (nint)ptr; + + public void FromNativeValue(nint value) => ptr = (int*)value; } "; public static string CustomStructMarshallingNativeTypePinnable = @" +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System; @@ -821,6 +821,7 @@ public Native(S s) : this() { ptr = (byte*)Marshal.AllocCoTaskMem(sizeof(byte)); *ptr = s.c; + stackBuffer = new Span(ptr, 1); } public Native(S s, Span buffer) : this() @@ -829,18 +830,16 @@ public Native(S s, Span buffer) : this() stackBuffer[0] = s.c; } - public ref byte GetPinnableReference() => ref (ptr != null ? ref *ptr : ref stackBuffer.GetPinnableReference()); + public ref byte GetPinnableReference() => ref stackBuffer.GetPinnableReference(); public S ToManaged() { return new S { c = *ptr }; } - public byte* Value - { - get => ptr != null ? ptr : throw new InvalidOperationException(); - set => ptr = value; - } + public byte* ToNativeValue() => (byte*)Unsafe.AsPointer(ref GetPinnableReference()); + + public void FromNativeValue(byte* value) => ptr = value; public void FreeNative() { @@ -877,7 +876,7 @@ public Native(S s) : this() value = s; } - public ref byte Value { get => ref value.c; } + public ref byte ToNativeValue() => ref value.c; } "; @@ -975,12 +974,12 @@ public struct IntStructWrapperNative { public IntStructWrapperNative(IntStructWrapper managed) { - Value = managed.Value; } - public int Value { get; set; } + public int ToNativeValue() => throw null; + public void FromNativeValue(int value) => throw null; - public IntStructWrapper ToManaged() => new IntStructWrapper { Value = Value }; + public IntStructWrapper ToManaged() => new IntStructWrapper { Value = 1 }; } "; @@ -1120,10 +1119,15 @@ class TestCollection {} [CustomTypeMarshaller(typeof(TestCollection<>), CustomTypeMarshallerKind.LinearCollection)] ref struct Marshaller { + public Marshaller(int nativeElementSize) : this() {} public Marshaller(TestCollection managed, int nativeElementSize) : this() {} - public System.Span ManagedValues { get; } - public System.Span NativeValueStorage { get; } - public System.IntPtr Value { get; } + public System.ReadOnlySpan GetManagedValuesSource() => throw null; + public System.Span GetManagedValuesDestination(int length) => throw null; + public System.ReadOnlySpan GetNativeValuesSource(int length) => throw null; + public System.Span GetNativeValuesDestination() => throw null; + public System.IntPtr ToNativeValue() => throw null; + public void FromNativeValue(System.IntPtr value) => throw null; + public TestCollection ToManaged() => throw null; } "; @@ -1156,10 +1160,12 @@ ref struct Marshaller { public Marshaller(int nativeElementSize) : this() {} public Marshaller(TestCollection managed, int nativeElementSize) : this() {} - public System.Span ManagedValues { get; } - public System.Span NativeValueStorage { get; } - public System.IntPtr Value { get; set; } - public void SetUnmarshalledCollectionLength(int length) {} + public System.ReadOnlySpan GetManagedValuesSource() => throw null; + public System.Span GetManagedValuesDestination(int length) => throw null; + public System.ReadOnlySpan GetNativeValuesSource(int length) => throw null; + public System.Span GetNativeValuesDestination() => throw null; + public System.IntPtr ToNativeValue() => throw null; + public void FromNativeValue(System.IntPtr value) => throw null; public TestCollection ToManaged() => throw null; }"; } @@ -1252,9 +1258,14 @@ class TestCollection {} ref struct Marshaller { public Marshaller(TestCollection managed, int nativeElementSize) : this() {} - public System.Span ManagedValues { get; } - public System.Span NativeValueStorage { get; } - public System.IntPtr Value { get; } + + public System.ReadOnlySpan GetManagedValuesSource() => throw null; + public System.Span GetManagedValuesDestination(int length) => throw null; + public System.ReadOnlySpan GetNativeValuesSource(int length) => throw null; + public System.Span GetNativeValuesDestination() => throw null; + public System.IntPtr ToNativeValue() => throw null; + public void FromNativeValue(System.IntPtr value) => throw null; + public TestCollection ToManaged() => throw null; }"; @@ -1406,7 +1417,7 @@ public static partial void Method( [MarshalUsing(CountElementName=""arr6"", ElementIndirectionDepth = 6)] [MarshalUsing(CountElementName=""arr7"", ElementIndirectionDepth = 7)] [MarshalUsing(CountElementName=""arr8"", ElementIndirectionDepth = 8)] - [MarshalUsing(CountElementName=""arr9"", ElementIndirectionDepth = 9)]ref int[][][][][][][][][][] arr10, + [MarshalUsing(CountElementName=""arr9"", ElementIndirectionDepth = 9)]ref int[][][][][][][][][][][] arr10, [MarshalUsing(CountElementName=""arr0"", ElementIndirectionDepth = 0)] [MarshalUsing(CountElementName=""arr1"", ElementIndirectionDepth = 1)] [MarshalUsing(CountElementName=""arr2"", ElementIndirectionDepth = 2)] @@ -1415,7 +1426,7 @@ public static partial void Method( [MarshalUsing(CountElementName=""arr5"", ElementIndirectionDepth = 5)] [MarshalUsing(CountElementName=""arr6"", ElementIndirectionDepth = 6)] [MarshalUsing(CountElementName=""arr7"", ElementIndirectionDepth = 7)] - [MarshalUsing(CountElementName=""arr8"", ElementIndirectionDepth = 8)]ref int[][][][][][][][][] arr9, + [MarshalUsing(CountElementName=""arr8"", ElementIndirectionDepth = 8)]ref int[][][][][][][][][][] arr9, [MarshalUsing(CountElementName=""arr0"", ElementIndirectionDepth = 0)] [MarshalUsing(CountElementName=""arr1"", ElementIndirectionDepth = 1)] [MarshalUsing(CountElementName=""arr2"", ElementIndirectionDepth = 2)] diff --git a/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/ManualTypeMarshallingAnalyzerTests.cs b/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/ManualTypeMarshallingAnalyzerTests.cs index 8d473b00be4d3..0869f698d2de7 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/ManualTypeMarshallingAnalyzerTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/DllImportGenerator.UnitTests/ManualTypeMarshallingAnalyzerTests.cs @@ -158,7 +158,8 @@ public Native(S s) public S ToManaged() => new S(); - public string {|#0:Value|} { get => null; set {} } + public string {|#0:ToNativeValue|}() => throw null; + public void FromNativeValue(string value) => throw null; }"; await VerifyCS.VerifyAnalyzerAsync(source, @@ -190,7 +191,8 @@ public Native(S s) public S ToManaged() => new S() { s = value }; - public IntPtr Value { get => IntPtr.Zero; set {} } + public IntPtr ToNativeValue() => throw null; + public void FromNativeValue(IntPtr value) => throw null; }"; await VerifyCS.VerifyAnalyzerAsync(source); @@ -221,7 +223,8 @@ public Native(S s) public S ToManaged() => new S() { s = value }; - public IntPtr Value { get => IntPtr.Zero; set {} } + public IntPtr ToNativeValue() => throw null; + public void FromNativeValue(IntPtr value) => throw null; }"; await VerifyCS.VerifyAnalyzerAsync(source, @@ -255,7 +258,8 @@ public Native(S s) public S ToManaged() => new S(); - public IntPtr Value { get => IntPtr.Zero; set {} } + public IntPtr ToNativeValue() => throw null; + public void FromNativeValue(IntPtr value) => throw null; }"; await VerifyCS.VerifyAnalyzerAsync(source, @@ -289,7 +293,8 @@ public Native(S s) : this() public S ToManaged() => new S(); - public IntPtr Value { get => IntPtr.Zero; set {} } + public IntPtr ToNativeValue() => throw null; + public void FromNativeValue(IntPtr value) => throw null; }"; await VerifyCS.VerifyAnalyzerAsync(source); @@ -322,7 +327,8 @@ public Native(S s) public S ToManaged() => new S(); - public IntPtr Value { get => IntPtr.Zero; set {} } + public IntPtr ToNativeValue() => throw null; + public void FromNativeValue(IntPtr value) => throw null; }"; await VerifyCS.VerifyAnalyzerAsync(source); @@ -355,7 +361,8 @@ public Native(S s) : this() public ref byte GetPinnableReference() => ref System.Runtime.CompilerServices.Unsafe.NullRef(); - public IntPtr Value { get => IntPtr.Zero; set {} } + public IntPtr ToNativeValue() => throw null; + public void FromNativeValue(IntPtr value) => throw null; }"; await VerifyCS.VerifyAnalyzerAsync(source); @@ -388,7 +395,8 @@ public Native(S s) : this() public S ToManaged() => new S(); - public int {|#0:Value|} { get => 0; set {} } + public int {|#0:ToNativeValue|}() => throw null; + public void FromNativeValue(int value) => throw null; }"; await VerifyCS.VerifyAnalyzerAsync(source, @@ -422,7 +430,8 @@ public Native(S s) : this() public S ToManaged() => new S(); - public int* Value { get => null; set {} } + public int* ToNativeValue() => throw null; + public void FromNativeValue(int* value) => throw null; }"; await VerifyCS.VerifyAnalyzerAsync(source); @@ -452,8 +461,7 @@ public Native(S s) : this() { value = s; } - - public ref byte {|#0:Value|} { get => ref value.GetPinnableReference(); } + public ref byte {|#0:ToNativeValue|}() => throw null; }"; await VerifyCS.VerifyAnalyzerAsync(source, @@ -485,7 +493,7 @@ public Native(S s) : this() public ref byte GetPinnableReference() => ref value.c; - public ref byte {|#0:Value|} { get => ref GetPinnableReference(); } + public ref byte {|#0:ToNativeValue|}() => throw null; }"; await VerifyCS.VerifyAnalyzerAsync(source, @@ -547,7 +555,8 @@ public Native(S s) : this() public ref byte GetPinnableReference() => ref System.Runtime.CompilerServices.Unsafe.NullRef(); - public int Value { get; set; } + public int ToNativeValue() => throw null; + public void FromNativeValue(int value) => throw null; }"; await VerifyCS.VerifyAnalyzerAsync(source); @@ -615,10 +624,10 @@ class S { public Native(S s) : this() {} - public Span ManagedValues { get; set; } - public Span NativeValueStorage { get; set; } - public IntPtr Value { get; } + public System.ReadOnlySpan GetManagedValuesSource() => throw null; + public System.Span GetNativeValuesDestination() => throw null; + public System.IntPtr ToNativeValue() => throw null; }"; await VerifyCS.VerifyAnalyzerAsync(source, @@ -643,10 +652,9 @@ ref struct Native { public Native(S s, int nativeElementSize) : this() {} - public Span ManagedValues { get; set; } - public Span NativeValueStorage { get; set; } - - public IntPtr Value { get; } + public System.ReadOnlySpan GetManagedValuesSource() => throw null; + public System.Span GetNativeValuesDestination() => throw null; + public System.IntPtr ToNativeValue() => throw null; }"; await VerifyCS.VerifyAnalyzerAsync(source); @@ -670,10 +678,9 @@ class S { public Native(S s, Span stackSpace) : this() {} - public Span ManagedValues { get; set; } - public Span NativeValueStorage { get; set; } - - public IntPtr Value { get; } + public System.ReadOnlySpan GetManagedValuesSource() => throw null; + public System.Span GetNativeValuesDestination() => throw null; + public System.IntPtr ToNativeValue() => throw null; }"; await VerifyCS.VerifyAnalyzerAsync(source, @@ -698,10 +705,9 @@ class S { public Native(S s, Span stackSpace, int nativeElementSize) : this() {} - public Span ManagedValues { get; set; } - public Span NativeValueStorage { get; set; } - - public IntPtr Value { get; } + public System.ReadOnlySpan GetManagedValuesSource() => throw null; + public System.Span GetNativeValuesDestination() => throw null; + public System.IntPtr ToNativeValue() => throw null; }"; await VerifyCS.VerifyAnalyzerAsync(source, @@ -709,7 +715,7 @@ await VerifyCS.VerifyAnalyzerAsync(source, } [ConditionalFact] - public async Task CollectionNativeTypeWithMissingManagedValuesProperty_ReportsDiagnostic() + public async Task CollectionNativeTypeWithMissingManagedValuesSourceProperty_ReportsDiagnostic() { string source = @" using System; @@ -726,9 +732,8 @@ class S { public Native(S s, int nativeElementSize) : this() {} - public Span NativeValueStorage { get; set; } - - public IntPtr Value { get; } + public System.Span GetNativeValuesDestination() => throw null; + public System.IntPtr ToNativeValue() => throw null; }"; await VerifyCS.VerifyAnalyzerAsync(source, @@ -736,7 +741,7 @@ await VerifyCS.VerifyAnalyzerAsync(source, } [ConditionalFact] - public async Task CollectionNativeTypeWithMissingNativeValueStorageProperty_ReportsDiagnostic() + public async Task CollectionNativeTypeWithMissingNativeValuesDestinationProperty_ReportsDiagnostic() { string source = @" using System; @@ -753,9 +758,8 @@ class S { public Native(S s, int nativeElementSize) : this() {} - public Span ManagedValues { get; set; } - - public IntPtr Value { get; } + public System.ReadOnlySpan GetManagedValuesSource() => throw null; + public System.IntPtr ToNativeValue() => throw null; }"; await VerifyCS.VerifyAnalyzerAsync(source, @@ -857,7 +861,7 @@ await VerifyCS.VerifyAnalyzerAsync(source, } [ConditionalFact] - public async Task NativeTypeWithConstructorAndSetOnlyValueProperty_ReportsDiagnostic() + public async Task NativeTypeWithConstructorAndFromNativeValueMethod_ReportsDiagnostic() { string source = @" using System; @@ -870,11 +874,13 @@ class S } [CustomTypeMarshaller(typeof(S))] -struct Native +struct {|#0:Native|} { public Native(S s) {} - public IntPtr {|#0:Value|} { set {} } + public void FromNativeValue(IntPtr value) => throw null; + + public S ToManaged() => new S(); }"; await VerifyCS.VerifyAnalyzerAsync(source, @@ -882,7 +888,7 @@ await VerifyCS.VerifyAnalyzerAsync(source, } [ConditionalFact] - public async Task NativeTypeWithToManagedAndGetOnlyValueProperty_ReportsDiagnostic() + public async Task NativeTypeWithToManagedAndToNativeValueMethod_ReportsDiagnostic() { string source = @" using System; @@ -895,11 +901,13 @@ class S } [CustomTypeMarshaller(typeof(S))] -struct Native +struct {|#0:Native|} { + public Native(S managed) {} + public S ToManaged() => new S(); - public IntPtr {|#0:Value|} => IntPtr.Zero; + public IntPtr ToNativeValue() => IntPtr.Zero; }"; await VerifyCS.VerifyAnalyzerAsync(source, @@ -1011,41 +1019,6 @@ await VerifyCS.VerifyAnalyzerAsync(source, VerifyCS.Diagnostic(NativeTypeMustBeBlittableRule).WithLocation(0).WithArguments("Native", "S")); } - [ConditionalFact] - public async Task NonBlittableNativeTypeOnMarshalUsingField_ReportsDiagnostic() - { - string source = @" -using System; -using System.Runtime.InteropServices; - -struct S -{ - public string s; -} - -[CustomTypeMarshaller(typeof(S))] -struct {|#0:Native|} -{ - private string value; - - public Native(S s) : this() - { - } - - public S ToManaged() => new S(); -} - - -struct Test -{ - [MarshalUsing(typeof(Native))] - S s; -} -"; - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(NativeTypeMustBeBlittableRule).WithLocation(0).WithArguments("Native", "S")); - } - [ConditionalFact] public async Task GenericNativeTypeWithGenericMemberInstantiatedWithBlittable_DoesNotReportDiagnostic() { @@ -1065,12 +1038,12 @@ struct Native { public Native(S s) { - Value = new T(); } public S ToManaged() => new S(); - public T Value { get; set; } + public T ToNativeValue() => throw null; + public void FromNativeValue(T value) => throw null; }"; await VerifyCS.VerifyAnalyzerAsync(source); } @@ -1094,12 +1067,12 @@ struct Native { public Native(S s) { - Value = new T(); } public S ToManaged() => new S(); - public T Value { get; set; } + public T ToNativeValue() => throw null; + public void FromNativeValue(T value) => throw null; }"; await VerifyCS.VerifyAnalyzerAsync(source, VerifyCS.Diagnostic(NativeGenericTypeMustBeClosedOrMatchArityRule).WithLocation(0).WithArguments("Native<>", "S")); } @@ -1122,12 +1095,12 @@ struct Native { public Native(S s) { - Value = new T(); } public S ToManaged() => new S(); - public T Value { get; set; } + public T ToNativeValue() => throw null; + public void FromNativeValue(T value) => throw null; } static class Test @@ -1156,12 +1129,12 @@ struct {|#1:Native|} { public Native(S s) { - Value = 0; } public S ToManaged() => new S(); - public int Value { get; set; } + public T ToNativeValue() => throw null; + public void FromNativeValue(T value) => throw null; }"; await VerifyCS.VerifyAnalyzerAsync(source, VerifyCS.Diagnostic(NativeTypeMustHaveCustomTypeMarshallerAttributeRule).WithLocation(0).WithArguments("S"), @@ -1186,12 +1159,12 @@ struct Native { public Native(S s) { - Value = 0; } public S ToManaged() => new S(); - public int Value { get; set; } + public T ToNativeValue() => throw null; + public void FromNativeValue(T value) => throw null; }"; await VerifyCS.VerifyAnalyzerAsync(source); } 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 accc1719c70ba..b9edba83a7e5c 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/NonBlittable.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/NonBlittable.cs @@ -57,11 +57,9 @@ public DoubleToLongMarshaler(double d) public double ToManaged() => MemoryMarshal.Cast(MemoryMarshal.CreateSpan(ref l, 1))[0]; - public long Value - { - get => l; - set => l = value; - } + public long ToNativeValue() => l; + + public void FromNativeValue(long value) => l = value; } [NativeMarshalling(typeof(BoolStructNative))] @@ -113,7 +111,10 @@ public IntWrapperMarshaler(IntWrapper managed) *Value = managed.i; } - public int* Value { get; set; } + private int* Value { get; set; } + + public int* ToNativeValue() => Value; + public void FromNativeValue(int* value) => Value = value; public IntWrapper ToManaged() => new IntWrapper { i = *Value }; @@ -123,7 +124,7 @@ public void FreeNative() } } - [CustomTypeMarshaller(typeof(string), BufferSize = 0x100, RequiresStackBuffer = true)] + [CustomTypeMarshaller(typeof(string), BufferSize = 0x100)] public unsafe ref struct Utf16StringMarshaller { private ushort* allocated; @@ -165,24 +166,19 @@ public ref ushort GetPinnableReference() return ref span.GetPinnableReference(); } - public ushort* Value + public ushort* ToNativeValue() => (ushort*)Unsafe.AsPointer(ref GetPinnableReference()); + + public void FromNativeValue(ushort* value) { - get - { - return (ushort*)Unsafe.AsPointer(ref GetPinnableReference()); - } - set + allocated = value; + span = new Span(value, value == null ? 0 : FindStringLength(value)); + isNullString = value == null; + + static int FindStringLength(ushort* ptr) { - allocated = value; - span = new Span(value, value == null ? 0 : FindStringLength(value)); - isNullString = value == null; - - static int FindStringLength(ushort* ptr) - { - // Implemented similarly to string.wcslen as we can't access that outside of CoreLib - var searchSpace = new Span(ptr, int.MaxValue); - return searchSpace.IndexOf((ushort)0); - } + // Implemented similarly to string.wcslen as we can't access that outside of CoreLib + var searchSpace = new Span(ptr, int.MaxValue); + return searchSpace.IndexOf((ushort)0); } } @@ -197,7 +193,7 @@ public void FreeNative() } } - [CustomTypeMarshaller(typeof(string), BufferSize = 0x100, RequiresStackBuffer = true)] + [CustomTypeMarshaller(typeof(string), BufferSize = 0x100)] public unsafe ref struct Utf8StringMarshaller { private byte* allocated; @@ -226,19 +222,11 @@ public Utf8StringMarshaller(string str, Span buffer) } } - public byte* Value - { - get - { - return allocated != null ? allocated : (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(span)); - } - set - { - allocated = value; - } - } + public byte* ToNativeValue() => allocated != null ? allocated : (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(span)); + + public void FromNativeValue(byte* value) => allocated = value; - public string? ToManaged() => Marshal.PtrToStringUTF8((IntPtr)Value); + public string? ToManaged() => Marshal.PtrToStringUTF8((IntPtr)allocated); public void FreeNative() => Marshal.FreeCoTaskMem((IntPtr)allocated); } @@ -261,7 +249,7 @@ public IntStructWrapperNative(IntStructWrapper managed) public IntStructWrapper ToManaged() => new IntStructWrapper { Value = value }; } - [CustomTypeMarshaller(typeof(List<>), CustomTypeMarshallerKind.LinearCollection, BufferSize = 0x200, RequiresStackBuffer = true)] + [CustomTypeMarshaller(typeof(List<>), CustomTypeMarshallerKind.LinearCollection, BufferSize = 0x200)] public unsafe ref struct ListMarshaller { private List managedList; @@ -303,40 +291,39 @@ public ListMarshaller(List managed, Span stackSpace, int sizeOfNativeEl } } - public Span ManagedValues => CollectionsMarshal.AsSpan(managedList); + public ReadOnlySpan GetManagedValuesSource() => CollectionsMarshal.AsSpan(managedList); - public Span NativeValueStorage { get; private set; } - - public ref byte GetPinnableReference() => ref NativeValueStorage.GetPinnableReference(); - - public void SetUnmarshalledCollectionLength(int length) + public Span GetManagedValuesDestination(int length) { + if (allocatedMemory == IntPtr.Zero) + { + managedList = null; + return default; + } managedList = new List(length); for (int i = 0; i < length; i++) { managedList.Add(default); } + return CollectionsMarshal.AsSpan(managedList); } - public byte* Value + private Span NativeValueStorage { get; set; } + + public Span GetNativeValuesDestination() => NativeValueStorage; + + public ReadOnlySpan GetNativeValuesSource(int length) { - get - { - return (byte*)Unsafe.AsPointer(ref GetPinnableReference()); - } - set - { - if (value == null) - { - managedList = null; - NativeValueStorage = default; - } - else - { - allocatedMemory = (IntPtr)value; - NativeValueStorage = new Span(value, (managedList?.Count ?? 0) * sizeOfNativeElement); - } - } + return allocatedMemory == IntPtr.Zero ? default : new Span((void*)allocatedMemory, length * sizeOfNativeElement); + } + + public ref byte GetPinnableReference() => ref NativeValueStorage.GetPinnableReference(); + + public byte* ToNativeValue() => (byte*)Unsafe.AsPointer(ref GetPinnableReference()); + + public void FromNativeValue(byte* value) + { + allocatedMemory = (IntPtr)value; } public List ToManaged() => managedList; From bad0de1fb2f4d86516784d444deae0e6939b93e7 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 4 Mar 2022 16:08:08 -0800 Subject: [PATCH 13/24] A few misc fixes --- .../System/Runtime/InteropServices/CustomTypeMarshallerKind.cs | 2 +- .../Analyzers/ManualTypeMarshallingAnalyzer.cs | 2 +- .../Microsoft.Interop.SourceGeneration.csproj | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libraries/Common/src/System/Runtime/InteropServices/CustomTypeMarshallerKind.cs b/src/libraries/Common/src/System/Runtime/InteropServices/CustomTypeMarshallerKind.cs index a887330d2f6b1..b1f26d283991e 100644 --- a/src/libraries/Common/src/System/Runtime/InteropServices/CustomTypeMarshallerKind.cs +++ b/src/libraries/Common/src/System/Runtime/InteropServices/CustomTypeMarshallerKind.cs @@ -8,7 +8,7 @@ // namespace System.Runtime.InteropServices { -#if DLLIMPORT_GENERATOR_TEST +#if LIBRARYIMPORT_GENERATOR_TEST public #else internal diff --git a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/ManualTypeMarshallingAnalyzer.cs b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/ManualTypeMarshallingAnalyzer.cs index d54ee1bb466ce..80ad3f2d12fda 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/ManualTypeMarshallingAnalyzer.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/ManualTypeMarshallingAnalyzer.cs @@ -210,7 +210,7 @@ private void PrepareForAnalysis(CompilationStartAnalysisContext context) INamedTypeSymbol? spanOfT = context.Compilation.GetTypeByMetadataName(TypeNames.System_Span_Metadata); INamedTypeSymbol? spanOfByte = spanOfT?.Construct(context.Compilation.GetSpecialType(SpecialType.System_Byte)); INamedTypeSymbol? readOnlySpanOfT = context.Compilation.GetTypeByMetadataName(TypeNames.System_ReadOnlySpan_Metadata); - INamedTypeSymbol? readOnlySpanOfByte = spanOfT?.Construct(context.Compilation.GetSpecialType(SpecialType.System_Byte)); + INamedTypeSymbol? readOnlySpanOfByte = readOnlySpanOfT?.Construct(context.Compilation.GetSpecialType(SpecialType.System_Byte)); if (spanOfT is not null && readOnlySpanOfT is not null) { 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 568acc00cc7ba..aa224a5705e0f 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 @@ -5,7 +5,7 @@ enable Microsoft.Interop true - DLLIMPORT_GENERATOR_TEST + $(DefineConstants);LIBRARYIMPORT_GENERATOR_TEST From bae02622446ab73fba092faf06512a7cf2c15d33 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 7 Mar 2022 09:21:12 -0800 Subject: [PATCH 14/24] Convert custom marshallers in the tree to the approved shape --- src/libraries/Common/src/Interop/Interop.Ldap.cs | 10 ++++------ .../InteropServices/HandleRefMarshaller.cs | 2 +- .../Diagnostics/Reader/UnsafeNativeMethods.cs | 16 +++++++--------- .../src/System/Drawing/Imaging/BitmapData.cs | 2 +- .../src/System/Drawing/Imaging/ColorMatrix.cs | 2 +- .../System/Drawing/Imaging/MetafileHeaderEmf.cs | 2 +- .../System/Drawing/Imaging/MetafileHeaderWmf.cs | 8 +++----- .../Drawing/Imaging/WmfPlaceableFileHeader.cs | 2 +- 8 files changed, 19 insertions(+), 25 deletions(-) diff --git a/src/libraries/Common/src/Interop/Interop.Ldap.cs b/src/libraries/Common/src/Interop/Interop.Ldap.cs index e8d7717a7d2d5..7606c653300e7 100644 --- a/src/libraries/Common/src/Interop/Interop.Ldap.cs +++ b/src/libraries/Common/src/Interop/Interop.Ldap.cs @@ -187,7 +187,7 @@ public PinningMarshaller(BerVal managed) public ref int GetPinnableReference() => ref (_managed is null ? ref Unsafe.NullRef() : ref _managed.bv_len); - public void* Value => Unsafe.AsPointer(ref GetPinnableReference()); + public void* ToNativeValue() => Unsafe.AsPointer(ref GetPinnableReference()); } #endif } @@ -239,11 +239,9 @@ public Marshaller(LdapReferralCallback managed) _native.dereference = managed.dereference is not null ? Marshal.GetFunctionPointerForDelegate(managed.dereference) : IntPtr.Zero; } - public Native Value - { - get => _native; - set => _native = value; - } + public Native ToNativeValue() => _native; + + public void FromNativeValue(Native value) => _native = value; public LdapReferralCallback ToManaged() { diff --git a/src/libraries/Common/src/System/Runtime/InteropServices/HandleRefMarshaller.cs b/src/libraries/Common/src/System/Runtime/InteropServices/HandleRefMarshaller.cs index f01b86054ed4b..478dfcc6b189d 100644 --- a/src/libraries/Common/src/System/Runtime/InteropServices/HandleRefMarshaller.cs +++ b/src/libraries/Common/src/System/Runtime/InteropServices/HandleRefMarshaller.cs @@ -16,7 +16,7 @@ public HandleRefMarshaller(HandleRef handle) _handle = handle; } - public IntPtr Value => _handle.Handle; + public IntPtr ToNativeValue() => _handle.Handle; public void FreeNative() => GC.KeepAlive(_handle.Wrapper); } 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 7b1d0d82a7ce0..a34cd55a6c478 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 @@ -381,18 +381,16 @@ public Marshaller(EvtRpcLogin managed) _value.Flags = managed.Flags; } - public Native Value + public Native ToNativeValue() => _value; + + public void FromNativeValue(Native value) { - get => _value; - set + // SafeHandle fields cannot change the underlying handle value during marshalling. + if (_value.Password != value.Password) { - // SafeHandle fields cannot change the underlying handle value during marshalling. - if (_value.Password != value.Password) - { - throw new InvalidOperationException(); - } - _value = value; + throw new InvalidOperationException(); } + _value = value; } public EvtRpcLogin ToManaged() diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/BitmapData.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/BitmapData.cs index 105807d318676..94f33eab4c7f9 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/BitmapData.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/BitmapData.cs @@ -109,7 +109,7 @@ public PinningMarshaller(BitmapData managed) public ref int GetPinnableReference() => ref (_managed is null ? ref Unsafe.NullRef() : ref _managed.GetPinnableReference()); - public void* Value => Unsafe.AsPointer(ref GetPinnableReference()); + public void* ToNativeValue() => Unsafe.AsPointer(ref GetPinnableReference()); } #endif } diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/ColorMatrix.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/ColorMatrix.cs index 7f78979c02e36..0dd6d93f83621 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/ColorMatrix.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/ColorMatrix.cs @@ -407,7 +407,7 @@ public PinningMarshaller(ColorMatrix managed) public ref float GetPinnableReference() => ref (_managed is null ? ref Unsafe.NullRef() : ref _managed.GetPinnableReference()); - public void* Value => Unsafe.AsPointer(ref GetPinnableReference()); + public void* ToNativeValue() => Unsafe.AsPointer(ref GetPinnableReference()); } #endif } diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/MetafileHeaderEmf.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/MetafileHeaderEmf.cs index 73940fc2b35d4..614b503918a8b 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/MetafileHeaderEmf.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/MetafileHeaderEmf.cs @@ -45,7 +45,7 @@ public PinningMarshaller(MetafileHeaderEmf managed) public ref byte GetPinnableReference() => ref (_managed is null ? ref Unsafe.NullRef() : ref _managed.GetPinnableReference()); - public void* Value => Unsafe.AsPointer(ref GetPinnableReference()); + public void* ToNativeValue() => Unsafe.AsPointer(ref GetPinnableReference()); } #endif } diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/MetafileHeaderWmf.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/MetafileHeaderWmf.cs index 9a9e31275f921..42ef89d497f1e 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/MetafileHeaderWmf.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/MetafileHeaderWmf.cs @@ -121,11 +121,9 @@ public InPlaceMarshaller(MetafileHeaderWmf managed) _native.LogicalDpiY = managed.LogicalDpiY; } - public Native Value - { - get => _native; - set => _native = value; - } + public Native ToNativeValue() => _native; + + public void FromNativeValue(Native value) => _native = value; public MetafileHeaderWmf ToManaged() { diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/WmfPlaceableFileHeader.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/WmfPlaceableFileHeader.cs index e5cb014b2f4c5..2010db9eb60bd 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/WmfPlaceableFileHeader.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/WmfPlaceableFileHeader.cs @@ -117,7 +117,7 @@ public PinningMarshaller(WmfPlaceableFileHeader managed) public ref int GetPinnableReference() => ref (_managed is null ? ref Unsafe.NullRef() : ref _managed.GetPinnableReference()); - public void* Value => Unsafe.AsPointer(ref GetPinnableReference()); + public void* ToNativeValue() => Unsafe.AsPointer(ref GetPinnableReference()); } #endif } From 9b4b1f7f189e9369fe92f97e8e3e8e26273f047b Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 7 Mar 2022 17:18:33 -0800 Subject: [PATCH 15/24] Fix test failure --- .../System.Runtime.InteropServices.sln | 66 +++++++++---------- .../AdditionalAttributesOnStub.cs | 1 + 2 files changed, 32 insertions(+), 35 deletions(-) diff --git a/src/libraries/System.Runtime.InteropServices/System.Runtime.InteropServices.sln b/src/libraries/System.Runtime.InteropServices/System.Runtime.InteropServices.sln index 6aaa0d3306a0b..7f09d31fb6907 100644 --- a/src/libraries/System.Runtime.InteropServices/System.Runtime.InteropServices.sln +++ b/src/libraries/System.Runtime.InteropServices/System.Runtime.InteropServices.sln @@ -109,12 +109,12 @@ Global {1B248B4C-7584-4C04-850A-A50EB592052C}.Release|x64.Build.0 = Release|Any CPU {1B248B4C-7584-4C04-850A-A50EB592052C}.Release|x86.ActiveCfg = Release|Any CPU {1B248B4C-7584-4C04-850A-A50EB592052C}.Release|x86.Build.0 = Release|Any CPU - {1B248B4C-7584-4C04-850A-A50EB592052C}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {1B248B4C-7584-4C04-850A-A50EB592052C}.Checked|Any CPU.Build.0 = Debug|Any CPU - {1B248B4C-7584-4C04-850A-A50EB592052C}.Checked|x64.ActiveCfg = Debug|Any CPU - {1B248B4C-7584-4C04-850A-A50EB592052C}.Checked|x64.Build.0 = Debug|Any CPU - {1B248B4C-7584-4C04-850A-A50EB592052C}.Checked|x86.ActiveCfg = Debug|Any CPU - {1B248B4C-7584-4C04-850A-A50EB592052C}.Checked|x86.Build.0 = Debug|Any CPU + {90CDAD9F-3ACC-43B0-9696-0C849FCD8C40}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {90CDAD9F-3ACC-43B0-9696-0C849FCD8C40}.Checked|Any CPU.Build.0 = Debug|Any CPU + {90CDAD9F-3ACC-43B0-9696-0C849FCD8C40}.Checked|x64.ActiveCfg = Debug|Any CPU + {90CDAD9F-3ACC-43B0-9696-0C849FCD8C40}.Checked|x64.Build.0 = Debug|Any CPU + {90CDAD9F-3ACC-43B0-9696-0C849FCD8C40}.Checked|x86.ActiveCfg = Debug|Any CPU + {90CDAD9F-3ACC-43B0-9696-0C849FCD8C40}.Checked|x86.Build.0 = Debug|Any CPU {90CDAD9F-3ACC-43B0-9696-0C849FCD8C40}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {90CDAD9F-3ACC-43B0-9696-0C849FCD8C40}.Debug|Any CPU.Build.0 = Debug|Any CPU {90CDAD9F-3ACC-43B0-9696-0C849FCD8C40}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -127,12 +127,12 @@ Global {90CDAD9F-3ACC-43B0-9696-0C849FCD8C40}.Release|x64.Build.0 = Release|Any CPU {90CDAD9F-3ACC-43B0-9696-0C849FCD8C40}.Release|x86.ActiveCfg = Release|Any CPU {90CDAD9F-3ACC-43B0-9696-0C849FCD8C40}.Release|x86.Build.0 = Release|Any CPU - {90CDAD9F-3ACC-43B0-9696-0C849FCD8C40}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {90CDAD9F-3ACC-43B0-9696-0C849FCD8C40}.Checked|Any CPU.Build.0 = Debug|Any CPU - {90CDAD9F-3ACC-43B0-9696-0C849FCD8C40}.Checked|x64.ActiveCfg = Debug|Any CPU - {90CDAD9F-3ACC-43B0-9696-0C849FCD8C40}.Checked|x64.Build.0 = Debug|Any CPU - {90CDAD9F-3ACC-43B0-9696-0C849FCD8C40}.Checked|x86.ActiveCfg = Debug|Any CPU - {90CDAD9F-3ACC-43B0-9696-0C849FCD8C40}.Checked|x86.Build.0 = Debug|Any CPU + {768B77B0-EA45-469D-B39E-545EB72F5A43}.Checked|Any CPU.ActiveCfg = Release|Any CPU + {768B77B0-EA45-469D-B39E-545EB72F5A43}.Checked|Any CPU.Build.0 = Release|Any CPU + {768B77B0-EA45-469D-B39E-545EB72F5A43}.Checked|x64.ActiveCfg = Release|Any CPU + {768B77B0-EA45-469D-B39E-545EB72F5A43}.Checked|x64.Build.0 = Release|Any CPU + {768B77B0-EA45-469D-B39E-545EB72F5A43}.Checked|x86.ActiveCfg = Release|Any CPU + {768B77B0-EA45-469D-B39E-545EB72F5A43}.Checked|x86.Build.0 = Release|Any CPU {768B77B0-EA45-469D-B39E-545EB72F5A43}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {768B77B0-EA45-469D-B39E-545EB72F5A43}.Debug|Any CPU.Build.0 = Debug|Any CPU {768B77B0-EA45-469D-B39E-545EB72F5A43}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -199,12 +199,12 @@ Global {79F7BE0E-01AA-4AFB-B047-CF7C0B38F81E}.Release|x64.Build.0 = Release|Any CPU {79F7BE0E-01AA-4AFB-B047-CF7C0B38F81E}.Release|x86.ActiveCfg = Release|Any CPU {79F7BE0E-01AA-4AFB-B047-CF7C0B38F81E}.Release|x86.Build.0 = Release|Any CPU - {79F7BE0E-01AA-4AFB-B047-CF7C0B38F81E}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {79F7BE0E-01AA-4AFB-B047-CF7C0B38F81E}.Checked|Any CPU.Build.0 = Debug|Any CPU - {79F7BE0E-01AA-4AFB-B047-CF7C0B38F81E}.Checked|x64.ActiveCfg = Debug|Any CPU - {79F7BE0E-01AA-4AFB-B047-CF7C0B38F81E}.Checked|x64.Build.0 = Debug|Any CPU - {79F7BE0E-01AA-4AFB-B047-CF7C0B38F81E}.Checked|x86.ActiveCfg = Debug|Any CPU - {79F7BE0E-01AA-4AFB-B047-CF7C0B38F81E}.Checked|x86.Build.0 = Debug|Any CPU + {9C2C2B5C-5E75-4935-8A4A-DE3D3A5DBBC1}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {9C2C2B5C-5E75-4935-8A4A-DE3D3A5DBBC1}.Checked|Any CPU.Build.0 = Debug|Any CPU + {9C2C2B5C-5E75-4935-8A4A-DE3D3A5DBBC1}.Checked|x64.ActiveCfg = Debug|Any CPU + {9C2C2B5C-5E75-4935-8A4A-DE3D3A5DBBC1}.Checked|x64.Build.0 = Debug|Any CPU + {9C2C2B5C-5E75-4935-8A4A-DE3D3A5DBBC1}.Checked|x86.ActiveCfg = Debug|Any CPU + {9C2C2B5C-5E75-4935-8A4A-DE3D3A5DBBC1}.Checked|x86.Build.0 = Debug|Any CPU {9C2C2B5C-5E75-4935-8A4A-DE3D3A5DBBC1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9C2C2B5C-5E75-4935-8A4A-DE3D3A5DBBC1}.Debug|Any CPU.Build.0 = Debug|Any CPU {9C2C2B5C-5E75-4935-8A4A-DE3D3A5DBBC1}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -217,12 +217,12 @@ Global {9C2C2B5C-5E75-4935-8A4A-DE3D3A5DBBC1}.Release|x64.Build.0 = Release|Any CPU {9C2C2B5C-5E75-4935-8A4A-DE3D3A5DBBC1}.Release|x86.ActiveCfg = Release|Any CPU {9C2C2B5C-5E75-4935-8A4A-DE3D3A5DBBC1}.Release|x86.Build.0 = Release|Any CPU - {9C2C2B5C-5E75-4935-8A4A-DE3D3A5DBBC1}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {9C2C2B5C-5E75-4935-8A4A-DE3D3A5DBBC1}.Checked|Any CPU.Build.0 = Debug|Any CPU - {9C2C2B5C-5E75-4935-8A4A-DE3D3A5DBBC1}.Checked|x64.ActiveCfg = Debug|Any CPU - {9C2C2B5C-5E75-4935-8A4A-DE3D3A5DBBC1}.Checked|x64.Build.0 = Debug|Any CPU - {9C2C2B5C-5E75-4935-8A4A-DE3D3A5DBBC1}.Checked|x86.ActiveCfg = Debug|Any CPU - {9C2C2B5C-5E75-4935-8A4A-DE3D3A5DBBC1}.Checked|x86.Build.0 = Debug|Any CPU + {EA8DBC12-60BC-433E-ABFF-A89DFA795283}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {EA8DBC12-60BC-433E-ABFF-A89DFA795283}.Checked|Any CPU.Build.0 = Debug|Any CPU + {EA8DBC12-60BC-433E-ABFF-A89DFA795283}.Checked|x64.ActiveCfg = Debug|Any CPU + {EA8DBC12-60BC-433E-ABFF-A89DFA795283}.Checked|x64.Build.0 = Debug|Any CPU + {EA8DBC12-60BC-433E-ABFF-A89DFA795283}.Checked|x86.ActiveCfg = Debug|Any CPU + {EA8DBC12-60BC-433E-ABFF-A89DFA795283}.Checked|x86.Build.0 = Debug|Any CPU {EA8DBC12-60BC-433E-ABFF-A89DFA795283}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EA8DBC12-60BC-433E-ABFF-A89DFA795283}.Debug|Any CPU.Build.0 = Debug|Any CPU {EA8DBC12-60BC-433E-ABFF-A89DFA795283}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -235,12 +235,12 @@ Global {EA8DBC12-60BC-433E-ABFF-A89DFA795283}.Release|x64.Build.0 = Release|Any CPU {EA8DBC12-60BC-433E-ABFF-A89DFA795283}.Release|x86.ActiveCfg = Release|Any CPU {EA8DBC12-60BC-433E-ABFF-A89DFA795283}.Release|x86.Build.0 = Release|Any CPU - {EA8DBC12-60BC-433E-ABFF-A89DFA795283}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {EA8DBC12-60BC-433E-ABFF-A89DFA795283}.Checked|Any CPU.Build.0 = Debug|Any CPU - {EA8DBC12-60BC-433E-ABFF-A89DFA795283}.Checked|x64.ActiveCfg = Debug|Any CPU - {EA8DBC12-60BC-433E-ABFF-A89DFA795283}.Checked|x64.Build.0 = Debug|Any CPU - {EA8DBC12-60BC-433E-ABFF-A89DFA795283}.Checked|x86.ActiveCfg = Debug|Any CPU - {EA8DBC12-60BC-433E-ABFF-A89DFA795283}.Checked|x86.Build.0 = Debug|Any CPU + {25D66424-2EAF-464D-8460-10C04EDEF3C3}.Checked|Any CPU.ActiveCfg = Release|Any CPU + {25D66424-2EAF-464D-8460-10C04EDEF3C3}.Checked|Any CPU.Build.0 = Release|Any CPU + {25D66424-2EAF-464D-8460-10C04EDEF3C3}.Checked|x64.ActiveCfg = Release|Any CPU + {25D66424-2EAF-464D-8460-10C04EDEF3C3}.Checked|x64.Build.0 = Release|Any CPU + {25D66424-2EAF-464D-8460-10C04EDEF3C3}.Checked|x86.ActiveCfg = Release|Any CPU + {25D66424-2EAF-464D-8460-10C04EDEF3C3}.Checked|x86.Build.0 = Release|Any CPU {25D66424-2EAF-464D-8460-10C04EDEF3C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {25D66424-2EAF-464D-8460-10C04EDEF3C3}.Debug|Any CPU.Build.0 = Debug|Any CPU {25D66424-2EAF-464D-8460-10C04EDEF3C3}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -315,7 +315,7 @@ Global {94B59BA0-491F-4B59-ADFF-A057EC3EC835} = {B1678CCD-95C8-4419-B9F9-14A03061BE4B} {1FF4CC8E-49C3-42A0-A6E0-2E5908455FBA} = {FB99AC59-1744-4F12-A4B0-0D54FCA048BF} {1B248B4C-7584-4C04-850A-A50EB592052C} = {E1AEBD5D-AE4E-4F61-B9ED-AEF950B0CC33} - {07F19F91-D438-428D-99F0-61DAD87E78BA} = {E1AEBD5D-AE4E-4F61-B9ED-AEF950B0CC33} + {90CDAD9F-3ACC-43B0-9696-0C849FCD8C40} = {E1AEBD5D-AE4E-4F61-B9ED-AEF950B0CC33} {768B77B0-EA45-469D-B39E-545EB72F5A43} = {E1AEBD5D-AE4E-4F61-B9ED-AEF950B0CC33} {8671F164-F78C-44FA-93B7-A310F67890FE} = {D893B9AA-57C5-49E3-97B1-12CC62D84307} {4FC33B9B-1BCF-4D16-B886-DCA8F2B823C1} = {B1678CCD-95C8-4419-B9F9-14A03061BE4B} @@ -326,10 +326,6 @@ Global {049B7FD4-ACEF-4BCD-A7A7-75C9BBEC4EBF} = {FB99AC59-1744-4F12-A4B0-0D54FCA048BF} {866D295E-424A-4747-9417-CD7746936138} = {FB99AC59-1744-4F12-A4B0-0D54FCA048BF} {D3A329E3-0FEB-4136-9CB6-B38319B0FFA5} = {FB99AC59-1744-4F12-A4B0-0D54FCA048BF} - {1B248B4C-7584-4C04-850A-A50EB592052C} = {E1AEBD5D-AE4E-4F61-B9ED-AEF950B0CC33} - {90CDAD9F-3ACC-43B0-9696-0C849FCD8C40} = {E1AEBD5D-AE4E-4F61-B9ED-AEF950B0CC33} - {768B77B0-EA45-469D-B39E-545EB72F5A43} = {E1AEBD5D-AE4E-4F61-B9ED-AEF950B0CC33} - {8671F164-F78C-44FA-93B7-A310F67890FE} = {D893B9AA-57C5-49E3-97B1-12CC62D84307} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {D4031401-FEB5-4CCF-91C1-38F5646B2BFD} diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/AdditionalAttributesOnStub.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/AdditionalAttributesOnStub.cs index 8d86b4412e1b0..681e1e3a6d355 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/AdditionalAttributesOnStub.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/AdditionalAttributesOnStub.cs @@ -83,6 +83,7 @@ struct S { } +[CustomTypeMarshaller(typeof(S))] struct Native { public Native(S s) { } From 6768f8f1f2e6d3cf8cb3071869652d1db3923116 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 8 Mar 2022 11:01:14 -0800 Subject: [PATCH 16/24] Fix free path --- .../src/System/Runtime/InteropServices/ArrayMarshaller.cs | 4 ++-- .../tests/TestAssets/SharedTypes/NonBlittable.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libraries/Common/src/System/Runtime/InteropServices/ArrayMarshaller.cs b/src/libraries/Common/src/System/Runtime/InteropServices/ArrayMarshaller.cs index fcaeb8a3cb866..d7ce6f7401d5e 100644 --- a/src/libraries/Common/src/System/Runtime/InteropServices/ArrayMarshaller.cs +++ b/src/libraries/Common/src/System/Runtime/InteropServices/ArrayMarshaller.cs @@ -68,7 +68,7 @@ public ArrayMarshaller(T[]? managed, Span stackSpace, int sizeOfNativeElem public ReadOnlySpan GetNativeValuesSource(int length) { - return _allocatedMemory == IntPtr.Zero ? default : new Span((void*)_allocatedMemory, length * _sizeOfNativeElement); + return _allocatedMemory == IntPtr.Zero ? default : NativeValueStorage = new Span((void*)_allocatedMemory, length * _sizeOfNativeElement); } private Span NativeValueStorage { get; set; } @@ -146,7 +146,7 @@ public PtrArrayMarshaller(T*[]? managed, Span stackSpace, int sizeOfNative public ReadOnlySpan GetNativeValuesSource(int length) { - return _allocatedMemory == IntPtr.Zero ? default : new Span((void*)_allocatedMemory, length * _sizeOfNativeElement); + return _allocatedMemory == IntPtr.Zero ? default : NativeValueStorage = new Span((void*)_allocatedMemory, length * _sizeOfNativeElement); } private Span NativeValueStorage { get; set; } 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 b9edba83a7e5c..c2d256f51667f 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/NonBlittable.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/NonBlittable.cs @@ -314,7 +314,7 @@ public Span GetManagedValuesDestination(int length) public ReadOnlySpan GetNativeValuesSource(int length) { - return allocatedMemory == IntPtr.Zero ? default : new Span((void*)allocatedMemory, length * sizeOfNativeElement); + return allocatedMemory == IntPtr.Zero ? default : NativeValueStorage = new Span((void*)allocatedMemory, length * sizeOfNativeElement); } public ref byte GetPinnableReference() => ref NativeValueStorage.GetPinnableReference(); From 130ceb3b66b9986dae0764d0a985eeebe6f36bb3 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 11 Mar 2022 14:18:16 -0800 Subject: [PATCH 17/24] Add Direction and Features to the API. Update the analyzer to offer fixes to help provide the right shape for the described features. --- eng/generators.targets | 2 + .../Test.CoreLib/src/Test.CoreLib.csproj | 1 + .../Common/src/Interop/Interop.Ldap.cs | 6 +- .../Advapi32/Interop.LsaLookupNames2.cs | 2 +- .../CryptUI/Interop.CryptUIDlgCertificate.cs | 4 +- .../Windows/WebSocket/Interop.Structs.cs | 2 +- .../Windows/WinHttp/Interop.winhttp.cs | 6 +- .../Windows/WinHttp/Interop.winhttp_types.cs | 2 +- .../InteropServices/ArrayMarshaller.cs | 4 +- .../CustomTypeMarshallerDirection.cs | 31 ++ .../CustomTypeMarshallerFeatures.cs | 24 + .../GeneratedMarshallingAttribute.cs | 2 + .../InteropServices/HandleRefMarshaller.cs | 2 +- .../Diagnostics/Reader/UnsafeNativeMethods.cs | 4 +- .../src/Interop/Windows/Interop.Gdi32.cs | 2 +- .../src/System/Drawing/Imaging/BitmapData.cs | 2 +- .../src/System/Drawing/Imaging/ColorMatrix.cs | 2 +- .../Drawing/Imaging/MetafileHeaderEmf.cs | 2 +- .../Drawing/Imaging/MetafileHeaderWmf.cs | 2 +- .../Drawing/Imaging/WmfPlaceableFileHeader.cs | 2 +- .../MsQuic/Interop/MsQuicNativeMethods.cs | 4 +- .../Analyzers/AnalyzerDiagnostics.cs | 10 +- ...zer.cs => CustomTypeMarshallerAnalyzer.cs} | 445 +++++++++++----- .../Analyzers/CustomTypeMarshallerFixer.cs | 266 ++++++++++ .../Resources.Designer.cs | 302 ++++++++--- .../gen/LibraryImportGenerator/Resources.resx | 112 +++- .../ManualTypeMarshallingHelper.cs | 14 +- ...ributedMarshallingModelGeneratorFactory.cs | 7 +- .../MarshallingAttributeInfo.cs | 94 +--- .../Microsoft.Interop.SourceGeneration.csproj | 6 +- .../Resources.Designer.cs | 87 +--- .../Resources.resx | 27 - .../Ancillary.Interop.csproj | 2 + .../Ancillary.Interop/SpanMarshallers.cs | 10 +- .../CodeSnippets.cs | 29 +- ...s.cs => CustomTypeMarshallerFixerTests.cs} | 483 +++++++++++++----- .../TestAssets/SharedTypes/NonBlittable.cs | 12 +- 37 files changed, 1397 insertions(+), 617 deletions(-) create mode 100644 src/libraries/Common/src/System/Runtime/InteropServices/CustomTypeMarshallerDirection.cs create mode 100644 src/libraries/Common/src/System/Runtime/InteropServices/CustomTypeMarshallerFeatures.cs rename src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/{ManualTypeMarshallingAnalyzer.cs => CustomTypeMarshallerAnalyzer.cs} (55%) create mode 100644 src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/CustomTypeMarshallerFixer.cs rename src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/{ManualTypeMarshallingAnalyzerTests.cs => CustomTypeMarshallerFixerTests.cs} (62%) diff --git a/eng/generators.targets b/eng/generators.targets index 900a336969020..936aa4764df31 100644 --- a/eng/generators.targets +++ b/eng/generators.targets @@ -46,6 +46,8 @@ and '$(IncludeLibraryImportGeneratorSources)' == 'true'"> + + diff --git a/src/coreclr/nativeaot/Test.CoreLib/src/Test.CoreLib.csproj b/src/coreclr/nativeaot/Test.CoreLib/src/Test.CoreLib.csproj index 0cba6dfc1f5a2..2ac88f28c51f9 100644 --- a/src/coreclr/nativeaot/Test.CoreLib/src/Test.CoreLib.csproj +++ b/src/coreclr/nativeaot/Test.CoreLib/src/Test.CoreLib.csproj @@ -4,6 +4,7 @@ false netstandard2.0 true + $(DefineConstants);TEST_CORELIB diff --git a/src/libraries/Common/src/Interop/Interop.Ldap.cs b/src/libraries/Common/src/Interop/Interop.Ldap.cs index 7606c653300e7..cd9d098dcab67 100644 --- a/src/libraries/Common/src/Interop/Interop.Ldap.cs +++ b/src/libraries/Common/src/Interop/Interop.Ldap.cs @@ -45,7 +45,7 @@ internal struct SEC_WINNT_AUTH_IDENTITY_EX public int packageListLength; #if NET7_0_OR_GREATER - [CustomTypeMarshaller(typeof(SEC_WINNT_AUTH_IDENTITY_EX))] + [CustomTypeMarshaller(typeof(SEC_WINNT_AUTH_IDENTITY_EX), Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.UnmanagedResources)] #endif [StructLayout(LayoutKind.Sequential)] internal struct Native @@ -176,7 +176,7 @@ internal sealed class BerVal public IntPtr bv_val = IntPtr.Zero; #if NET7_0_OR_GREATER - [CustomTypeMarshaller(typeof(BerVal))] + [CustomTypeMarshaller(typeof(BerVal), Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] internal unsafe struct PinningMarshaller { private readonly BerVal _managed; @@ -215,7 +215,7 @@ internal struct LdapReferralCallback #if NET7_0_OR_GREATER public static readonly unsafe int Size = sizeof(Marshaller.Native); - [CustomTypeMarshaller(typeof(LdapReferralCallback))] + [CustomTypeMarshaller(typeof(LdapReferralCallback), Features = CustomTypeMarshallerFeatures.UnmanagedResources)] public unsafe struct Marshaller { public unsafe struct Native diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.LsaLookupNames2.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.LsaLookupNames2.cs index 51cc3ecb7c44b..7b0d02ff67778 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.LsaLookupNames2.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.LsaLookupNames2.cs @@ -26,7 +26,7 @@ internal struct MARSHALLED_UNICODE_STRING internal ushort MaximumLength; internal string Buffer; - [CustomTypeMarshaller(typeof(MARSHALLED_UNICODE_STRING))] + [CustomTypeMarshaller(typeof(MARSHALLED_UNICODE_STRING), Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.UnmanagedResources)] public struct Native { internal ushort Length; 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 15d634a7fd748..c56c00d7f4bbc 100644 --- a/src/libraries/Common/src/Interop/Windows/CryptUI/Interop.CryptUIDlgCertificate.cs +++ b/src/libraries/Common/src/Interop/Windows/CryptUI/Interop.CryptUIDlgCertificate.cs @@ -37,7 +37,7 @@ internal struct CRYPTUI_VIEWCERTIFICATE_STRUCTW internal uint nStartPage; #if NET7_0_OR_GREATER - [CustomTypeMarshaller(typeof(CRYPTUI_VIEWCERTIFICATE_STRUCTW))] + [CustomTypeMarshaller(typeof(CRYPTUI_VIEWCERTIFICATE_STRUCTW), Features = CustomTypeMarshallerFeatures.UnmanagedResources)] internal unsafe struct Native { private uint dwSize; @@ -140,7 +140,7 @@ internal struct CRYPTUI_SELECTCERTIFICATE_STRUCTW internal IntPtr hSelectedCertStore; #if NET7_0_OR_GREATER - [CustomTypeMarshaller(typeof(CRYPTUI_SELECTCERTIFICATE_STRUCTW))] + [CustomTypeMarshaller(typeof(CRYPTUI_SELECTCERTIFICATE_STRUCTW), Features = CustomTypeMarshallerFeatures.UnmanagedResources)] internal unsafe struct Native { private uint dwSize; diff --git a/src/libraries/Common/src/Interop/Windows/WebSocket/Interop.Structs.cs b/src/libraries/Common/src/Interop/Windows/WebSocket/Interop.Structs.cs index 8f3db8e88931c..a4219658a3a45 100644 --- a/src/libraries/Common/src/Interop/Windows/WebSocket/Interop.Structs.cs +++ b/src/libraries/Common/src/Interop/Windows/WebSocket/Interop.Structs.cs @@ -49,7 +49,7 @@ internal struct HttpHeader internal string Value; internal uint ValueLength; - [CustomTypeMarshaller(typeof(HttpHeader))] + [CustomTypeMarshaller(typeof(HttpHeader), Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.UnmanagedResources)] internal struct Native { private IntPtr Name; 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 54cc32da10224..8cfcb10465483 100644 --- a/src/libraries/Common/src/Interop/Windows/WinHttp/Interop.winhttp.cs +++ b/src/libraries/Common/src/Interop/Windows/WinHttp/Interop.winhttp.cs @@ -53,7 +53,7 @@ public static partial bool WinHttpAddRequestHeaders( uint modifiers); #if NET7_0_OR_GREATER - [CustomTypeMarshaller(typeof(StringBuilder))] + [CustomTypeMarshaller(typeof(StringBuilder), Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.TwoStageMarshalling)] private unsafe struct SimpleStringBufferMarshaller { public SimpleStringBufferMarshaller(StringBuilder builder) @@ -65,7 +65,9 @@ public SimpleStringBufferMarshaller(StringBuilder builder) builder.CopyTo(0, buffer, length - 1); } - public void* Value { get; } + private void* Value { get; } + + public void* ToNativeValue() => Value; public void FreeNative() { 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 bcfc21dd767c6..0a0c24292ca9c 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 @@ -259,7 +259,7 @@ public struct WINHTTP_AUTOPROXY_OPTIONS [MarshalAs(UnmanagedType.Bool)] public bool AutoLoginIfChallenged; #if NET7_0_OR_GREATER - [CustomTypeMarshaller(typeof(WINHTTP_AUTOPROXY_OPTIONS))] + [CustomTypeMarshaller(typeof(WINHTTP_AUTOPROXY_OPTIONS), Features = CustomTypeMarshallerFeatures.UnmanagedResources)] public struct Native { private uint Flags; diff --git a/src/libraries/Common/src/System/Runtime/InteropServices/ArrayMarshaller.cs b/src/libraries/Common/src/System/Runtime/InteropServices/ArrayMarshaller.cs index d7ce6f7401d5e..f5c5277a7d373 100644 --- a/src/libraries/Common/src/System/Runtime/InteropServices/ArrayMarshaller.cs +++ b/src/libraries/Common/src/System/Runtime/InteropServices/ArrayMarshaller.cs @@ -15,7 +15,7 @@ namespace System.Runtime.InteropServices.GeneratedMarshalling // Stack-alloc threshold set to 256 bytes to enable small arrays to be passed on the stack. // Number kept small to ensure that P/Invokes with a lot of array parameters doesn't // blow the stack since this is a new optimization in the code-generated interop. - [CustomTypeMarshaller(typeof(CustomTypeMarshallerAttribute.GenericPlaceholder[]), CustomTypeMarshallerKind.LinearCollection, BufferSize = 0x200)] + [CustomTypeMarshaller(typeof(CustomTypeMarshallerAttribute.GenericPlaceholder[]), CustomTypeMarshallerKind.LinearCollection, Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.CallerAllocatedBuffer | CustomTypeMarshallerFeatures.TwoStageMarshalling, BufferSize = 0x200)] #if LIBRARYIMPORT_GENERATOR_TEST public #else @@ -93,7 +93,7 @@ public void FreeNative() // Stack-alloc threshold set to 256 bytes to enable small arrays to be passed on the stack. // Number kept small to ensure that P/Invokes with a lot of array parameters doesn't // blow the stack since this is a new optimization in the code-generated interop. - [CustomTypeMarshaller(typeof(CustomTypeMarshallerAttribute.GenericPlaceholder*[]), CustomTypeMarshallerKind.LinearCollection, BufferSize = 0x200)] + [CustomTypeMarshaller(typeof(CustomTypeMarshallerAttribute.GenericPlaceholder*[]), CustomTypeMarshallerKind.LinearCollection, Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.CallerAllocatedBuffer | CustomTypeMarshallerFeatures.TwoStageMarshalling, BufferSize = 0x200)] #if LIBRARYIMPORT_GENERATOR_TEST public #else diff --git a/src/libraries/Common/src/System/Runtime/InteropServices/CustomTypeMarshallerDirection.cs b/src/libraries/Common/src/System/Runtime/InteropServices/CustomTypeMarshallerDirection.cs new file mode 100644 index 0000000000000..6b31f7afb95ff --- /dev/null +++ b/src/libraries/Common/src/System/Runtime/InteropServices/CustomTypeMarshallerDirection.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable enable + +#if !TEST_CORELIB +using System.ComponentModel; +#endif + +// +// Types in this file are used for generated p/invokes (docs/design/features/source-generator-pinvokes.md). +// +namespace System.Runtime.InteropServices +{ + [Flags] +#if LIBRARYIMPORT_GENERATOR_TEST + public +#else + internal +#endif + enum CustomTypeMarshallerDirection + { +#if !TEST_CORELIB + [EditorBrowsable(EditorBrowsableState.Never)] +#endif + None = 0, + In = 0x1, + Out = 0x2, + Ref = In | Out, + } +} diff --git a/src/libraries/Common/src/System/Runtime/InteropServices/CustomTypeMarshallerFeatures.cs b/src/libraries/Common/src/System/Runtime/InteropServices/CustomTypeMarshallerFeatures.cs new file mode 100644 index 0000000000000..292f6337b163a --- /dev/null +++ b/src/libraries/Common/src/System/Runtime/InteropServices/CustomTypeMarshallerFeatures.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable enable + +// +// Types in this file are used for generated p/invokes (docs/design/features/source-generator-pinvokes.md). +// +namespace System.Runtime.InteropServices +{ + [Flags] +#if LIBRARYIMPORT_GENERATOR_TEST + public +#else + internal +#endif + enum CustomTypeMarshallerFeatures + { + None = 0, + UnmanagedResources = 0x1, + CallerAllocatedBuffer = 0x2, + TwoStageMarshalling = 0x4 + } +} diff --git a/src/libraries/Common/src/System/Runtime/InteropServices/GeneratedMarshallingAttribute.cs b/src/libraries/Common/src/System/Runtime/InteropServices/GeneratedMarshallingAttribute.cs index bf7184426f711..b7fc0ff8dc4d9 100644 --- a/src/libraries/Common/src/System/Runtime/InteropServices/GeneratedMarshallingAttribute.cs +++ b/src/libraries/Common/src/System/Runtime/InteropServices/GeneratedMarshallingAttribute.cs @@ -81,6 +81,8 @@ public CustomTypeMarshallerAttribute(Type managedType, CustomTypeMarshallerKind public Type ManagedType { get; } public CustomTypeMarshallerKind MarshallerKind { get; } public int BufferSize { get; set; } + public CustomTypeMarshallerDirection Direction { get; set; } = CustomTypeMarshallerDirection.Ref; + public CustomTypeMarshallerFeatures Features { get; set; } /// /// This type is used as a placeholder for the first generic parameter when generic parameters cannot be used diff --git a/src/libraries/Common/src/System/Runtime/InteropServices/HandleRefMarshaller.cs b/src/libraries/Common/src/System/Runtime/InteropServices/HandleRefMarshaller.cs index 478dfcc6b189d..d5b0a308bc6da 100644 --- a/src/libraries/Common/src/System/Runtime/InteropServices/HandleRefMarshaller.cs +++ b/src/libraries/Common/src/System/Runtime/InteropServices/HandleRefMarshaller.cs @@ -6,7 +6,7 @@ namespace System.Runtime.InteropServices.GeneratedMarshalling { - [CustomTypeMarshaller(typeof(HandleRef))] + [CustomTypeMarshaller(typeof(HandleRef), Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.TwoStageMarshalling)] internal struct HandleRefMarshaller { private HandleRef _handle; 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 a34cd55a6c478..c3bb44291ada7 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 @@ -353,7 +353,7 @@ internal struct EvtRpcLogin public CoTaskMemUnicodeSafeHandle Password; public int Flags; #if NET7_0_OR_GREATER - [CustomTypeMarshaller(typeof(EvtRpcLogin))] + [CustomTypeMarshaller(typeof(EvtRpcLogin), Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.TwoStageMarshalling)] public struct Marshaller { public struct Native @@ -700,7 +700,7 @@ internal struct EvtStringVariant public uint Type; #if NET7_0_OR_GREATER - [CustomTypeMarshaller(typeof(EvtStringVariant))] + [CustomTypeMarshaller(typeof(EvtStringVariant), Features = CustomTypeMarshallerFeatures.UnmanagedResources)] [StructLayout(LayoutKind.Explicit)] public struct Native { 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 6ab96d1af0c61..ed59d6ed2a30d 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 - [CustomTypeMarshaller(typeof(DOCINFO))] + [CustomTypeMarshaller(typeof(DOCINFO), Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.UnmanagedResources)] internal struct Native { internal int cbSize; diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/BitmapData.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/BitmapData.cs index 94f33eab4c7f9..4817da34d12e7 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/BitmapData.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/BitmapData.cs @@ -98,7 +98,7 @@ public int Reserved internal ref int GetPinnableReference() => ref _width; #if NET7_0_OR_GREATER - [CustomTypeMarshaller(typeof(BitmapData))] + [CustomTypeMarshaller(typeof(BitmapData), Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] internal unsafe struct PinningMarshaller { private readonly BitmapData _managed; diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/ColorMatrix.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/ColorMatrix.cs index 0dd6d93f83621..336d6c68a0f93 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/ColorMatrix.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/ColorMatrix.cs @@ -396,7 +396,7 @@ internal float[][] GetMatrix() internal ref float GetPinnableReference() => ref _matrix00; #if NET7_0_OR_GREATER - [CustomTypeMarshaller(typeof(ColorMatrix))] + [CustomTypeMarshaller(typeof(ColorMatrix), Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] internal unsafe struct PinningMarshaller { private readonly ColorMatrix _managed; diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/MetafileHeaderEmf.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/MetafileHeaderEmf.cs index 614b503918a8b..55575087df76e 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/MetafileHeaderEmf.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/MetafileHeaderEmf.cs @@ -34,7 +34,7 @@ internal sealed class MetafileHeaderEmf internal ref byte GetPinnableReference() => ref Unsafe.As(ref type); #if NET7_0_OR_GREATER - [CustomTypeMarshaller(typeof(MetafileHeaderEmf))] + [CustomTypeMarshaller(typeof(MetafileHeaderEmf), Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] internal unsafe struct PinningMarshaller { private readonly MetafileHeaderEmf _managed; diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/MetafileHeaderWmf.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/MetafileHeaderWmf.cs index 42ef89d497f1e..3295b5ca55434 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/MetafileHeaderWmf.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/MetafileHeaderWmf.cs @@ -55,7 +55,7 @@ internal sealed class MetafileHeaderWmf public int LogicalDpiY; #if NET7_0_OR_GREATER - [CustomTypeMarshaller(typeof(MetafileHeaderWmf))] + [CustomTypeMarshaller(typeof(MetafileHeaderWmf), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] internal unsafe struct InPlaceMarshaller { [StructLayout(LayoutKind.Sequential, Pack = 8)] diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/WmfPlaceableFileHeader.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/WmfPlaceableFileHeader.cs index 2010db9eb60bd..1f7d7cdfd70bb 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/WmfPlaceableFileHeader.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/WmfPlaceableFileHeader.cs @@ -106,7 +106,7 @@ public short Checksum internal ref int GetPinnableReference() => ref _key; #if NET7_0_OR_GREATER - [CustomTypeMarshaller(typeof(WmfPlaceableFileHeader))] + [CustomTypeMarshaller(typeof(WmfPlaceableFileHeader), Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] internal unsafe struct PinningMarshaller { private readonly WmfPlaceableFileHeader _managed; diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicNativeMethods.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicNativeMethods.cs index 5de546f20a9d3..f19e664fa21e9 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicNativeMethods.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicNativeMethods.cs @@ -97,7 +97,7 @@ internal struct RegistrationConfig internal string AppName; internal QUIC_EXECUTION_PROFILE ExecutionProfile; - [CustomTypeMarshaller(typeof(RegistrationConfig))] + [CustomTypeMarshaller(typeof(RegistrationConfig), Features = CustomTypeMarshallerFeatures.UnmanagedResources)] [StructLayout(LayoutKind.Sequential)] public struct Native { @@ -251,7 +251,7 @@ internal struct CredentialConfig // TODO: define delegate for AsyncHandler and make proper use of it. internal IntPtr AsyncHandler; - [CustomTypeMarshaller(typeof(CredentialConfig))] + [CustomTypeMarshaller(typeof(CredentialConfig), Features = CustomTypeMarshallerFeatures.UnmanagedResources)] [StructLayout(LayoutKind.Sequential)] public struct Native { diff --git a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/AnalyzerDiagnostics.cs b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/AnalyzerDiagnostics.cs index 2468b9faf31cc..b4ff5b12d7c4d 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/AnalyzerDiagnostics.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/AnalyzerDiagnostics.cs @@ -16,20 +16,20 @@ public static class Ids // ManualTypeMarshalling public const string MarshallerTypeMustSpecifyManagedType = Prefix + "001"; - public const string MarshallerKindMustBeValid = Prefix + "002"; + public const string CustomTypeMarshallerAttributeMustBeValid = Prefix + "002"; public const string NativeTypeMustHaveCustomTypeMarshallerAttribute = Prefix + "003"; public const string NativeTypeMustBeBlittable = Prefix + "004"; public const string GetPinnableReferenceReturnTypeBlittable = Prefix + "005"; public const string NativeTypeMustBePointerSized = Prefix + "006"; - public const string NativeTypeMustHaveRequiredShape = Prefix + "007"; - public const string ValuePropertyMustHaveSetter = Prefix + "008"; - public const string ValuePropertyMustHaveGetter = Prefix + "009"; + public const string CustomMarshallerTypeMustHaveRequiredShape = Prefix + "007"; + public const string CustomMarshallerTypeMustSupportDirection = Prefix + "008"; + public const string Unused = Prefix + "009"; public const string GetPinnableReferenceShouldSupportAllocatingMarshallingFallback = Prefix + "010"; public const string CallerAllocMarshallingShouldSupportAllocatingMarshallingFallback = Prefix + "011"; public const string CallerAllocConstructorMustHaveStackBufferSize = Prefix + "012"; public const string RefValuePropertyUnsupported = Prefix + "014"; public const string NativeGenericTypeMustBeClosedOrMatchArity = Prefix + "016"; - public const string MarshallerGetPinnableReferenceRequiresValueProperty = Prefix + "018"; + public const string MarshallerGetPinnableReferenceRequiresTwoStageMarshalling = Prefix + "018"; // Migration from DllImport to LibraryImport public const string ConvertToLibraryImport = Prefix + "015"; diff --git a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/ManualTypeMarshallingAnalyzer.cs b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/CustomTypeMarshallerAnalyzer.cs similarity index 55% rename from src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/ManualTypeMarshallingAnalyzer.cs rename to src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/CustomTypeMarshallerAnalyzer.cs index 80ad3f2d12fda..0cc5c0d39a019 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/ManualTypeMarshallingAnalyzer.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/CustomTypeMarshallerAnalyzer.cs @@ -14,10 +14,22 @@ namespace Microsoft.Interop.Analyzers { [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] - public class ManualTypeMarshallingAnalyzer : DiagnosticAnalyzer + public class CustomTypeMarshallerAnalyzer : DiagnosticAnalyzer { private const string Category = "Usage"; + public static class MissingMemberNames + { + public const string MissingMemberNameKey = nameof(MissingMemberNames); + public const char Delimiter = ' '; + + public const string ValueManagedToNativeConstructor = nameof(ValueManagedToNativeConstructor); + public const string ValueCallerAllocatedBufferConstructor = nameof(ValueCallerAllocatedBufferConstructor); + public const string CollectionManagedToNativeConstructor = nameof(CollectionManagedToNativeConstructor); + public const string CollectionCallerAllocatedBufferConstructor = nameof(CollectionCallerAllocatedBufferConstructor); + public const string CollectionNativeElementSizeConstructor = nameof(CollectionNativeElementSizeConstructor); + } + public static readonly DiagnosticDescriptor MarshallerTypeMustSpecifyManagedTypeRule = new DiagnosticDescriptor( Ids.MarshallerTypeMustSpecifyManagedType, @@ -28,16 +40,36 @@ public class ManualTypeMarshallingAnalyzer : DiagnosticAnalyzer isEnabledByDefault: true, description: GetResourceString(nameof(Resources.MarshallerTypeMustSpecifyManagedTypeDescription))); + public static readonly DiagnosticDescriptor CustomTypeMarshallerAttributeMustBeValidRule = + new DiagnosticDescriptor( + Ids.CustomTypeMarshallerAttributeMustBeValid, + "CustomTypeMarshallerAttributeMustBeValid", + GetResourceString(nameof(Resources.CustomTypeMarshallerAttributeMustBeValidMessage)), + Category, + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: GetResourceString(nameof(Resources.CustomTypeMarshallerAttributeMustBeValidDescription))); + public static readonly DiagnosticDescriptor MarshallerKindMustBeValidRule = new DiagnosticDescriptor( - Ids.MarshallerKindMustBeValid, - "MarshallerKindMustBeValid", + Ids.CustomTypeMarshallerAttributeMustBeValid, + "CustomTypeMarshallerAttributeMustBeValid", GetResourceString(nameof(Resources.MarshallerKindMustBeValidMessage)), Category, DiagnosticSeverity.Error, isEnabledByDefault: true, description: GetResourceString(nameof(Resources.MarshallerKindMustBeValidDescription))); + public static readonly DiagnosticDescriptor MarshallerDirectionMustBeValidRule = + new DiagnosticDescriptor( + Ids.CustomTypeMarshallerAttributeMustBeValid, + "CustomTypeMarshallerAttributeMustBeValid", + GetResourceString(nameof(Resources.MarshallerDirectionMustBeValidMessage)), + Category, + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: GetResourceString(nameof(Resources.MarshallerDirectionMustBeValidDescription))); + public static readonly DiagnosticDescriptor NativeTypeMustHaveCustomTypeMarshallerAttributeRule = new DiagnosticDescriptor( Ids.NativeTypeMustHaveCustomTypeMarshallerAttribute, @@ -78,45 +110,125 @@ public class ManualTypeMarshallingAnalyzer : DiagnosticAnalyzer isEnabledByDefault: true, description: GetResourceString(nameof(Resources.NativeTypeMustBePointerSizedDescription))); - public static readonly DiagnosticDescriptor NativeTypeMustHaveRequiredShapeRule = + public static readonly DiagnosticDescriptor CustomMarshallerTypeMustSupportDirectionRule = + new DiagnosticDescriptor( + Ids.CustomMarshallerTypeMustSupportDirection, + "CustomMarshallerTypeMustSupportDirection", + GetResourceString(nameof(Resources.CustomMarshallerTypeMustSupportDirectionMessage)), + Category, + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: GetResourceString(nameof(Resources.CustomMarshallerTypeMustSupportDirectionDescription))); + + public static readonly DiagnosticDescriptor ValueInRequiresOneParameterConstructorRule = new DiagnosticDescriptor( - Ids.NativeTypeMustHaveRequiredShape, - "NativeTypeMustHaveRequiredShape", - GetResourceString(nameof(Resources.NativeTypeMustHaveRequiredShapeMessage)), + Ids.CustomMarshallerTypeMustHaveRequiredShape, + "CustomMarshallerTypeMustHaveRequiredShape", + GetResourceString(nameof(Resources.ValueInRequiresOneParameterConstructorMessage)), Category, DiagnosticSeverity.Error, isEnabledByDefault: true, - description: GetResourceString(nameof(Resources.NativeTypeMustHaveRequiredShapeDescription))); + description: GetResourceString(nameof(Resources.ValueInRequiresOneParameterConstructorDescription))); - public static readonly DiagnosticDescriptor CollectionNativeTypeMustHaveRequiredShapeRule = + public static readonly DiagnosticDescriptor LinearCollectionInRequiresTwoParameterConstructorRule = new DiagnosticDescriptor( - Ids.NativeTypeMustHaveRequiredShape, - "NativeTypeMustHaveRequiredShape", - GetResourceString(nameof(Resources.CollectionNativeTypeMustHaveRequiredShapeMessage)), + Ids.CustomMarshallerTypeMustHaveRequiredShape, + "CustomMarshallerTypeMustHaveRequiredShape", + GetResourceString(nameof(Resources.LinearCollectionInRequiresTwoParameterConstructorMessage)), Category, DiagnosticSeverity.Error, isEnabledByDefault: true, - description: GetResourceString(nameof(Resources.CollectionNativeTypeMustHaveRequiredShapeDescription))); + description: GetResourceString(nameof(Resources.LinearCollectionInRequiresTwoParameterConstructorDescription))); - public static readonly DiagnosticDescriptor ValuePropertyMustHaveSetterRule = + public static readonly DiagnosticDescriptor ValueInCallerAllocatedBufferRequiresSpanConstructorRule = new DiagnosticDescriptor( - Ids.ValuePropertyMustHaveSetter, - "ValuePropertyMustHaveSetter", - GetResourceString(nameof(Resources.ValuePropertyMustHaveSetterMessage)), + Ids.CustomMarshallerTypeMustHaveRequiredShape, + "CustomMarshallerTypeMustHaveRequiredShape", + GetResourceString(nameof(Resources.ValueInCallerAllocatedBufferRequiresSpanConstructorMessage)), Category, DiagnosticSeverity.Error, isEnabledByDefault: true, - description: GetResourceString(nameof(Resources.ValuePropertyMustHaveSetterDescription))); + description: GetResourceString(nameof(Resources.ValueInCallerAllocatedBufferRequiresSpanConstructorDescription))); - public static readonly DiagnosticDescriptor ValuePropertyMustHaveGetterRule = + public static readonly DiagnosticDescriptor LinearCollectionInCallerAllocatedBufferRequiresSpanConstructorRule = new DiagnosticDescriptor( - Ids.ValuePropertyMustHaveGetter, - "ValuePropertyMustHaveGetter", - GetResourceString(nameof(Resources.ValuePropertyMustHaveGetterMessage)), + Ids.CustomMarshallerTypeMustHaveRequiredShape, + "CustomMarshallerTypeMustHaveRequiredShape", + GetResourceString(nameof(Resources.LinearCollectionInCallerAllocatedBufferRequiresSpanConstructorMessage)), Category, DiagnosticSeverity.Error, isEnabledByDefault: true, - description: GetResourceString(nameof(Resources.ValuePropertyMustHaveGetterDescription))); + description: GetResourceString(nameof(Resources.LinearCollectionInCallerAllocatedBufferRequiresSpanConstructorDescription))); + + public static readonly DiagnosticDescriptor OutRequiresToManagedRule = + new DiagnosticDescriptor( + Ids.CustomMarshallerTypeMustHaveRequiredShape, + "CustomMarshallerTypeMustHaveRequiredShape", + GetResourceString(nameof(Resources.OutRequiresToManagedMessage)), + Category, + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: GetResourceString(nameof(Resources.OutRequiresToManagedDescription))); + + public static readonly DiagnosticDescriptor LinearCollectionInRequiresCollectionMethodsRule = + new DiagnosticDescriptor( + Ids.CustomMarshallerTypeMustHaveRequiredShape, + "CustomMarshallerTypeMustHaveRequiredShape", + GetResourceString(nameof(Resources.LinearCollectionInRequiresCollectionMethodsMessage)), + Category, + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: GetResourceString(nameof(Resources.LinearCollectionInRequiresCollectionMethodsDescription))); + + public static readonly DiagnosticDescriptor LinearCollectionOutRequiresCollectionMethodsRule = + new DiagnosticDescriptor( + Ids.CustomMarshallerTypeMustHaveRequiredShape, + "CustomMarshallerTypeMustHaveRequiredShape", + GetResourceString(nameof(Resources.LinearCollectionOutRequiresCollectionMethodsMessage)), + Category, + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: GetResourceString(nameof(Resources.LinearCollectionOutRequiresCollectionMethodsDescription))); + + public static readonly DiagnosticDescriptor LinearCollectionOutRequiresIntConstructorRule = + new DiagnosticDescriptor( + Ids.CustomMarshallerTypeMustHaveRequiredShape, + "CustomMarshallerTypeMustHaveRequiredShape", + GetResourceString(nameof(Resources.LinearCollectionOutRequiresIntConstructorMessage)), + Category, + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: GetResourceString(nameof(Resources.LinearCollectionOutRequiresIntConstructorDescription))); + + public static readonly DiagnosticDescriptor UnmanagedResourcesRequiresFreeNativeRule = + new DiagnosticDescriptor( + Ids.CustomMarshallerTypeMustHaveRequiredShape, + "CustomMarshallerTypeMustHaveRequiredShape", + GetResourceString(nameof(Resources.UnmanagedResourcesRequiresFreeNativeMessage)), + Category, + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: GetResourceString(nameof(Resources.UnmanagedResourcesRequiresFreeNativeDescription))); + + public static readonly DiagnosticDescriptor OutTwoStageMarshallingRequiresFromNativeValueRule = + new DiagnosticDescriptor( + Ids.CustomMarshallerTypeMustHaveRequiredShape, + "CustomMarshallerTypeMustHaveRequiredShape", + GetResourceString(nameof(Resources.OutTwoStageMarshallingRequiresFromNativeValueMessage)), + Category, + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: GetResourceString(nameof(Resources.OutTwoStageMarshallingRequiresFromNativeValueDescription))); + + public static readonly DiagnosticDescriptor InTwoStageMarshallingRequiresToNativeValueRule = + new DiagnosticDescriptor( + Ids.CustomMarshallerTypeMustHaveRequiredShape, + "CustomMarshallerTypeMustHaveRequiredShape", + GetResourceString(nameof(Resources.InTwoStageMarshallingRequiresToNativeValueMessage)), + Category, + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: GetResourceString(nameof(Resources.InTwoStageMarshallingRequiresToNativeValueDescription))); public static readonly DiagnosticDescriptor GetPinnableReferenceShouldSupportAllocatingMarshallingFallbackRule = new DiagnosticDescriptor( @@ -168,34 +280,41 @@ public class ManualTypeMarshallingAnalyzer : DiagnosticAnalyzer isEnabledByDefault: true, description: GetResourceString(nameof(Resources.NativeGenericTypeMustBeClosedOrMatchArityDescription))); - public static readonly DiagnosticDescriptor MarshallerGetPinnableReferenceRequiresValuePropertyRule = + public static readonly DiagnosticDescriptor MarshallerGetPinnableReferenceRequiresTwoStageMarshallingRule = new DiagnosticDescriptor( - Ids.MarshallerGetPinnableReferenceRequiresValueProperty, - "MarshallerGetPinnableReferenceRequiresValueProperty", - GetResourceString(nameof(Resources.MarshallerGetPinnableReferenceRequiresValuePropertyMessage)), + Ids.MarshallerGetPinnableReferenceRequiresTwoStageMarshalling, + "MarshallerGetPinnableReferenceRequiresTwoStageMarshalling", + GetResourceString(nameof(Resources.MarshallerGetPinnableReferenceRequiresTwoStageMarshallingMessage)), Category, DiagnosticSeverity.Error, isEnabledByDefault: true, - description: GetResourceString(nameof(Resources.MarshallerGetPinnableReferenceRequiresValuePropertyDescription))); + description: GetResourceString(nameof(Resources.MarshallerGetPinnableReferenceRequiresTwoStageMarshallingDescription))); public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create( MarshallerTypeMustSpecifyManagedTypeRule, + CustomTypeMarshallerAttributeMustBeValidRule, MarshallerKindMustBeValidRule, + MarshallerDirectionMustBeValidRule, NativeTypeMustHaveCustomTypeMarshallerAttributeRule, NativeTypeMustBeBlittableRule, GetPinnableReferenceReturnTypeBlittableRule, NativeTypeMustBePointerSizedRule, - NativeTypeMustHaveRequiredShapeRule, - CollectionNativeTypeMustHaveRequiredShapeRule, - ValuePropertyMustHaveSetterRule, - ValuePropertyMustHaveGetterRule, + ValueInRequiresOneParameterConstructorRule, + LinearCollectionInRequiresTwoParameterConstructorRule, + OutRequiresToManagedRule, + LinearCollectionInRequiresCollectionMethodsRule, + LinearCollectionOutRequiresCollectionMethodsRule, + LinearCollectionOutRequiresIntConstructorRule, + CustomMarshallerTypeMustSupportDirectionRule, + OutTwoStageMarshallingRequiresFromNativeValueRule, + InTwoStageMarshallingRequiresToNativeValueRule, GetPinnableReferenceShouldSupportAllocatingMarshallingFallbackRule, CallerAllocMarshallingShouldSupportAllocatingMarshallingFallbackRule, CallerAllocConstructorMustHaveBufferSizeRule, RefValuePropertyUnsupportedRule, NativeGenericTypeMustBeClosedOrMatchArityRule, - MarshallerGetPinnableReferenceRequiresValuePropertyRule); + MarshallerGetPinnableReferenceRequiresTwoStageMarshallingRule); public override void Initialize(AnalysisContext context) { @@ -347,7 +466,7 @@ private static bool TypeSymbolsConstructedFromEqualTypes(ITypeSymbol left, IType public void AnalyzeMarshallerType(SymbolAnalysisContext context) { INamedTypeSymbol marshallerType = (INamedTypeSymbol)context.Symbol; - (bool hasCustomTypeMarshallerAttribute, ITypeSymbol? type, CustomTypeMarshallerData? marshallerData) = ManualTypeMarshallingHelper.GetMarshallerShapeInfo(marshallerType); + (bool hasCustomTypeMarshallerAttribute, ITypeSymbol? type, CustomTypeMarshallerData? marshallerDataMaybe) = ManualTypeMarshallingHelper.GetMarshallerShapeInfo(marshallerType); type = ManualTypeMarshallingHelper.ResolveManagedType(type, marshallerType, context.Compilation); if (!hasCustomTypeMarshallerAttribute) @@ -360,9 +479,16 @@ public void AnalyzeMarshallerType(SymbolAnalysisContext context) return; } - if (marshallerData == null || !Enum.IsDefined(typeof(CustomTypeMarshallerKind), marshallerData.Value.Kind)) + if (marshallerDataMaybe is not { } marshallerData) + { + context.ReportDiagnostic(marshallerType.CreateDiagnostic(CustomTypeMarshallerAttributeMustBeValidRule, marshallerType.ToDisplayString())); + return; + } + + if (!Enum.IsDefined(typeof(CustomTypeMarshallerKind), marshallerData.Kind)) { context.ReportDiagnostic(marshallerType.CreateDiagnostic(MarshallerKindMustBeValidRule, marshallerType.ToDisplayString())); + return; } if (type is INamedTypeSymbol { IsUnboundGenericType: true } generic) @@ -379,24 +505,9 @@ public void AnalyzeMarshallerType(SymbolAnalysisContext context) type = generic.ConstructedFrom.Construct(marshallerType.TypeArguments, marshallerType.TypeArgumentNullableAnnotations); } - DiagnosticDescriptor requiredShapeRule = marshallerData.Value.Kind switch - { - CustomTypeMarshallerKind.Value => NativeTypeMustHaveRequiredShapeRule, - CustomTypeMarshallerKind.LinearCollection => CollectionNativeTypeMustHaveRequiredShapeRule, - _ => throw new InvalidOperationException() - }; - - if (!marshallerType.IsValueType) - { - context.ReportDiagnostic( - marshallerType.CreateDiagnostic( - requiredShapeRule, - marshallerType.ToDisplayString(), - type.ToDisplayString())); - } - - bool hasConstructor = false; - bool hasCallerAllocSpanConstructor = false; + IMethodSymbol? inConstructor = null; + IMethodSymbol? callerAllocatedSpanConstructor = null; + IMethodSymbol collectionOutConstructor = null; foreach (IMethodSymbol ctor in marshallerType.Constructors) { if (ctor.IsStatic) @@ -404,46 +515,117 @@ public void AnalyzeMarshallerType(SymbolAnalysisContext context) continue; } - hasConstructor = hasConstructor || ManualTypeMarshallingHelper.IsManagedToNativeConstructor(ctor, type, marshallerData.Value.Kind); + if (inConstructor is null && ManualTypeMarshallingHelper.IsManagedToNativeConstructor(ctor, type, marshallerData.Kind)) + { + inConstructor = ctor; + } + + if (callerAllocatedSpanConstructor is null && ManualTypeMarshallingHelper.IsCallerAllocatedSpanConstructor(ctor, type, _spanOfByte, marshallerData.Kind)) + { + callerAllocatedSpanConstructor = ctor; + } + if (collectionOutConstructor is null && ctor.Parameters.Length == 1 && ctor.Parameters[0].Type.SpecialType == SpecialType.System_Int32) + { + collectionOutConstructor = ctor; + } + } - if (!hasCallerAllocSpanConstructor && ManualTypeMarshallingHelper.IsCallerAllocatedSpanConstructor(ctor, type, _spanOfByte, marshallerData.Value.Kind)) + if (marshallerData.Direction.HasFlag(CustomTypeMarshallerDirection.In)) + { + if (inConstructor is null) { - hasCallerAllocSpanConstructor = true; - if (marshallerData.Value.BufferSize == null) + context.ReportDiagnostic( + marshallerType.CreateDiagnostic( + GetInConstructorShapeRule(marshallerData.Kind), + ImmutableDictionary.Empty.Add( + MissingMemberNames.MissingMemberNameKey, + GetInConstructorMissingMemberName(marshallerData.Kind)), + marshallerType.ToDisplayString(), + type.ToDisplayString())); + } + if (marshallerData.Features.HasFlag(CustomTypeMarshallerFeatures.CallerAllocatedBuffer)) + { + if (callerAllocatedSpanConstructor is null) + { + context.ReportDiagnostic( + marshallerType.CreateDiagnostic( + GetCallerAllocatedBufferConstructorShapeRule(marshallerData.Kind), + ImmutableDictionary.Empty.Add( + MissingMemberNames.MissingMemberNameKey, + GetCallerAllocatedBufferConstructorMissingMemberName(marshallerData.Kind)), + marshallerType.ToDisplayString(), + type.ToDisplayString())); + } + if (marshallerData.BufferSize == null) { context.ReportDiagnostic( - ctor.CreateDiagnostic( + (callerAllocatedSpanConstructor ?? (ISymbol)marshallerType).CreateDiagnostic( CallerAllocConstructorMustHaveBufferSizeRule, marshallerType.ToDisplayString())); } } + + // Validate that this type can support marshalling when stackalloc is not usable. + if (callerAllocatedSpanConstructor is not null && inConstructor is null) + { + context.ReportDiagnostic( + marshallerType.CreateDiagnostic( + CallerAllocMarshallingShouldSupportAllocatingMarshallingFallbackRule, + marshallerType.ToDisplayString())); + } } - bool hasToManaged = ManualTypeMarshallingHelper.HasToManagedMethod(marshallerType, type); + if (marshallerData.Direction.HasFlag(CustomTypeMarshallerDirection.Out) && !ManualTypeMarshallingHelper.HasToManagedMethod(marshallerType, type)) + { + context.ReportDiagnostic( + marshallerType.CreateDiagnostic( + OutRequiresToManagedRule, + ImmutableDictionary.Empty.Add( + MissingMemberNames.MissingMemberNameKey, + ShapeMemberNames.Value.ToManaged), + marshallerType.ToDisplayString())); + } - if (marshallerData.Value.Kind == CustomTypeMarshallerKind.LinearCollection) + if (marshallerData.Kind == CustomTypeMarshallerKind.LinearCollection) { - requiredShapeRule = CollectionNativeTypeMustHaveRequiredShapeRule; IMethodSymbol? getManagedValuesSourceMethod = ManualTypeMarshallingHelper.FindGetManagedValuesSourceMethod(marshallerType, _readOnlySpanOfT); IMethodSymbol? getManagedValuesDestinationMethod = ManualTypeMarshallingHelper.FindGetManagedValuesDestinationMethod(marshallerType, _spanOfT); IMethodSymbol? getNativeValuesSourceMethod = ManualTypeMarshallingHelper.FindGetNativeValuesSourceMethod(marshallerType, _readOnlySpanOfByte); IMethodSymbol? getNativeValuesDestinationMethod = ManualTypeMarshallingHelper.FindGetNativeValuesDestinationMethod(marshallerType, _spanOfByte); - if ((hasConstructor || hasCallerAllocSpanConstructor) && (getManagedValuesSourceMethod is null || getNativeValuesDestinationMethod is null)) + if (marshallerData.Direction.HasFlag(CustomTypeMarshallerDirection.In) && (getManagedValuesSourceMethod is null || getNativeValuesDestinationMethod is null)) { + var missingMembers = (getManagedValuesSourceMethod, getNativeValuesDestinationMethod) switch + { + (null, not null) => ShapeMemberNames.LinearCollection.GetManagedValuesSource, + (not null, null) => ShapeMemberNames.LinearCollection.GetNativeValuesDestination, + (null, null) => $"{ShapeMemberNames.LinearCollection.GetManagedValuesSource}{MissingMemberNames.Delimiter}{ShapeMemberNames.LinearCollection.GetNativeValuesDestination}", + (not null, not null) => string.Empty + }; context.ReportDiagnostic( marshallerType.CreateDiagnostic( - requiredShapeRule, - marshallerType.ToDisplayString(), - type.ToDisplayString())); + LinearCollectionInRequiresCollectionMethodsRule, + ImmutableDictionary.Empty.Add( + MissingMemberNames.MissingMemberNameKey, + missingMembers), + marshallerType.ToDisplayString())); } - if (hasToManaged && (getNativeValuesSourceMethod is null || getManagedValuesDestinationMethod is null)) + if (marshallerData.Direction.HasFlag(CustomTypeMarshallerDirection.Out) && (getNativeValuesSourceMethod is null || getManagedValuesDestinationMethod is null)) { + var missingMembers = (getNativeValuesSourceMethod, getManagedValuesDestinationMethod) switch + { + (not null, null) => ShapeMemberNames.LinearCollection.GetNativeValuesSource, + (null, not null) => ShapeMemberNames.LinearCollection.GetManagedValuesDestination, + (null, null) => $"{ShapeMemberNames.LinearCollection.GetNativeValuesSource}{MissingMemberNames.Delimiter}{ShapeMemberNames.LinearCollection.GetManagedValuesDestination}", + (not null, not null) => string.Empty + }; context.ReportDiagnostic( marshallerType.CreateDiagnostic( - requiredShapeRule, - marshallerType.ToDisplayString(), - type.ToDisplayString())); + LinearCollectionOutRequiresCollectionMethodsRule, + ImmutableDictionary.Empty.Add( + MissingMemberNames.MissingMemberNameKey, + missingMembers), + marshallerType.ToDisplayString())); } if (getManagedValuesSourceMethod is not null @@ -454,26 +636,38 @@ public void AnalyzeMarshallerType(SymbolAnalysisContext context) { // TODO: Diagnostic for mismatched element collection type } + if (marshallerData.Direction.HasFlag(CustomTypeMarshallerDirection.Out) && collectionOutConstructor is null) + { + context.ReportDiagnostic( + marshallerType.CreateDiagnostic( + LinearCollectionOutRequiresIntConstructorRule, + ImmutableDictionary.Empty.Add( + MissingMemberNames.MissingMemberNameKey, + MissingMemberNames.CollectionNativeElementSizeConstructor), + marshallerType.ToDisplayString())); + } } - // Validate that the native type has at least one marshalling method (either managed to native or native to managed) - if (!hasConstructor && !hasCallerAllocSpanConstructor && !hasToManaged) + // Validate that the native type has at least one marshalling direction (either managed to native or native to managed) + if ((marshallerData.Direction & CustomTypeMarshallerDirection.Ref) == CustomTypeMarshallerDirection.None) { context.ReportDiagnostic( marshallerType.CreateDiagnostic( - requiredShapeRule, - marshallerType.ToDisplayString(), - type.ToDisplayString())); + CustomMarshallerTypeMustSupportDirectionRule, + marshallerType.ToDisplayString())); } - // Validate that this type can support marshalling when stackalloc is not usable. - if (hasCallerAllocSpanConstructor && !hasConstructor) + if (marshallerData.Features.HasFlag(CustomTypeMarshallerFeatures.UnmanagedResources) && !ManualTypeMarshallingHelper.HasFreeNativeMethod(marshallerType)) { context.ReportDiagnostic( marshallerType.CreateDiagnostic( - CallerAllocMarshallingShouldSupportAllocatingMarshallingFallbackRule, - marshallerType.ToDisplayString())); + UnmanagedResourcesRequiresFreeNativeRule, + ImmutableDictionary.Empty.Add( + MissingMemberNames.MissingMemberNameKey, + ShapeMemberNames.Value.FreeNative), + marshallerType.ToDisplayString(), + type.ToDisplayString())); } IMethodSymbol? toNativeValueMethod = ManualTypeMarshallingHelper.FindToNativeValueMethod(marshallerType); @@ -482,7 +676,35 @@ public void AnalyzeMarshallerType(SymbolAnalysisContext context) ITypeSymbol nativeType = marshallerType; // If either ToNativeValue or FromNativeValue is provided, validate the scenarios where they are required. - ValidateTwoStageMarshalling(); + if (marshallerData.Features.HasFlag(CustomTypeMarshallerFeatures.TwoStageMarshalling)) + { + if (marshallerData.Direction.HasFlag(CustomTypeMarshallerDirection.In) && toNativeValueMethod is null) + { + context.ReportDiagnostic(marshallerType.CreateDiagnostic( + InTwoStageMarshallingRequiresToNativeValueRule, + ImmutableDictionary.Empty.Add( + MissingMemberNames.MissingMemberNameKey, + ShapeMemberNames.Value.ToNativeValue), + marshallerType.ToDisplayString())); + } + if (marshallerData.Direction.HasFlag(CustomTypeMarshallerDirection.Out) && fromNativeValueMethod is null) + { + context.ReportDiagnostic(marshallerType.CreateDiagnostic( + OutTwoStageMarshallingRequiresFromNativeValueRule, + ImmutableDictionary.Empty.Add( + MissingMemberNames.MissingMemberNameKey, + ShapeMemberNames.Value.FromNativeValue), + marshallerType.ToDisplayString())); + } + + // ToNativeValue and FromNativeValue must be provided with the same type. + if (toNativeValueMethod is not null + && fromNativeValueMethod is not null + && !SymbolEqualityComparer.Default.Equals(toNativeValueMethod.ReturnType, fromNativeValueMethod.Parameters[0].Type)) + { + // TODO: Add diagnostic. + } + } if (toNativeValueMethod is not null) { @@ -498,13 +720,13 @@ public void AnalyzeMarshallerType(SymbolAnalysisContext context) } else if (ManualTypeMarshallingHelper.FindGetPinnableReference(marshallerType) is IMethodSymbol marshallerGetPinnableReferenceMethod) { - // If we don't have a Value property, then we disallow a GetPinnableReference on the marshaler type. + // If we don't have a ToNativeValue method, then we disallow a GetPinnableReference on the marshaler type. // We do this since there is no valid use case that we can think of for a GetPinnableReference on a blittable type // being a requirement to calculate the value of the fields of the same blittable instance, // so we're pre-emptively blocking this until a use case is discovered. context.ReportDiagnostic( marshallerGetPinnableReferenceMethod.CreateDiagnostic( - MarshallerGetPinnableReferenceRequiresValuePropertyRule, + MarshallerGetPinnableReferenceRequiresTwoStageMarshallingRule, nativeType.ToDisplayString())); } @@ -525,7 +747,7 @@ public void AnalyzeMarshallerType(SymbolAnalysisContext context) context.ReportDiagnostic(managedGetPinnableReferenceMethod.CreateDiagnostic(GetPinnableReferenceReturnTypeBlittableRule)); } // Validate that our marshaler supports scenarios where GetPinnableReference cannot be used. - if (!hasConstructor || toNativeValueMethod is null) + if (!marshallerData.Direction.HasFlag(CustomTypeMarshallerDirection.In)) { context.ReportDiagnostic( type.CreateDiagnostic( @@ -549,41 +771,32 @@ IPointerTypeSymbol or managedGetPinnableReferenceMethod.ContainingType.ToDisplayString())); } } - - void ValidateTwoStageMarshalling() - { - bool hasTwoStageMarshalling = false; - if ((hasConstructor || hasCallerAllocSpanConstructor) && toNativeValueMethod is not null) - { - hasTwoStageMarshalling = true; - } - - if (hasToManaged && fromNativeValueMethod is not null) - { - hasTwoStageMarshalling = true; - } - - if (hasTwoStageMarshalling) - { - if ((hasConstructor || hasCallerAllocSpanConstructor) && toNativeValueMethod is null) - { - context.ReportDiagnostic(marshallerType.CreateDiagnostic(ValuePropertyMustHaveGetterRule, marshallerType.ToDisplayString())); - } - if (hasToManaged && fromNativeValueMethod is null) - { - context.ReportDiagnostic(marshallerType.CreateDiagnostic(ValuePropertyMustHaveSetterRule, marshallerType.ToDisplayString())); - } - } - - // ToNativeValue and FromNativeValue must be provided with the same type. - if (toNativeValueMethod is not null - && fromNativeValueMethod is not null - && !SymbolEqualityComparer.Default.Equals(toNativeValueMethod.ReturnType, fromNativeValueMethod.Parameters[0].Type)) - { - // TODO: Add diagnostic. - } - } } + + private DiagnosticDescriptor GetInConstructorShapeRule(CustomTypeMarshallerKind kind) => kind switch + { + CustomTypeMarshallerKind.Value => ValueInRequiresOneParameterConstructorRule, + CustomTypeMarshallerKind.LinearCollection => LinearCollectionInRequiresTwoParameterConstructorRule, + _ => throw new UnreachableException() + }; + private string GetInConstructorMissingMemberName(CustomTypeMarshallerKind kind) => kind switch + { + CustomTypeMarshallerKind.Value => MissingMemberNames.ValueManagedToNativeConstructor, + CustomTypeMarshallerKind.LinearCollection => MissingMemberNames.CollectionManagedToNativeConstructor, + _ => throw new UnreachableException() + }; + private DiagnosticDescriptor GetCallerAllocatedBufferConstructorShapeRule(CustomTypeMarshallerKind kind) => kind switch + { + CustomTypeMarshallerKind.Value => ValueInCallerAllocatedBufferRequiresSpanConstructorRule, + CustomTypeMarshallerKind.LinearCollection => LinearCollectionInCallerAllocatedBufferRequiresSpanConstructorRule, + _ => throw new UnreachableException() + }; + private string GetCallerAllocatedBufferConstructorMissingMemberName(CustomTypeMarshallerKind kind) => kind switch + { + CustomTypeMarshallerKind.Value => MissingMemberNames.ValueCallerAllocatedBufferConstructor, + CustomTypeMarshallerKind.LinearCollection => MissingMemberNames.CollectionCallerAllocatedBufferConstructor, + _ => throw new UnreachableException() + }; } } } diff --git a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/CustomTypeMarshallerFixer.cs b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/CustomTypeMarshallerFixer.cs new file mode 100644 index 0000000000000..158b845126ce1 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/CustomTypeMarshallerFixer.cs @@ -0,0 +1,266 @@ +// 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.Composition; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Editing; + +namespace Microsoft.Interop.Analyzers +{ + [ExportCodeFixProvider(LanguageNames.CSharp, LanguageNames.VisualBasic), Shared] + public class CustomTypeMarshallerFixer : CodeFixProvider + { + private class CustomFixAllProvider : DocumentBasedFixAllProvider + { + protected override async Task FixAllAsync(FixAllContext fixAllContext, Document document, ImmutableArray diagnostics) + { + SyntaxNode? root = await document.GetSyntaxRootAsync(fixAllContext.CancellationToken).ConfigureAwait(false); + if (root == null) + return document; + + var editor = await DocumentEditor.CreateAsync(document, fixAllContext.CancellationToken).ConfigureAwait(false); + + foreach (var diagnosticsBySpan in diagnostics.GroupBy(d => d.Location.SourceSpan)) + { + SyntaxNode node = root.FindNode(diagnosticsBySpan.Key); + var (missingMemberNames, _) = GetRequiredShapeMissingMemberNames(diagnosticsBySpan); + ITypeSymbol marshallerType = (ITypeSymbol)editor.SemanticModel.GetDeclaredSymbol(node); + editor.ReplaceNode(node, (node, gen) => AddMissingMembers(node, marshallerType, missingMemberNames, editor.SemanticModel.Compilation, gen, fixAllContext.CancellationToken)); + } + return editor.GetChangedDocument(); + } + } + + public override FixAllProvider? GetFixAllProvider() => new CustomFixAllProvider(); + + public override ImmutableArray FixableDiagnosticIds { get; } = + ImmutableArray.Create( + AnalyzerDiagnostics.Ids.CustomMarshallerTypeMustHaveRequiredShape, + AnalyzerDiagnostics.Ids.CallerAllocMarshallingShouldSupportAllocatingMarshallingFallback); + + public override async Task RegisterCodeFixesAsync(CodeFixContext context) + { + Document doc = context.Document; + SyntaxNode? root = await doc.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); + if (root == null) + return; + + SyntaxNode node = root.FindNode(context.Span); + var (missingMemberNames, diagnostics) = GetRequiredShapeMissingMemberNames(context.Diagnostics); + + if (diagnostics.Count > 0) + { + context.RegisterCodeFix( + CodeAction.Create( + Resources.AddMissingCustomTypeMarshallerMembers, + ct => AddMissingMembers(doc, node, missingMemberNames, ct), + nameof(Resources.AddMissingCustomTypeMarshallerMembers)), + diagnostics); + } + } + + private static (List missingMembers, List fixedDiagnostics) GetRequiredShapeMissingMemberNames(IEnumerable diagnostics) + { + List missingMemberNames = new(); + List requiredShapeDiagnostics = new(); + foreach (var diagnostic in diagnostics) + { + if (diagnostic.Id == AnalyzerDiagnostics.Ids.CustomMarshallerTypeMustHaveRequiredShape) + { + requiredShapeDiagnostics.Add(diagnostic); + if (diagnostic.Properties.TryGetValue(CustomTypeMarshallerAnalyzer.MissingMemberNames.MissingMemberNameKey, out string missingMembers)) + { + missingMemberNames.AddRange(missingMembers.Split(CustomTypeMarshallerAnalyzer.MissingMemberNames.Delimiter)); + } + } + } + + return (missingMemberNames, requiredShapeDiagnostics); + } + + private static async Task AddMissingMembers(Document doc, SyntaxNode node, List missingMemberNames, CancellationToken ct) + { + var editor = await DocumentEditor.CreateAsync(doc, ct).ConfigureAwait(false); + var gen = editor.Generator; + + SyntaxNode updatedDeclaration = AddMissingMembers(node, (ITypeSymbol)editor.SemanticModel.GetDeclaredSymbol(node, ct), missingMemberNames, editor.SemanticModel.Compilation, gen, ct); + + editor.ReplaceNode(node, updatedDeclaration); + + return editor.GetChangedDocument(); + } + + private static SyntaxNode AddMissingMembers(SyntaxNode node, ITypeSymbol + marshallerType, List missingMemberNames, Compilation compilation, SyntaxGenerator gen, CancellationToken ct) + { + INamedTypeSymbol @byte = compilation.GetSpecialType(SpecialType.System_Byte); + INamedTypeSymbol @object = compilation.GetSpecialType(SpecialType.System_Object); + INamedTypeSymbol spanOfT = compilation.GetTypeByMetadataName(TypeNames.System_Span_Metadata)!; + INamedTypeSymbol spanOfByte = spanOfT.Construct(@byte)!; + INamedTypeSymbol readOnlySpanOfT = compilation.GetTypeByMetadataName(TypeNames.System_ReadOnlySpan_Metadata)!; + INamedTypeSymbol readOnlySpanOfByte = readOnlySpanOfT.Construct(@byte)!; + INamedTypeSymbol int32 = compilation.GetSpecialType(SpecialType.System_Int32); + + SyntaxNode updatedDeclaration = node; + + + (_, ITypeSymbol managedType, _) = ManualTypeMarshallingHelper.GetMarshallerShapeInfo(marshallerType); + + IMethodSymbol? fromNativeValueMethod = ManualTypeMarshallingHelper.FindFromNativeValueMethod(marshallerType); + IMethodSymbol? toNativeValueMethod = ManualTypeMarshallingHelper.FindToNativeValueMethod(marshallerType); + IMethodSymbol? getManagedValuesSourceMethod = ManualTypeMarshallingHelper.FindGetManagedValuesSourceMethod(marshallerType, readOnlySpanOfT); + IMethodSymbol? getManagedValuesDestinationMethod = ManualTypeMarshallingHelper.FindGetManagedValuesDestinationMethod(marshallerType, spanOfT); + + SyntaxNode[] throwNotImplementedStatements = new[] + { + gen.ThrowStatement(gen.ObjectCreationExpression(gen.DottedName("System.NotImplementedException"))) + }; + + foreach (string missingMemberName in missingMemberNames) + { + switch (missingMemberName) + { + case CustomTypeMarshallerAnalyzer.MissingMemberNames.ValueManagedToNativeConstructor: + updatedDeclaration = gen.AddMembers(updatedDeclaration, gen.ConstructorDeclaration( + gen.GetName(node), + new[] + { + gen.ParameterDeclaration("managed", type: gen.TypeExpression(managedType)) + }, + accessibility: Accessibility.Public, + statements: throwNotImplementedStatements)); + break; + case CustomTypeMarshallerAnalyzer.MissingMemberNames.ValueCallerAllocatedBufferConstructor: + updatedDeclaration = gen.AddMembers(updatedDeclaration, gen.ConstructorDeclaration( + gen.GetName(node), + new[] + { + gen.ParameterDeclaration("managed", type: gen.TypeExpression(managedType)), + gen.ParameterDeclaration("buffer", type: gen.TypeExpression(spanOfByte)) + }, + accessibility: Accessibility.Public, + statements: throwNotImplementedStatements)); + break; + case CustomTypeMarshallerAnalyzer.MissingMemberNames.CollectionManagedToNativeConstructor: + updatedDeclaration = gen.AddMembers(updatedDeclaration, gen.ConstructorDeclaration( + gen.GetName(node), + new[] + { + gen.ParameterDeclaration("managed", type: gen.TypeExpression(managedType)), + gen.ParameterDeclaration("nativeElementSize", type: gen.TypeExpression(int32)) + }, + accessibility: Accessibility.Public, + statements: throwNotImplementedStatements)); + break; + case CustomTypeMarshallerAnalyzer.MissingMemberNames.CollectionCallerAllocatedBufferConstructor: + updatedDeclaration = gen.AddMembers(updatedDeclaration, gen.ConstructorDeclaration( + gen.GetName(node), + new[] + { + gen.ParameterDeclaration("managed", type: gen.TypeExpression(managedType)), + gen.ParameterDeclaration("buffer", type: gen.TypeExpression(spanOfByte)), + gen.ParameterDeclaration("nativeElementSize", type: gen.TypeExpression(int32)) + }, + accessibility: Accessibility.Public, + statements: throwNotImplementedStatements)); + break; + case CustomTypeMarshallerAnalyzer.MissingMemberNames.CollectionNativeElementSizeConstructor: + updatedDeclaration = gen.AddMembers(updatedDeclaration, gen.ConstructorDeclaration( + gen.GetName(node), + new[] + { + gen.ParameterDeclaration("nativeElementSize", type: gen.TypeExpression(int32)) + }, + accessibility: Accessibility.Public, + statements: throwNotImplementedStatements)); + break; + case ShapeMemberNames.Value.ToManaged: + updatedDeclaration = gen.AddMembers(updatedDeclaration, gen.MethodDeclaration( + ShapeMemberNames.Value.ToManaged, + returnType: gen.TypeExpression(managedType), + accessibility: Accessibility.Public, + statements: throwNotImplementedStatements)); + break; + case ShapeMemberNames.Value.FreeNative: + updatedDeclaration = gen.AddMembers(updatedDeclaration, gen.MethodDeclaration(ShapeMemberNames.Value.FreeNative, + accessibility: Accessibility.Public, + statements: throwNotImplementedStatements)); + break; + case ShapeMemberNames.Value.FromNativeValue: + updatedDeclaration = gen.AddMembers(updatedDeclaration, gen.MethodDeclaration( + ShapeMemberNames.Value.FromNativeValue, + parameters: new[] + { + gen.ParameterDeclaration("value", + type: gen.TypeExpression(toNativeValueMethod?.ReturnType ?? @byte)) + }, + accessibility: Accessibility.Public, + statements: throwNotImplementedStatements)); + break; + case ShapeMemberNames.Value.ToNativeValue: + updatedDeclaration = gen.AddMembers(updatedDeclaration, gen.MethodDeclaration( + ShapeMemberNames.Value.ToNativeValue, + returnType: gen.TypeExpression(fromNativeValueMethod?.Parameters[0].Type ?? @byte), + accessibility: Accessibility.Public, + statements: throwNotImplementedStatements)); + break; + case ShapeMemberNames.LinearCollection.GetManagedValuesSource: + INamedTypeSymbol? getManagedValuesDestinationReturnType = (INamedTypeSymbol?)getManagedValuesDestinationMethod?.ReturnType; + updatedDeclaration = gen.AddMembers(updatedDeclaration, gen.MethodDeclaration( + ShapeMemberNames.LinearCollection.GetManagedValuesSource, + returnType: gen.TypeExpression( + readOnlySpanOfT.Construct( + getManagedValuesDestinationReturnType?.TypeArguments[0] ?? @object)), + accessibility: Accessibility.Public, + statements: throwNotImplementedStatements)); + break; + case ShapeMemberNames.LinearCollection.GetNativeValuesDestination: + updatedDeclaration = gen.AddMembers(updatedDeclaration, gen.MethodDeclaration( + ShapeMemberNames.LinearCollection.GetNativeValuesDestination, + returnType: gen.TypeExpression(spanOfByte), + accessibility: Accessibility.Public, + statements: throwNotImplementedStatements)); + break; + case ShapeMemberNames.LinearCollection.GetNativeValuesSource: + updatedDeclaration = gen.AddMembers(updatedDeclaration, gen.MethodDeclaration( + ShapeMemberNames.LinearCollection.GetNativeValuesSource, + parameters: new[] + { + gen.ParameterDeclaration("numElements", type: gen.TypeExpression(int32)) + }, + returnType: gen.TypeExpression(readOnlySpanOfByte), + accessibility: Accessibility.Public, + statements: throwNotImplementedStatements)); + break; + case ShapeMemberNames.LinearCollection.GetManagedValuesDestination: + INamedTypeSymbol? getManagedValuesSourceReturnType = (INamedTypeSymbol?)getManagedValuesSourceMethod?.ReturnType; + updatedDeclaration = gen.AddMembers(updatedDeclaration, gen.MethodDeclaration( + ShapeMemberNames.LinearCollection.GetNativeValuesDestination, + parameters: new[] + { + gen.ParameterDeclaration("numElements", type: gen.TypeExpression(int32)) + }, + returnType: gen.TypeExpression( + spanOfT.Construct( + getManagedValuesSourceReturnType?.TypeArguments[0] ?? @object)), + accessibility: Accessibility.Public, + statements: throwNotImplementedStatements)); + break; + default: + break; + } + } + + return updatedDeclaration; + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Resources.Designer.cs b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Resources.Designer.cs index 8adacdffba593..96920cb6093c4 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Resources.Designer.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Resources.Designer.cs @@ -60,6 +60,15 @@ internal Resources() { } } + /// + /// Looks up a localized string similar to Add missing custom type marshaller members. + /// + internal static string AddMissingCustomTypeMarshallerMembers { + get { + return ResourceManager.GetString("AddMissingCustomTypeMarshallerMembers", resourceCulture); + } + } + /// /// Looks up a localized string similar to When a constructor taking a ''Span<byte> is specified on the native type, the type must set the BufferSize field on the 'System.Runtime.InteropServices.CustomTypeMarshallerAttribute' to provide the size of the caller-allocated buffer.. /// @@ -123,42 +132,6 @@ internal static string CannotForwardToDllImportTitle { } } - /// - /// Looks up a localized string similar to The 'BlittableTypeAttribute' and 'NativeMarshallingAttribute' attributes are mutually exclusive.. - /// - internal static string CannotHaveMultipleMarshallingAttributesDescription { - get { - return ResourceManager.GetString("CannotHaveMultipleMarshallingAttributesDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Type '{0}' is marked with 'BlittableTypeAttribute' and 'NativeMarshallingAttribute'. A type can only have one of these two attributes.. - /// - internal static string CannotHaveMultipleMarshallingAttributesMessage { - get { - return ResourceManager.GetString("CannotHaveMultipleMarshallingAttributesMessage", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to A 'LinearCollection'-kind native type must have at least one of the two marshalling methods as well as a 'ManagedValues' property of type 'Span<T>' for some 'T' and a 'NativeValueStorage' property of type 'Span<byte>' to enable marshalling the managed type.. - /// - internal static string CollectionNativeTypeMustHaveRequiredShapeDescription { - get { - return ResourceManager.GetString("CollectionNativeTypeMustHaveRequiredShapeDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The native type '{0}' must be a value type and have a constructor that takes two parameters, one of type '{1}' and an 'int', or have a parameterless instance method named 'ToManaged' that returns '{1}' as well as a 'ManagedValues' property of type 'Span<T>' for some 'T' and a 'NativeValueStorage' property of type 'Span<byte>'. - /// - internal static string CollectionNativeTypeMustHaveRequiredShapeMessage { - get { - return ResourceManager.GetString("CollectionNativeTypeMustHaveRequiredShapeMessage", resourceCulture); - } - } - /// /// Looks up a localized string similar to Source-generated P/Invokes will ignore any configuration that is not supported.. /// @@ -294,6 +267,42 @@ internal static string ConvertToLibraryImportWithSuffix { } } + /// + /// Looks up a localized string similar to A native must set the 'Direction' property on the 'CustomTypeMarshallerAttribute' to a value that sets at least one known flag value on the 'CustomTypeMarshallerDirection' enum. + /// + internal static string CustomMarshallerTypeMustSupportDirectionDescription { + get { + return ResourceManager.GetString("CustomMarshallerTypeMustSupportDirectionDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The native type '{0}' must set the 'Direction' property on the 'CustomTypeMarshallerAttribute' to a value that sets at least one known flag value on the 'CustomTypeMarshallerDirection' enum. + /// + internal static string CustomMarshallerTypeMustSupportDirectionMessage { + get { + return ResourceManager.GetString("CustomMarshallerTypeMustSupportDirectionMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The 'CustomTypeMarshallerAttribute' attribute must be semantically valid. + /// + internal static string CustomTypeMarshallerAttributeMustBeValidDescription { + get { + return ResourceManager.GetString("CustomTypeMarshallerAttributeMustBeValidDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The 'CustomTypeMarshallerAttribute' on '{0}' is semantically invalid. + /// + internal static string CustomTypeMarshallerAttributeMustBeValidMessage { + get { + return ResourceManager.GetString("CustomTypeMarshallerAttributeMustBeValidMessage", resourceCulture); + } + } + /// /// Looks up a localized string similar to The specified parameter needs to be marshalled from managed to native, but the native type '{0}' does not support it.. /// @@ -348,6 +357,24 @@ internal static string GetPinnableReferenceShouldSupportAllocatingMarshallingFal } } + /// + /// Looks up a localized string similar to The 'TwoStageMarshalling' feature requires a TNativeType FromNativeValue()' method for the 'In' direction.. + /// + internal static string InTwoStageMarshallingRequiresToNativeValueDescription { + get { + return ResourceManager.GetString("InTwoStageMarshallingRequiresToNativeValueDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The marshaller type '{0}' supports marshalling in the 'In' direction with the 'TwoStageMarshalling' feature must provide a 'ToNativeValue' instance method that returns the native value. + /// + internal static string InTwoStageMarshallingRequiresToNativeValueMessage { + get { + return ResourceManager.GetString("InTwoStageMarshallingRequiresToNativeValueMessage", resourceCulture); + } + } + /// /// Looks up a localized string similar to Method '{0}' is contained in a type '{1}' that is not marked 'partial'. P/Invoke source generation will ignore method '{0}'.. /// @@ -421,20 +448,128 @@ internal static string InvalidStringMarshallingConfigurationNotCustom { } /// - /// Looks up a localized string similar to The use cases for 'GetPinnableReference' are not applicable in any scenarios where a 'Value' property is not also required.. + /// Looks up a localized string similar to A 'LinearCollection'-kind native type that supports the 'CallerAllocatedBuffer' feature must provide a three-parameter constructor taking the managed type as the first parameter, a 'Span<byte>' as the second parameter, and the native size of the element as the second parameter. + /// + internal static string LinearCollectionInCallerAllocatedBufferRequiresSpanConstructorDescription { + get { + return ResourceManager.GetString("LinearCollectionInCallerAllocatedBufferRequiresSpanConstructorDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The type '{0}' specifies that it supports 'In' marshalling with the 'CallerAllocatedBuffer' feature for '{1}' but does not provide a three-parameter constructor that takes a '{1}' , a 'Span<byte>', and an 'int'. + /// + internal static string LinearCollectionInCallerAllocatedBufferRequiresSpanConstructorMessage { + get { + return ResourceManager.GetString("LinearCollectionInCallerAllocatedBufferRequiresSpanConstructorMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to A 'LinearCollection'-kind native type that supports marshalling in the 'In' direction must provide a 'GetManagedValuesSource' that returns a 'ReadOnlySpan<>' and a 'GetNativeValuesDestination' method that returns a 'Span<byte>'.. + /// + internal static string LinearCollectionInRequiresCollectionMethodsDescription { + get { + return ResourceManager.GetString("LinearCollectionInRequiresCollectionMethodsDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The type '{0}' specifies that is supports marshalling in the 'In' direction, but it does not provide a 'GetManagedValuesSource' that returns a 'ReadOnlySpan<>' and a 'GetNativeValuesDestination' method that returns a 'Span<byte>'. /// - internal static string MarshallerGetPinnableReferenceRequiresValuePropertyDescription { + internal static string LinearCollectionInRequiresCollectionMethodsMessage { get { - return ResourceManager.GetString("MarshallerGetPinnableReferenceRequiresValuePropertyDescription", resourceCulture); + return ResourceManager.GetString("LinearCollectionInRequiresCollectionMethodsMessage", resourceCulture); } } /// - /// Looks up a localized string similar to The 'GetPinnableReference' method cannot be provided on the native type '{0}' unless a 'Value' property is also provided. + /// Looks up a localized string similar to A 'LinearCollection'-kind native type must provide a two-parameter constructor taking the managed type as the first parameter and the native size of the element as the second parameter. /// - internal static string MarshallerGetPinnableReferenceRequiresValuePropertyMessage { + internal static string LinearCollectionInRequiresTwoParameterConstructorDescription { get { - return ResourceManager.GetString("MarshallerGetPinnableReferenceRequiresValuePropertyMessage", resourceCulture); + return ResourceManager.GetString("LinearCollectionInRequiresTwoParameterConstructorDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The type '{0}' specifies that it supports 'In' marshalling of '{1}' but does not provide a two-parameter constructor that takes a '{1}' as the first parameter and an 'int' as the second parameter. + /// + internal static string LinearCollectionInRequiresTwoParameterConstructorMessage { + get { + return ResourceManager.GetString("LinearCollectionInRequiresTwoParameterConstructorMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to A 'LinearCollection'-kind native type that supports marshalling in the 'Out' direction must provide a 'GetManagedValuesDestination' that takes an 'int' and returns a 'Span<>' and a 'GetNativeValuesSource' method that takes an 'int' and rreturns a 'ReadOnlySpan<byte>'.. + /// + internal static string LinearCollectionOutRequiresCollectionMethodsDescription { + get { + return ResourceManager.GetString("LinearCollectionOutRequiresCollectionMethodsDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The type '{0}' specifies that it supports marshalling in the 'Out' direction, but it does not provide a 'GetManagedValuesDestination' that takes an 'int' and returns a 'Span<>' and a 'GetNativeValuesSource' method that takes an 'int' and rreturns a 'ReadOnlySpan<byte>'. + /// + internal static string LinearCollectionOutRequiresCollectionMethodsMessage { + get { + return ResourceManager.GetString("LinearCollectionOutRequiresCollectionMethodsMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to A 'LinearCollection'-kind native type that supports marshalling in the 'Out' direction must provide a constructor that takes the size of the native element as an 'int'.. + /// + internal static string LinearCollectionOutRequiresIntConstructorDescription { + get { + return ResourceManager.GetString("LinearCollectionOutRequiresIntConstructorDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The type '{0}' specifies that it supports marshalling in the 'Out' direction, but it does not provide a constructor that takes the size of the native element as an 'int'.. + /// + internal static string LinearCollectionOutRequiresIntConstructorMessage { + get { + return ResourceManager.GetString("LinearCollectionOutRequiresIntConstructorMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The specified marshaller direction must be valid value of the 'System.Runtime.InteropServices.CustomMarshallerDirection' enum.. + /// + internal static string MarshallerDirectionMustBeValidDescription { + get { + return ResourceManager.GetString("MarshallerDirectionMustBeValidDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The specified custom marshaller direction for '{0}' is invalid. + /// + internal static string MarshallerDirectionMustBeValidMessage { + get { + return ResourceManager.GetString("MarshallerDirectionMustBeValidMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The use cases for 'GetPinnableReference' are not applicable in any scenarios where 'TwoStageMarshalling' is not also required.. + /// + internal static string MarshallerGetPinnableReferenceRequiresTwoStageMarshallingDescription { + get { + return ResourceManager.GetString("MarshallerGetPinnableReferenceRequiresTwoStageMarshallingDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The 'GetPinnableReference' method cannot be provided on the native type '{0}' unless the 'TwoStageMarshalling' feature is also supported. + /// + internal static string MarshallerGetPinnableReferenceRequiresTwoStageMarshallingMessage { + get { + return ResourceManager.GetString("MarshallerGetPinnableReferenceRequiresTwoStageMarshallingMessage", resourceCulture); } } @@ -448,7 +583,7 @@ internal static string MarshallerKindMustBeValidDescription { } /// - /// Looks up a localized string similar to The specified custom marshller kind for '{0}' is invalid. + /// Looks up a localized string similar to The specified custom marshaller kind for '{0}' is invalid. /// internal static string MarshallerKindMustBeValidMessage { get { @@ -474,15 +609,6 @@ internal static string MarshallerTypeMustSpecifyManagedTypeMessage { } } - /// - /// Looks up a localized string similar to The native type '{0}' must be a closed generic so the emitted code can use a specific instantiation.. - /// - internal static string NativeGenericTypeMustBeClosedDescription { - get { - return ResourceManager.GetString("NativeGenericTypeMustBeClosedDescription", resourceCulture); - } - } - /// /// Looks up a localized string similar to The native type '{0}' must be a closed generic or have the same number of generic parameters as the managed type so the emitted code can use a specific instantiation.. /// @@ -556,20 +682,38 @@ internal static string NativeTypeMustHaveCustomTypeMarshallerAttributeMessage { } /// - /// Looks up a localized string similar to A 'Value'-kind native type must have at least one of the two marshalling methods to enable marshalling the managed type.. + /// Looks up a localized string similar to A 'Value' or 'LinearCollection'-kind native type that supports marshalling in the 'Out' direction must provide a 'ToManaged' method that returns the managed type.. + /// + internal static string OutRequiresToManagedDescription { + get { + return ResourceManager.GetString("OutRequiresToManagedDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The type '{0}' specifies it supports marshalling in the 'Out' direction, but it does not provide a 'ToManaged' method that returns the managed type. /// - internal static string NativeTypeMustHaveRequiredShapeDescription { + internal static string OutRequiresToManagedMessage { get { - return ResourceManager.GetString("NativeTypeMustHaveRequiredShapeDescription", resourceCulture); + return ResourceManager.GetString("OutRequiresToManagedMessage", resourceCulture); } } /// - /// Looks up a localized string similar to The native type '{0}' must be a value type and have a constructor that takes one parameter of type '{1}' or a parameterless instance method named 'ToManaged' that returns '{1}'. + /// Looks up a localized string similar to The 'TwoStageMarshalling' feature requires a 'void FromNativeValue(TNativeType value)' method for the 'Out' direction.. /// - internal static string NativeTypeMustHaveRequiredShapeMessage { + internal static string OutTwoStageMarshallingRequiresFromNativeValueDescription { get { - return ResourceManager.GetString("NativeTypeMustHaveRequiredShapeMessage", resourceCulture); + return ResourceManager.GetString("OutTwoStageMarshallingRequiresFromNativeValueDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The marshaller type '{0}' supports marshalling in the 'Out' direction with the 'TwoStageMarshalling' feature, but it does not provide a 'FromNativeValue' instance method that returns 'void' and takes one parameter.. + /// + internal static string OutTwoStageMarshallingRequiresFromNativeValueMessage { + get { + return ResourceManager.GetString("OutTwoStageMarshallingRequiresFromNativeValueMessage", resourceCulture); } } @@ -682,38 +826,56 @@ internal static string TypeNotSupportedTitle { } /// - /// Looks up a localized string similar to The native type's 'Value' property must have a getter to support marshalling from managed to native.. + /// Looks up a localized string similar to The 'UnmanagedResources' feature requires a 'void FreeNative()' method.. + /// + internal static string UnmanagedResourcesRequiresFreeNativeDescription { + get { + return ResourceManager.GetString("UnmanagedResourcesRequiresFreeNativeDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The marshaller type '{0}' supports marshalling with the 'UnmanagedResources' feature, but it does not provide a parameterless 'FreeNative' instance method that returns 'void'. + /// + internal static string UnmanagedResourcesRequiresFreeNativeMessage { + get { + return ResourceManager.GetString("UnmanagedResourcesRequiresFreeNativeMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to A 'Value'-kind native type that supports the 'CallerAllocatedBuffer' feature must provide a two-parameter constructor taking the managed type and a 'Span<byte>' as parameters. /// - internal static string ValuePropertyMustHaveGetterDescription { + internal static string ValueInCallerAllocatedBufferRequiresSpanConstructorDescription { get { - return ResourceManager.GetString("ValuePropertyMustHaveGetterDescription", resourceCulture); + return ResourceManager.GetString("ValueInCallerAllocatedBufferRequiresSpanConstructorDescription", resourceCulture); } } /// - /// Looks up a localized string similar to The 'Value' property on the native type '{0}' must have a getter. + /// Looks up a localized string similar to The type '{0}' specifies that it supports 'In' marshalling with the 'CallerAllocatedBuffer' feature for '{1}' but does not provide a one-parameter constructor that takes a '{1}' and 'Span<byte>' as parameters. /// - internal static string ValuePropertyMustHaveGetterMessage { + internal static string ValueInCallerAllocatedBufferRequiresSpanConstructorMessage { get { - return ResourceManager.GetString("ValuePropertyMustHaveGetterMessage", resourceCulture); + return ResourceManager.GetString("ValueInCallerAllocatedBufferRequiresSpanConstructorMessage", resourceCulture); } } /// - /// Looks up a localized string similar to The native type's 'Value' property must have a setter to support marshalling from native to managed.. + /// Looks up a localized string similar to A 'Value'-kind native type must provide a one-parameter constructor taking the managed type as a parameter. /// - internal static string ValuePropertyMustHaveSetterDescription { + internal static string ValueInRequiresOneParameterConstructorDescription { get { - return ResourceManager.GetString("ValuePropertyMustHaveSetterDescription", resourceCulture); + return ResourceManager.GetString("ValueInRequiresOneParameterConstructorDescription", resourceCulture); } } /// - /// Looks up a localized string similar to The 'Value' property on the native type '{0}' must have a setter. + /// Looks up a localized string similar to The type '{0}' specifies that it supports 'In' marshalling of '{1}' but does not provide a one-parameter constructor that takes a '{1}' as a parameter. /// - internal static string ValuePropertyMustHaveSetterMessage { + internal static string ValueInRequiresOneParameterConstructorMessage { get { - return ResourceManager.GetString("ValuePropertyMustHaveSetterMessage", resourceCulture); + return ResourceManager.GetString("ValueInRequiresOneParameterConstructorMessage", resourceCulture); } } } diff --git a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Resources.resx b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Resources.resx index e726ef95ec7f6..9f4f6611d149f 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Resources.resx +++ b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Resources.resx @@ -138,12 +138,6 @@ Specified 'GeneratedDllImportAttribute' arguments cannot be forwarded to 'DllImportAttribute' - - A 'LinearCollection'-kind native type must have at least one of the two marshalling methods as well as a 'ManagedValues' property of type 'Span<T>' for some 'T' and a 'NativeValueStorage' property of type 'Span<byte>' to enable marshalling the managed type. - - - The native type '{0}' must be a value type and have a constructor that takes two parameters, one of type '{1}' and an 'int', or have a parameterless instance method named 'ToManaged' that returns '{1}' as well as a 'ManagedValues' property of type 'Span<T>' for some 'T' and a 'NativeValueStorage' property of type 'Span<byte>' - Source-generated P/Invokes will ignore any configuration that is not supported. @@ -233,17 +227,17 @@ 'StringMarshalling' should be set to 'StringMarshalling.Custom' when 'StringMarshallingCustomType' is specified. - - The use cases for 'GetPinnableReference' are not applicable in any scenarios where a 'Value' property is not also required. + + The use cases for 'GetPinnableReference' are not applicable in any scenarios where 'TwoStageMarshalling' is not also required. - - The 'GetPinnableReference' method cannot be provided on the native type '{0}' unless a 'Value' property is also provided + + The 'GetPinnableReference' method cannot be provided on the native type '{0}' unless the 'TwoStageMarshalling' feature is also supported The specified marshaller kind must be valid value of the 'System.Runtime.InteropServices.CustomMarshallerKind' enum. - The specified custom marshller kind for '{0}' is invalid + The specified custom marshaller kind for '{0}' is invalid A type with a 'System.Runtime.InteropServices.CustomTypeMarshallerAttribute' must specify a managed type @@ -251,9 +245,6 @@ The type '{0}' does not specify a managed type in the 'System.Runtime.InteropServices.CustomTypeMarshallerAttribute' applied to the type - - The native type '{0}' must be a closed generic so the emitted code can use a specific instantiation. - The native type '{0}' must be a closed generic or have the same number of generic parameters as the managed type so the emitted code can use a specific instantiation. @@ -278,11 +269,11 @@ The native type for the type '{0}' must be a type with the 'System.Runtime.InteropServices.CustomTypeMarshallerAttribute' that specifies this type as the managed type - - A 'Value'-kind native type must have at least one of the two marshalling methods to enable marshalling the managed type. + + A native must set the 'Direction' property on the 'CustomTypeMarshallerAttribute' to a value that sets at least one known flag value on the 'CustomTypeMarshallerDirection' enum - - The native type '{0}' must be a value type and have a constructor that takes one parameter of type '{1}' or a parameterless instance method named 'ToManaged' that returns '{1}' + + The native type '{0}' must set the 'Direction' property on the 'CustomTypeMarshallerAttribute' to a value that sets at least one known flag value on the 'CustomTypeMarshallerDirection' enum The 'Value' property must not be a 'ref' or 'readonly ref' property. @@ -326,16 +317,85 @@ Specified type is not supported by source-generated P/Invokes - - The native type's 'Value' property must have a getter to support marshalling from managed to native. + + The 'TwoStageMarshalling' feature requires a TNativeType FromNativeValue()' method for the 'In' direction. + + + The marshaller type '{0}' supports marshalling in the 'In' direction with the 'TwoStageMarshalling' feature must provide a 'ToNativeValue' instance method that returns the native value + + + The 'TwoStageMarshalling' feature requires a 'void FromNativeValue(TNativeType value)' method for the 'Out' direction. + + + The marshaller type '{0}' supports marshalling in the 'Out' direction with the 'TwoStageMarshalling' feature, but it does not provide a 'FromNativeValue' instance method that returns 'void' and takes one parameter. + + + The 'CustomTypeMarshallerAttribute' attribute must be semantically valid + + + The 'CustomTypeMarshallerAttribute' on '{0}' is semantically invalid + + + A 'LinearCollection'-kind native type that supports the 'CallerAllocatedBuffer' feature must provide a three-parameter constructor taking the managed type as the first parameter, a 'Span<byte>' as the second parameter, and the native size of the element as the second parameter + + + The type '{0}' specifies that it supports 'In' marshalling with the 'CallerAllocatedBuffer' feature for '{1}' but does not provide a three-parameter constructor that takes a '{1}' , a 'Span<byte>', and an 'int' + + + A 'LinearCollection'-kind native type must provide a two-parameter constructor taking the managed type as the first parameter and the native size of the element as the second parameter + + + The type '{0}' specifies that it supports 'In' marshalling of '{1}' but does not provide a two-parameter constructor that takes a '{1}' as the first parameter and an 'int' as the second parameter + + + The specified marshaller direction must be valid value of the 'System.Runtime.InteropServices.CustomMarshallerDirection' enum. + + + The specified custom marshaller direction for '{0}' is invalid + + + A 'Value'-kind native type that supports the 'CallerAllocatedBuffer' feature must provide a two-parameter constructor taking the managed type and a 'Span<byte>' as parameters + + + The type '{0}' specifies that it supports 'In' marshalling with the 'CallerAllocatedBuffer' feature for '{1}' but does not provide a one-parameter constructor that takes a '{1}' and 'Span<byte>' as parameters + + + A 'Value'-kind native type must provide a one-parameter constructor taking the managed type as a parameter + + + The type '{0}' specifies that it supports 'In' marshalling of '{1}' but does not provide a one-parameter constructor that takes a '{1}' as a parameter + + + A 'LinearCollection'-kind native type that supports marshalling in the 'In' direction must provide a 'GetManagedValuesSource' that returns a 'ReadOnlySpan<>' and a 'GetNativeValuesDestination' method that returns a 'Span<byte>'. + + + The type '{0}' specifies that is supports marshalling in the 'In' direction, but it does not provide a 'GetManagedValuesSource' that returns a 'ReadOnlySpan<>' and a 'GetNativeValuesDestination' method that returns a 'Span<byte>' + + + A 'Value' or 'LinearCollection'-kind native type that supports marshalling in the 'Out' direction must provide a 'ToManaged' method that returns the managed type. + + + The type '{0}' specifies it supports marshalling in the 'Out' direction, but it does not provide a 'ToManaged' method that returns the managed type + + + A 'LinearCollection'-kind native type that supports marshalling in the 'Out' direction must provide a 'GetManagedValuesDestination' that takes an 'int' and returns a 'Span<>' and a 'GetNativeValuesSource' method that takes an 'int' and rreturns a 'ReadOnlySpan<byte>'. + + + The type '{0}' specifies that it supports marshalling in the 'Out' direction, but it does not provide a 'GetManagedValuesDestination' that takes an 'int' and returns a 'Span<>' and a 'GetNativeValuesSource' method that takes an 'int' and rreturns a 'ReadOnlySpan<byte>' + + + A 'LinearCollection'-kind native type that supports marshalling in the 'Out' direction must provide a constructor that takes the size of the native element as an 'int'. + + + The type '{0}' specifies that it supports marshalling in the 'Out' direction, but it does not provide a constructor that takes the size of the native element as an 'int'. - - The 'Value' property on the native type '{0}' must have a getter + + The 'UnmanagedResources' feature requires a 'void FreeNative()' method. - - The native type's 'Value' property must have a setter to support marshalling from native to managed. + + The marshaller type '{0}' supports marshalling with the 'UnmanagedResources' feature, but it does not provide a parameterless 'FreeNative' instance method that returns 'void' - - The 'Value' property on the native type '{0}' must have a setter + + Add missing custom type marshaller members \ No newline at end of file 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 0fb2cfaf394e8..4b609d84dcac5 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 @@ -10,7 +10,7 @@ namespace Microsoft.Interop { - public readonly record struct CustomTypeMarshallerData(CustomTypeMarshallerKind Kind, int? BufferSize); + public readonly record struct CustomTypeMarshallerData(CustomTypeMarshallerKind Kind, CustomTypeMarshallerDirection Direction, CustomTypeMarshallerFeatures Features, int? BufferSize); public static class ShapeMemberNames { @@ -36,6 +36,8 @@ public static class ManualTypeMarshallingHelper public static class CustomMarshallerAttributeFields { public const string BufferSize = nameof(BufferSize); + public const string Direction = nameof(Direction); + public const string Features = nameof(Features); } public static class MarshalUsingProperties @@ -67,12 +69,10 @@ public static (bool hasAttribute, ITypeSymbol? managedType, CustomTypeMarshaller kind = (CustomTypeMarshallerKind)i; } var namedArguments = attr.NamedArguments.ToImmutableDictionary(); - int? bufferSize = null; - if (namedArguments.TryGetValue(CustomMarshallerAttributeFields.BufferSize, out TypedConstant bufferSizeConstant)) - { - bufferSize = bufferSizeConstant.Value as int?; - } - return (true, managedType, new CustomTypeMarshallerData(kind, bufferSize)); + int? bufferSize = namedArguments.TryGetValue(CustomMarshallerAttributeFields.BufferSize, out TypedConstant bufferSizeConstant) ? bufferSizeConstant.Value as int? : null; + CustomTypeMarshallerDirection direction = namedArguments.TryGetValue(CustomMarshallerAttributeFields.Direction, out TypedConstant directionConstant) ? (CustomTypeMarshallerDirection)directionConstant.Value : CustomTypeMarshallerDirection.Ref; + CustomTypeMarshallerFeatures features = namedArguments.TryGetValue(CustomMarshallerAttributeFields.Features, out TypedConstant featuresConstant) ? (CustomTypeMarshallerFeatures)featuresConstant.Value : CustomTypeMarshallerFeatures.None; + return (true, managedType, new CustomTypeMarshallerData(kind, direction, features, bufferSize)); } /// 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 a7ddc91432b5e..f3aac5de6a729 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 @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Runtime.InteropServices; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -179,7 +180,7 @@ private IMarshallingGenerator CreateCustomNativeTypeMarshaller(TypePositionInfo if (marshalInfo.NativeValueType is not null) { - marshallingStrategy = DecorateWithValuePropertyStrategy(marshalInfo, marshallingStrategy); + marshallingStrategy = DecorateWithTwoStageMarshallingStrategy(marshalInfo, marshallingStrategy); } IMarshallingGenerator marshallingGenerator = new CustomNativeTypeMarshallingGenerator(marshallingStrategy, enableByValueContentsMarshalling: false); @@ -243,7 +244,7 @@ private void ValidateCustomNativeTypeMarshallingSupported(TypePositionInfo info, } } - private ICustomNativeTypeMarshallingStrategy DecorateWithValuePropertyStrategy(NativeMarshallingAttributeInfo marshalInfo, ICustomNativeTypeMarshallingStrategy nativeTypeMarshaller) + private ICustomNativeTypeMarshallingStrategy DecorateWithTwoStageMarshallingStrategy(NativeMarshallingAttributeInfo marshalInfo, ICustomNativeTypeMarshallingStrategy nativeTypeMarshaller) { TypeSyntax valuePropertyTypeSyntax = marshalInfo.NativeValueType!.Syntax; @@ -289,7 +290,7 @@ private IMarshallingGenerator CreateNativeCollectionMarshaller( // Explicitly insert the Value property handling here (before numElements handling) so that the numElements handling will be emitted before the Value property handling in unmarshalling. if (collectionInfo.NativeValueType is not null) { - marshallingStrategy = DecorateWithValuePropertyStrategy(collectionInfo, marshallingStrategy); + marshallingStrategy = DecorateWithTwoStageMarshallingStrategy(collectionInfo, marshallingStrategy); } marshallingStrategy = new SizeOfElementMarshalling( 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 40ea3066739ee..23e81e7e8dc8e 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 @@ -97,24 +97,6 @@ public sealed record MarshalAsInfo( /// public sealed record UnmanagedBlittableMarshallingInfo : MarshallingInfo; - [Flags] - public enum CustomTypeMarshallerDirection - { - None = 0, - In = 0x1, - Out = 0x2, - Ref = In | Out, - } - - [Flags] - public enum CustomTypeMarshallerFeatures - { - None = 0, - UnmanagedResources = 0x20, - CallerAllocatedBuffer = 0x4, - TwoStageMarshalling = 0x8 - } - [Flags] public enum CustomTypeMarshallerPinning { @@ -600,8 +582,8 @@ private MarshallingInfo CreateInfoFromMarshalAs( return new NativeLinearCollectionMarshallingInfo( NativeMarshallingType: ManagedTypeInfo.CreateTypeInfoForTypeSymbol(arrayMarshaller), NativeValueType: nativeValueType is not null ? ManagedTypeInfo.CreateTypeInfoForTypeSymbol(nativeValueType) : null, - Direction: CustomTypeMarshallerDirection.Ref, - MarshallingFeatures: CustomTypeMarshallerFeatures.TwoStageMarshalling | CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.CallerAllocatedBuffer, + Direction: customTypeMarshallerData.Value.Direction, + MarshallingFeatures: customTypeMarshallerData.Value.Features, PinningFeatures: CustomTypeMarshallerPinning.NativeType, UseDefaultMarshalling: true, BufferSize: customTypeMarshallerData.Value.BufferSize, @@ -621,11 +603,7 @@ private MarshallingInfo CreateNativeMarshallingInfo( ImmutableHashSet inspectedElements, ref int maxIndirectionDepthUsed) { - INamedTypeSymbol spanOfT = _compilation.GetTypeByMetadataName(TypeNames.System_Span_Metadata)!; - ITypeSymbol spanOfByte = spanOfT.Construct(_compilation.GetSpecialType(SpecialType.System_Byte)); - INamedTypeSymbol readOnlySpanOfT = _compilation.GetTypeByMetadataName(TypeNames.System_ReadOnlySpan_Metadata)!; - ITypeSymbol readOnlySpanOfByte = readOnlySpanOfT.Construct(_compilation.GetSpecialType(SpecialType.System_Byte)); if (nativeType.IsUnboundGenericType) { @@ -659,57 +637,13 @@ private MarshallingInfo CreateNativeMarshallingInfo( return NoMarshallingInfo.Instance; } - CustomTypeMarshallerDirection direction = CustomTypeMarshallerDirection.None; CustomTypeMarshallerPinning pinning = CustomTypeMarshallerPinning.None; - CustomTypeMarshallerFeatures features = CustomTypeMarshallerFeatures.None; if (!isMarshalUsingAttribute && ManualTypeMarshallingHelper.FindGetPinnableReference(type) is not null) { pinning |= CustomTypeMarshallerPinning.ManagedType; } - bool hasInt32Constructor = false; - foreach (IMethodSymbol ctor in nativeType.Constructors) - { - if (ManualTypeMarshallingHelper.IsManagedToNativeConstructor(ctor, type, customTypeMarshallerData.Value.Kind)) - { - direction |= CustomTypeMarshallerDirection.In; - } - else if (ManualTypeMarshallingHelper.IsCallerAllocatedSpanConstructor(ctor, type, spanOfByte, customTypeMarshallerData.Value.Kind)) - { - features |= CustomTypeMarshallerFeatures.CallerAllocatedBuffer; - } - else if (ctor.Parameters.Length == 1 && ctor.Parameters[0].Type.SpecialType == SpecialType.System_Int32) - { - hasInt32Constructor = true; - } - } - - // The constructor that takes only the native element size is required for collection marshallers - // in the native-to-managed scenario. - if ((customTypeMarshallerData.Value.Kind != CustomTypeMarshallerKind.LinearCollection - || hasInt32Constructor) - && ManualTypeMarshallingHelper.HasToManagedMethod(nativeType, type)) - { - direction |= CustomTypeMarshallerDirection.Out; - } - - if (direction == CustomTypeMarshallerDirection.None) - { - _diagnostics.ReportInvalidMarshallingAttributeInfo( - attrData, - customTypeMarshallerData.Value.Kind == CustomTypeMarshallerKind.LinearCollection - ? nameof(Resources.CollectionNativeTypeMustHaveRequiredShapeMessage) - : nameof(Resources.NativeTypeMustHaveRequiredShapeMessage), - nativeType.ToDisplayString()); - return NoMarshallingInfo.Instance; - } - - if (ManualTypeMarshallingHelper.HasFreeNativeMethod(nativeType)) - { - features |= CustomTypeMarshallerFeatures.UnmanagedResources; - } - if (ManualTypeMarshallingHelper.FindGetPinnableReference(nativeType) is not null) { pinning |= CustomTypeMarshallerPinning.NativeType; @@ -719,22 +653,6 @@ private MarshallingInfo CreateNativeMarshallingInfo( if (customTypeMarshallerData.Value.Kind == CustomTypeMarshallerKind.LinearCollection) { - if (direction.HasFlag(CustomTypeMarshallerDirection.In) - && (ManualTypeMarshallingHelper.FindGetManagedValuesSourceMethod(nativeType, readOnlySpanOfT) is null - || ManualTypeMarshallingHelper.FindGetNativeValuesDestinationMethod(nativeType, spanOfByte) is null)) - { - _diagnostics.ReportInvalidMarshallingAttributeInfo(attrData, nameof(Resources.CollectionNativeTypeMustHaveRequiredShapeMessage), nativeType.ToDisplayString()); - return NoMarshallingInfo.Instance; - } - - if (direction.HasFlag(CustomTypeMarshallerDirection.Out) - && (ManualTypeMarshallingHelper.FindGetManagedValuesDestinationMethod(nativeType, spanOfT) is null - || ManualTypeMarshallingHelper.FindGetNativeValuesSourceMethod(nativeType, readOnlySpanOfByte) is null)) - { - _diagnostics.ReportInvalidMarshallingAttributeInfo(attrData, nameof(Resources.CollectionNativeTypeMustHaveRequiredShapeMessage), nativeType.ToDisplayString()); - return NoMarshallingInfo.Instance; - } - if (!ManualTypeMarshallingHelper.TryGetElementTypeFromLinearCollectionMarshaller(nativeType, readOnlySpanOfT, out ITypeSymbol elementType)) { _diagnostics.ReportInvalidMarshallingAttributeInfo(attrData, nameof(Resources.CollectionNativeTypeMustHaveRequiredShapeMessage), nativeType.ToDisplayString()); @@ -744,8 +662,8 @@ private MarshallingInfo CreateNativeMarshallingInfo( return new NativeLinearCollectionMarshallingInfo( ManagedTypeInfo.CreateTypeInfoForTypeSymbol(nativeType), toNativeValueMethod is not null ? ManagedTypeInfo.CreateTypeInfoForTypeSymbol(toNativeValueMethod.ReturnType) : null, - direction, - features, + customTypeMarshallerData.Value.Direction, + customTypeMarshallerData.Value.Features, pinning, UseDefaultMarshalling: !isMarshalUsingAttribute, customTypeMarshallerData.Value.BufferSize, @@ -757,8 +675,8 @@ private MarshallingInfo CreateNativeMarshallingInfo( return new NativeMarshallingAttributeInfo( ManagedTypeInfo.CreateTypeInfoForTypeSymbol(nativeType), toNativeValueMethod is not null ? ManagedTypeInfo.CreateTypeInfoForTypeSymbol(toNativeValueMethod.ReturnType) : null, - direction, - features, + customTypeMarshallerData.Value.Direction, + customTypeMarshallerData.Value.Features, pinning, UseDefaultMarshalling: !isMarshalUsingAttribute, customTypeMarshallerData.Value.BufferSize); 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 aa224a5705e0f..8c9dbccb9f1d8 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 @@ -15,6 +15,10 @@ AutoGen="True" /> + + diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources.Designer.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources.Designer.cs index 812c7f84a5a3e..4a8eab93276ee 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources.Designer.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources.Designer.cs @@ -223,14 +223,14 @@ internal static string InOutAttributeMarshalerNotSupported { } /// - /// Marshalling bool without explicit marshalling information is not supported. Specify either 'MarshalUsingAttribute' or 'MarshalAsAttribute'.. + /// Looks up a localized string similar to Marshalling bool without explicit marshalling information is not supported. Specify either 'MarshalUsingAttribute' or 'MarshalAsAttribute'.. /// internal static string MarshallingBoolAsUndefinedNotSupported { get { return ResourceManager.GetString("MarshallingBoolAsUndefinedNotSupported", resourceCulture); } - } - + } + /// /// Looks up a localized string similar to Marshalling char with 'CharSet.{0}' is not supported. Instead, manually convert the char type to the desired byte representation and pass to the source-generated P/Invoke.. /// @@ -258,24 +258,6 @@ internal static string NativeGenericTypeMustBeClosedOrMatchArityMessage { } } - /// - /// Looks up a localized string similar to The native type must have at least one of the two marshalling methods to enable marshalling the managed type.. - /// - internal static string NativeTypeMustHaveRequiredShapeDescription { - get { - return ResourceManager.GetString("NativeTypeMustHaveRequiredShapeDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The native type '{0}' must be a value type and have a constructor that takes one parameter of type '{1}' or a parameterless instance method named 'ToManaged' that returns '{1}'. - /// - internal static string NativeTypeMustHaveRequiredShapeMessage { - get { - return ResourceManager.GetString("NativeTypeMustHaveRequiredShapeMessage", resourceCulture); - } - } - /// /// Looks up a localized string similar to The '[Out]' attribute is only supported on array parameters.. /// @@ -294,24 +276,6 @@ internal static string OutByValueNotSupportedMessage { } } - /// - /// Looks up a localized string similar to The 'Value' property must not be a 'ref' or 'readonly ref' property.. - /// - internal static string RefValuePropertyUnsupportedDescription { - get { - return ResourceManager.GetString("RefValuePropertyUnsupportedDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The 'Value' property on the native type '{0}' must not be a 'ref' or 'readonly ref' property.. - /// - internal static string RefValuePropertyUnsupportedMessage { - get { - return ResourceManager.GetString("RefValuePropertyUnsupportedMessage", resourceCulture); - } - } - /// /// Looks up a localized string similar to Runtime marshalling must be disabled in this project by applying the 'System.Runtime.InteropServices.DisableRuntimeMarshallingAttribute' to the assembly to enable marshalling this type.. /// @@ -338,50 +302,5 @@ internal static string TypeNotSupportedTitle { return ResourceManager.GetString("TypeNotSupportedTitle", resourceCulture); } } - - /// - /// Looks up a localized string similar to Marshalling a value between managed and native with a native type with a 'Value' property requires extra state, which is not supported in this context.. - /// - internal static string ValuePropertyMarshallingRequiresAdditionalState { - get { - return ResourceManager.GetString("ValuePropertyMarshallingRequiresAdditionalState", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The native type's 'Value' property must have a getter to support marshalling from managed to native.. - /// - internal static string ValuePropertyMustHaveGetterDescription { - get { - return ResourceManager.GetString("ValuePropertyMustHaveGetterDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The 'Value' property on the native type '{0}' must have a getter. - /// - internal static string ValuePropertyMustHaveGetterMessage { - get { - return ResourceManager.GetString("ValuePropertyMustHaveGetterMessage", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The native type's 'Value' property must have a setter to support marshalling from native to managed.. - /// - internal static string ValuePropertyMustHaveSetterDescription { - get { - return ResourceManager.GetString("ValuePropertyMustHaveSetterDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The 'Value' property on the native type '{0}' must have a setter. - /// - internal static string ValuePropertyMustHaveSetterMessage { - get { - return ResourceManager.GetString("ValuePropertyMustHaveSetterMessage", resourceCulture); - } - } } } diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources.resx b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources.resx index fd2dbec3e4ad0..f3225058ea088 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources.resx +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources.resx @@ -165,33 +165,12 @@ The '[Out]' attribute is not supported on the '{0}' parameter. - - The 'Value' property must not be a 'ref' or 'readonly ref' property. - - - The 'Value' property on the native type '{0}' must not be a 'ref' or 'readonly ref' property. - An abstract type derived from 'SafeHandle' cannot be marshalled by reference. The provided type must be concrete. Specified type is not supported by source-generated P/Invokes - - Marshalling a value between managed and native with a native type with a 'Value' property requires extra state, which is not supported in this context. - - - The native type's 'Value' property must have a getter to support marshalling from managed to native. - - - The 'Value' property on the native type '{0}' must have a getter - - - The native type's 'Value' property must have a setter to support marshalling from native to managed. - - - The 'Value' property on the native type '{0}' must have a setter - This element cannot depend on '{0}' for collection size information without creating a dependency cycle @@ -207,12 +186,6 @@ The native type '{0}' for managed type '{1}' must be a closed generic type or have the same arity as the managed type. - - The native type must have at least one of the two marshalling methods to enable marshalling the managed type. - - - The native type '{0}' must be a value type and have a constructor that takes one parameter of type '{1}' or a parameterless instance method named 'ToManaged' that returns '{1}' - A native type with the 'GenericContiguousCollectionMarshallerAttribute' must have at least one of the two marshalling methods as well as a 'ManagedValues' property of type 'Span<T>' for some 'T' and a 'NativeValueStorage' property of type 'Span<byte>' to enable marshalling the managed type. diff --git a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/Ancillary.Interop.csproj b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/Ancillary.Interop.csproj index 4b94200cbcdb7..35e77f259b241 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/Ancillary.Interop.csproj +++ b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/Ancillary.Interop.csproj @@ -14,6 +14,8 @@ + + diff --git a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/SpanMarshallers.cs b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/SpanMarshallers.cs index de6a586b406b6..358fc92cf6be1 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/SpanMarshallers.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/SpanMarshallers.cs @@ -9,7 +9,7 @@ namespace System.Runtime.InteropServices.GeneratedMarshalling // Stack-alloc threshold set to 256 bytes to enable small arrays to be passed on the stack. // Number kept small to ensure that P/Invokes with a lot of array parameters doesn't // blow the stack since this is a new optimization in the code-generated interop. - [CustomTypeMarshaller(typeof(ReadOnlySpan<>), CustomTypeMarshallerKind.LinearCollection, BufferSize = 0x200)] + [CustomTypeMarshaller(typeof(ReadOnlySpan<>), CustomTypeMarshallerKind.LinearCollection, Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.CallerAllocatedBuffer | CustomTypeMarshallerFeatures.TwoStageMarshalling, BufferSize = 0x200)] public unsafe ref struct ReadOnlySpanMarshaller { private ReadOnlySpan _managedSpan; @@ -66,7 +66,7 @@ public void FreeNative() } } - [CustomTypeMarshaller(typeof(Span<>), CustomTypeMarshallerKind.LinearCollection, BufferSize = 0x200)] + [CustomTypeMarshaller(typeof(Span<>), CustomTypeMarshallerKind.LinearCollection, Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.CallerAllocatedBuffer | CustomTypeMarshallerFeatures.TwoStageMarshalling, BufferSize = 0x200)] public unsafe ref struct SpanMarshaller { private Span _managedSpan; @@ -128,7 +128,7 @@ public void FreeNative() } } - [CustomTypeMarshaller(typeof(Span<>), CustomTypeMarshallerKind.LinearCollection, BufferSize = 0x200)] + [CustomTypeMarshaller(typeof(Span<>), CustomTypeMarshallerKind.LinearCollection, Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.CallerAllocatedBuffer | CustomTypeMarshallerFeatures.TwoStageMarshalling, BufferSize = 0x200)] public unsafe ref struct NeverNullSpanMarshaller { private SpanMarshaller _inner; @@ -166,7 +166,7 @@ public void FreeNative() } } - [CustomTypeMarshaller(typeof(ReadOnlySpan<>), CustomTypeMarshallerKind.LinearCollection, BufferSize = 0x200)] + [CustomTypeMarshaller(typeof(ReadOnlySpan<>), CustomTypeMarshallerKind.LinearCollection, Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.CallerAllocatedBuffer | CustomTypeMarshallerFeatures.TwoStageMarshalling, BufferSize = 0x200)] public unsafe ref struct NeverNullReadOnlySpanMarshaller { private ReadOnlySpanMarshaller _inner; @@ -199,7 +199,7 @@ public void FreeNative() } // Stack-alloc threshold set to 0 so that the generator can use the constructor that takes a stackSpace to let the marshaller know that the original data span can be used and safely pinned. - [CustomTypeMarshaller(typeof(Span<>), CustomTypeMarshallerKind.LinearCollection, BufferSize = 0)] + [CustomTypeMarshaller(typeof(Span<>), CustomTypeMarshallerKind.LinearCollection, Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.CallerAllocatedBuffer | CustomTypeMarshallerFeatures.TwoStageMarshalling, BufferSize = 0)] public unsafe ref struct DirectSpanMarshaller where T : unmanaged { 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 eb8db3a8b845d..1a80fb73527f4 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CodeSnippets.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CodeSnippets.cs @@ -677,7 +677,7 @@ struct S public bool b; } -[CustomTypeMarshaller(typeof(S), BufferSize = 1)] +[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.CallerAllocatedBuffer, BufferSize = 1)] struct Native { private int i; @@ -696,7 +696,7 @@ struct S public bool b; } -[CustomTypeMarshaller(typeof(S), BufferSize = 1)] +[CustomTypeMarshaller(typeof(S), Direction = CustomTypeMarshallerDirection.Out, Features = CustomTypeMarshallerFeatures.CallerAllocatedBuffer, BufferSize = 1)] struct Native { private int i; @@ -715,7 +715,7 @@ struct S public bool b; } -[CustomTypeMarshaller(typeof(S), BufferSize = 1)] +[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.CallerAllocatedBuffer, BufferSize = 1)] struct Native { private int i; @@ -739,7 +739,7 @@ struct S public bool b; } -[CustomTypeMarshaller(typeof(S), BufferSize = 1)] +[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.CallerAllocatedBuffer | CustomTypeMarshallerFeatures.TwoStageMarshalling, BufferSize = 1)] struct Native { public Native(S s, System.Span b) @@ -759,7 +759,7 @@ struct S public bool b; } -[CustomTypeMarshaller(typeof(S))] +[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] struct Native { public Native(S s) @@ -781,7 +781,7 @@ class S public ref int GetPinnableReference() => ref i; } -[CustomTypeMarshaller(typeof(S))] +[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] unsafe struct Native { private int* ptr; @@ -812,7 +812,7 @@ class S public byte c; } -[CustomTypeMarshaller(typeof(S), BufferSize = 1)] +[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.CallerAllocatedBuffer | CustomTypeMarshallerFeatures.TwoStageMarshalling, BufferSize = 1)] unsafe ref struct Native { private byte* ptr; @@ -867,7 +867,7 @@ class S public byte c = 0; } -[CustomTypeMarshaller(typeof(S))] +[CustomTypeMarshaller(typeof(S), Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] unsafe struct Native { private S value; @@ -918,7 +918,7 @@ struct S public bool b; } -[CustomTypeMarshaller(typeof(S))] +[CustomTypeMarshaller(typeof(S), Direction = CustomTypeMarshallerDirection.In)] struct Native { private int i; @@ -937,7 +937,7 @@ struct S public bool b; } -[CustomTypeMarshaller(typeof(S))] +[CustomTypeMarshaller(typeof(S), Direction = CustomTypeMarshallerDirection.In)] struct Native { private int i; @@ -956,6 +956,7 @@ struct S } [StructLayout(LayoutKind.Sequential)] +[CustomTypeMarshaller(typeof(S), Direction = CustomTypeMarshallerDirection.Out)] struct Native { private int i; @@ -970,7 +971,7 @@ public struct IntStructWrapper public int Value; } -[CustomTypeMarshaller(typeof(IntStructWrapper))] +[CustomTypeMarshaller(typeof(IntStructWrapper), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] public struct IntStructWrapperNative { public IntStructWrapperNative(IntStructWrapper managed) @@ -1117,7 +1118,7 @@ public static string CollectionByValue(string elementType) => BasicParameterByVa [NativeMarshalling(typeof(Marshaller<>))] class TestCollection {} -[CustomTypeMarshaller(typeof(TestCollection<>), CustomTypeMarshallerKind.LinearCollection)] +[CustomTypeMarshaller(typeof(TestCollection<>), CustomTypeMarshallerKind.LinearCollection, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] ref struct Marshaller { public Marshaller(int nativeElementSize) : this() {} @@ -1156,7 +1157,7 @@ public static string CustomCollectionWithMarshaller(bool enableDefaultMarshallin string nativeMarshallingAttribute = enableDefaultMarshalling ? "[NativeMarshalling(typeof(Marshaller<>))]" : string.Empty; return nativeMarshallingAttribute + @"class TestCollection {} -[CustomTypeMarshaller(typeof(TestCollection<>), CustomTypeMarshallerKind.LinearCollection)] +[CustomTypeMarshaller(typeof(TestCollection<>), CustomTypeMarshallerKind.LinearCollection, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] ref struct Marshaller { public Marshaller(int nativeElementSize) : this() {} @@ -1255,7 +1256,7 @@ public static partial void Method( [NativeMarshalling(typeof(Marshaller<,>))] class TestCollection {} -[CustomTypeMarshaller(typeof(TestCollection<>), CustomTypeMarshallerKind.LinearCollection)] +[CustomTypeMarshaller(typeof(TestCollection<>), CustomTypeMarshallerKind.LinearCollection, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] ref struct Marshaller { public Marshaller(TestCollection managed, int nativeElementSize) : this() {} diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/ManualTypeMarshallingAnalyzerTests.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CustomTypeMarshallerFixerTests.cs similarity index 62% rename from src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/ManualTypeMarshallingAnalyzerTests.cs rename to src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CustomTypeMarshallerFixerTests.cs index d03f3981b7dbe..a050b5c80f25c 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/ManualTypeMarshallingAnalyzerTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CustomTypeMarshallerFixerTests.cs @@ -6,14 +6,16 @@ using System.Collections.Generic; using System.Threading.Tasks; using Xunit; -using static Microsoft.Interop.Analyzers.ManualTypeMarshallingAnalyzer; +using static Microsoft.Interop.Analyzers.CustomTypeMarshallerAnalyzer; -using VerifyCS = LibraryImportGenerator.UnitTests.Verifiers.CSharpAnalyzerVerifier; +using VerifyCS = LibraryImportGenerator.UnitTests.Verifiers.CSharpCodeFixVerifier< + Microsoft.Interop.Analyzers.CustomTypeMarshallerAnalyzer, + Microsoft.Interop.Analyzers.CustomTypeMarshallerFixer>; namespace LibraryImportGenerator.UnitTests { [ActiveIssue("https://github.com/dotnet/runtime/issues/60650", TestRuntimes.Mono)] - public class ManualTypeMarshallingAnalyzerTests + public class CustomTypeMarshallerFixerTests { [ConditionalFact] public async Task NullNativeType_ReportsDiagnostic() @@ -27,8 +29,9 @@ struct S public string s; }"; - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(NativeTypeMustHaveCustomTypeMarshallerAttributeRule).WithLocation(0).WithArguments("S")); + await VerifyCS.VerifyCodeFixAsync(source, + VerifyCS.Diagnostic(NativeTypeMustHaveCustomTypeMarshallerAttributeRule).WithLocation(0).WithArguments("S"), + source); } [ConditionalFact] @@ -43,8 +46,9 @@ struct S public string s; }"; - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(NativeTypeMustHaveCustomTypeMarshallerAttributeRule).WithLocation(0).WithArguments("S")); + await VerifyCS.VerifyCodeFixAsync(source, + VerifyCS.Diagnostic(NativeTypeMustHaveCustomTypeMarshallerAttributeRule).WithLocation(0).WithArguments("S"), + source); } [ConditionalFact] @@ -71,8 +75,9 @@ public Native(S s) public S ToManaged() => new S { s = value }; }"; - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(NativeTypeMustBeBlittableRule).WithLocation(0).WithArguments("Native", "S")); + await VerifyCS.VerifyCodeFixAsync(source, + VerifyCS.Diagnostic(NativeTypeMustBeBlittableRule).WithLocation(0).WithArguments("Native", "S"), + source); } [ConditionalFact] @@ -99,9 +104,9 @@ public Native(S s) public S ToManaged() => new S(); }"; - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(NativeTypeMustHaveRequiredShapeRule).WithLocation(0).WithArguments("Native", "S"), - VerifyCS.Diagnostic(NativeTypeMustBeBlittableRule).WithLocation(0).WithArguments("Native", "S")); + await VerifyCS.VerifyCodeFixAsync(source, + VerifyCS.Diagnostic(NativeTypeMustBeBlittableRule).WithLocation(0).WithArguments("Native", "S"), + source); } [ConditionalFact] @@ -130,11 +135,11 @@ public Native(S s) public S ToManaged() => new S(); }"; - await VerifyCS.VerifyAnalyzerAsync(source); + await VerifyCS.VerifyCodeFixAsync(source, source); } [ConditionalFact] - public async Task BlittableNativeWithNonBlittableValueProperty_ReportsDiagnostic() + public async Task BlittableNativeWithNonBlittableToNativeValue_ReportsDiagnostic() { string source = @" using System; @@ -162,12 +167,13 @@ public Native(S s) public void FromNativeValue(string value) => throw null; }"; - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(NativeTypeMustBeBlittableRule).WithLocation(0).WithArguments("string", "S")); + await VerifyCS.VerifyCodeFixAsync(source, + VerifyCS.Diagnostic(NativeTypeMustBeBlittableRule).WithLocation(0).WithArguments("string", "S"), + source); } [ConditionalFact] - public async Task NonBlittableNativeTypeWithBlittableValueProperty_DoesNotReportDiagnostic() + public async Task NonBlittableNativeTypeWithBlittableToNativeValue_DoesNotReportDiagnostic() { string source = @" using System; @@ -195,40 +201,7 @@ public Native(S s) public void FromNativeValue(IntPtr value) => throw null; }"; - await VerifyCS.VerifyAnalyzerAsync(source); - } - - [ConditionalFact] - public async Task ClassNativeTypeWithValueProperty_ReportsDiagnostic() - { - string source = @" -using System; -using System.Runtime.InteropServices; - -[NativeMarshalling(typeof(Native))] -struct S -{ - public string s; -} - -[{|CS0592:CustomTypeMarshaller|}(typeof(S))] -class {|#0:Native|} -{ - private string value; - - public Native(S s) - { - value = s.s; - } - - public S ToManaged() => new S() { s = value }; - - public IntPtr ToNativeValue() => throw null; - public void FromNativeValue(IntPtr value) => throw null; -}"; - - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(NativeTypeMustHaveRequiredShapeRule).WithLocation(0).WithArguments("Native", "S")); + await VerifyCS.VerifyCodeFixAsync(source, source); } [ConditionalFact] @@ -262,8 +235,9 @@ public Native(S s) public void FromNativeValue(IntPtr value) => throw null; }"; - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(GetPinnableReferenceReturnTypeBlittableRule).WithLocation(0)); + await VerifyCS.VerifyCodeFixAsync(source, + VerifyCS.Diagnostic(GetPinnableReferenceReturnTypeBlittableRule).WithLocation(0), + source); } [ConditionalFact] @@ -297,7 +271,7 @@ public Native(S s) : this() public void FromNativeValue(IntPtr value) => throw null; }"; - await VerifyCS.VerifyAnalyzerAsync(source); + await VerifyCS.VerifyCodeFixAsync(source, source); } [ConditionalFact] @@ -331,7 +305,7 @@ public Native(S s) public void FromNativeValue(IntPtr value) => throw null; }"; - await VerifyCS.VerifyAnalyzerAsync(source); + await VerifyCS.VerifyCodeFixAsync(source, source); } [ConditionalFact] @@ -365,7 +339,7 @@ public Native(S s) : this() public void FromNativeValue(IntPtr value) => throw null; }"; - await VerifyCS.VerifyAnalyzerAsync(source); + await VerifyCS.VerifyCodeFixAsync(source, source); } [ConditionalFact] @@ -399,8 +373,9 @@ public Native(S s) : this() public void FromNativeValue(int value) => throw null; }"; - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(NativeTypeMustBePointerSizedRule).WithLocation(0).WithArguments("int", "S")); + await VerifyCS.VerifyCodeFixAsync(source, + VerifyCS.Diagnostic(NativeTypeMustBePointerSizedRule).WithLocation(0).WithArguments("int", "S"), + source); } [ConditionalFact] @@ -434,7 +409,7 @@ public Native(S s) : this() public void FromNativeValue(int* value) => throw null; }"; - await VerifyCS.VerifyAnalyzerAsync(source); + await VerifyCS.VerifyCodeFixAsync(source, source); } [ConditionalFact] @@ -452,7 +427,7 @@ class S public ref byte GetPinnableReference() => ref c; } -[CustomTypeMarshaller(typeof(S))] +[CustomTypeMarshaller(typeof(S), Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] unsafe struct Native { private S value; @@ -464,8 +439,9 @@ public Native(S s) : this() public ref byte {|#0:ToNativeValue|}() => throw null; }"; - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(RefValuePropertyUnsupportedRule).WithLocation(0).WithArguments("Native")); + await VerifyCS.VerifyCodeFixAsync(source, + VerifyCS.Diagnostic(RefValuePropertyUnsupportedRule).WithLocation(0).WithArguments("Native"), + source); } [ConditionalFact] @@ -481,7 +457,7 @@ class S public byte c; } -[CustomTypeMarshaller(typeof(S))] +[CustomTypeMarshaller(typeof(S), Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] unsafe struct Native { private S value; @@ -496,8 +472,9 @@ public Native(S s) : this() public ref byte {|#0:ToNativeValue|}() => throw null; }"; - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(RefValuePropertyUnsupportedRule).WithLocation(0).WithArguments("Native")); + await VerifyCS.VerifyCodeFixAsync(source, + VerifyCS.Diagnostic(RefValuePropertyUnsupportedRule).WithLocation(0).WithArguments("Native"), + source); } [ConditionalFact] @@ -513,7 +490,7 @@ class S public byte c; } -[CustomTypeMarshaller(typeof(S))] +[CustomTypeMarshaller(typeof(S), Direction = CustomTypeMarshallerDirection.In)] unsafe struct Native { private byte value; @@ -526,8 +503,9 @@ public Native(S s) : this() public ref byte {|#0:GetPinnableReference|}() => ref System.Runtime.CompilerServices.Unsafe.NullRef(); }"; - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(MarshallerGetPinnableReferenceRequiresValuePropertyRule).WithLocation(0).WithArguments("Native")); + await VerifyCS.VerifyCodeFixAsync(source, + VerifyCS.Diagnostic(MarshallerGetPinnableReferenceRequiresTwoStageMarshallingRule).WithLocation(0).WithArguments("Native"), + source); } [ConditionalFact] @@ -543,7 +521,7 @@ class S public byte c; } -[CustomTypeMarshaller(typeof(S))] +[CustomTypeMarshaller(typeof(S), Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] unsafe struct Native { private byte value; @@ -556,10 +534,9 @@ public Native(S s) : this() public ref byte GetPinnableReference() => ref System.Runtime.CompilerServices.Unsafe.NullRef(); public int ToNativeValue() => throw null; - public void FromNativeValue(int value) => throw null; }"; - await VerifyCS.VerifyAnalyzerAsync(source); + await VerifyCS.VerifyCodeFixAsync(source, source); } [ConditionalFact] @@ -575,13 +552,14 @@ class S public byte c; } -[CustomTypeMarshaller(typeof(S))] +[CustomTypeMarshaller(typeof(S), Direction = CustomTypeMarshallerDirection.None)] struct {|#0:Native|} { }"; - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(NativeTypeMustHaveRequiredShapeRule).WithLocation(0).WithArguments("Native", "S")); + await VerifyCS.VerifyCodeFixAsync(source, + VerifyCS.Diagnostic(CustomMarshallerTypeMustSupportDirectionRule).WithLocation(0).WithArguments("Native", "S"), + source); } [ConditionalFact] @@ -597,13 +575,14 @@ class S public byte c; } -[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection)] +[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection, Direction = CustomTypeMarshallerDirection.None)] struct {|#0:Native|} { }"; - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(CollectionNativeTypeMustHaveRequiredShapeRule).WithLocation(0).WithArguments("Native", "S")); + await VerifyCS.VerifyCodeFixAsync(source, + VerifyCS.Diagnostic(CustomMarshallerTypeMustSupportDirectionRule).WithLocation(0).WithArguments("Native", "S"), + source); } [ConditionalFact] @@ -619,19 +598,43 @@ class S public byte c; } -[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection)] +[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection, Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] ref struct {|#0:Native|} { public Native(S s) : this() {} + public System.ReadOnlySpan GetManagedValuesSource() => throw null; + public System.Span GetNativeValuesDestination() => throw null; + public System.IntPtr ToNativeValue() => throw null; +}"; + string fixedSource = @" +using System; +using System.Runtime.InteropServices; + +[NativeMarshalling(typeof(Native))] +class S +{ + public byte c; +} + +[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection, Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] +ref struct Native +{ + public Native(S s) : this() {} public System.ReadOnlySpan GetManagedValuesSource() => throw null; public System.Span GetNativeValuesDestination() => throw null; public System.IntPtr ToNativeValue() => throw null; + + public Native(S managed, int nativeElementSize) + { + throw new NotImplementedException(); + } }"; - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(CollectionNativeTypeMustHaveRequiredShapeRule).WithLocation(0).WithArguments("Native", "S")); + await VerifyCS.VerifyCodeFixAsync(source, + VerifyCS.Diagnostic(LinearCollectionInRequiresTwoParameterConstructorRule).WithLocation(0).WithArguments("Native", "S"), + fixedSource); } [ConditionalFact] @@ -647,7 +650,7 @@ class S public byte c; } -[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection)] +[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection, Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] ref struct Native { public Native(S s, int nativeElementSize) : this() {} @@ -657,7 +660,7 @@ public Native(S s, int nativeElementSize) : this() {} public System.IntPtr ToNativeValue() => throw null; }"; - await VerifyCS.VerifyAnalyzerAsync(source); + await VerifyCS.VerifyCodeFixAsync(source, source); } [ConditionalFact] @@ -673,7 +676,7 @@ class S public byte c; } -[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection, BufferSize = 1)] +[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection, Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.CallerAllocatedBuffer | CustomTypeMarshallerFeatures.TwoStageMarshalling, BufferSize = 1)] ref struct {|#0:Native|} { public Native(S s, Span stackSpace) : this() {} @@ -682,9 +685,43 @@ public Native(S s, Span stackSpace) : this() {} public System.Span GetNativeValuesDestination() => throw null; public System.IntPtr ToNativeValue() => throw null; }"; + string fixedSource = @" +using System; +using System.Runtime.InteropServices; + +[NativeMarshalling(typeof(Native))] +class S +{ + public byte c; +} + +[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection, Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.CallerAllocatedBuffer | CustomTypeMarshallerFeatures.TwoStageMarshalling, BufferSize = 1)] +ref struct Native +{ + public Native(S s, Span stackSpace) : this() {} + + public System.ReadOnlySpan GetManagedValuesSource() => throw null; + public System.Span GetNativeValuesDestination() => throw null; + public System.IntPtr ToNativeValue() => throw null; - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(CollectionNativeTypeMustHaveRequiredShapeRule).WithLocation(0).WithArguments("Native", "S")); + public Native(S managed, int nativeElementSize) + { + throw new NotImplementedException(); + } + + public Native(S managed, Span buffer, int nativeElementSize) + { + throw new NotImplementedException(); + } +}"; + + await VerifyCS.VerifyCodeFixAsync(source, + new[] + { + VerifyCS.Diagnostic(LinearCollectionInRequiresTwoParameterConstructorRule).WithLocation(0).WithArguments("Native", "S"), + VerifyCS.Diagnostic(LinearCollectionInCallerAllocatedBufferRequiresSpanConstructorRule).WithLocation(0).WithArguments("Native", "S") + }, + fixedSource); } [ConditionalFact] @@ -700,7 +737,7 @@ class S public byte c; } -[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection, BufferSize = 1)] +[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection, Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.CallerAllocatedBuffer | CustomTypeMarshallerFeatures.TwoStageMarshalling, BufferSize = 1)] ref struct {|#0:Native|} { public Native(S s, Span stackSpace, int nativeElementSize) : this() {} @@ -709,13 +746,42 @@ public Native(S s, Span stackSpace, int nativeElementSize) : this() {} public System.Span GetNativeValuesDestination() => throw null; public System.IntPtr ToNativeValue() => throw null; }"; + string fixedSource = @" +using System; +using System.Runtime.InteropServices; + +[NativeMarshalling(typeof(Native))] +class S +{ + public byte c; +} + +[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection, Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.CallerAllocatedBuffer | CustomTypeMarshallerFeatures.TwoStageMarshalling, BufferSize = 1)] +ref struct Native +{ + public Native(S s, Span stackSpace, int nativeElementSize) : this() {} - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(CallerAllocMarshallingShouldSupportAllocatingMarshallingFallbackRule).WithLocation(0).WithArguments("Native", "S")); + public System.ReadOnlySpan GetManagedValuesSource() => throw null; + public System.Span GetNativeValuesDestination() => throw null; + public System.IntPtr ToNativeValue() => throw null; + + public Native(S managed, int nativeElementSize) + { + throw new NotImplementedException(); + } +}"; + + await VerifyCS.VerifyCodeFixAsync(source, + new[] + { + VerifyCS.Diagnostic(CallerAllocMarshallingShouldSupportAllocatingMarshallingFallbackRule).WithLocation(0).WithArguments("Native", "S"), + VerifyCS.Diagnostic(LinearCollectionInRequiresTwoParameterConstructorRule).WithLocation(0).WithArguments("Native", "S"), + }, + fixedSource); } [ConditionalFact] - public async Task CollectionNativeTypeWithMissingManagedValuesSourceProperty_ReportsDiagnostic() + public async Task CollectionNativeTypeWithMissingManagedValuesSource_ReportsDiagnostic() { string source = @" using System; @@ -727,7 +793,7 @@ class S public byte c; } -[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection)] +[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection, Direction = CustomTypeMarshallerDirection.In)] ref struct {|#0:Native|} { public Native(S s, int nativeElementSize) : this() {} @@ -735,13 +801,37 @@ public Native(S s, int nativeElementSize) : this() {} public System.Span GetNativeValuesDestination() => throw null; public System.IntPtr ToNativeValue() => throw null; }"; + string fixedSource = @" +using System; +using System.Runtime.InteropServices; - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(CollectionNativeTypeMustHaveRequiredShapeRule).WithLocation(0).WithArguments("Native", "S")); +[NativeMarshalling(typeof(Native))] +class S +{ + public byte c; +} + +[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection, Direction = CustomTypeMarshallerDirection.In)] +ref struct Native +{ + public Native(S s, int nativeElementSize) : this() {} + + public System.Span GetNativeValuesDestination() => throw null; + public System.IntPtr ToNativeValue() => throw null; + + public ReadOnlySpan GetManagedValuesSource() + { + throw new NotImplementedException(); + } +}"; + + await VerifyCS.VerifyCodeFixAsync(source, + VerifyCS.Diagnostic(LinearCollectionInRequiresCollectionMethodsRule).WithLocation(0).WithArguments("Native", "S"), + fixedSource); } [ConditionalFact] - public async Task CollectionNativeTypeWithMissingNativeValuesDestinationProperty_ReportsDiagnostic() + public async Task CollectionNativeTypeWithMissingNativeValuesDestination_ReportsDiagnostic() { string source = @" using System; @@ -753,7 +843,7 @@ class S public byte c; } -[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection)] +[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection, Direction = CustomTypeMarshallerDirection.In)] ref struct {|#0:Native|} { public Native(S s, int nativeElementSize) : this() {} @@ -761,9 +851,33 @@ public Native(S s, int nativeElementSize) : this() {} public System.ReadOnlySpan GetManagedValuesSource() => throw null; public System.IntPtr ToNativeValue() => throw null; }"; + string fixedSource = @" +using System; +using System.Runtime.InteropServices; - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(CollectionNativeTypeMustHaveRequiredShapeRule).WithLocation(0).WithArguments("Native", "S")); +[NativeMarshalling(typeof(Native))] +class S +{ + public byte c; +} + +[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection, Direction = CustomTypeMarshallerDirection.In)] +ref struct Native +{ + public Native(S s, int nativeElementSize) : this() {} + + public System.ReadOnlySpan GetManagedValuesSource() => throw null; + public System.IntPtr ToNativeValue() => throw null; + + public Span GetNativeValuesDestination() + { + throw new NotImplementedException(); + } +}"; + + await VerifyCS.VerifyCodeFixAsync(source, + VerifyCS.Diagnostic(LinearCollectionInRequiresCollectionMethodsRule).WithLocation(0).WithArguments("Native", "S"), + fixedSource); } [ConditionalFact] @@ -779,13 +893,13 @@ class S public byte c; } -[CustomTypeMarshaller(typeof(S))] +[CustomTypeMarshaller(typeof(S), Direction = CustomTypeMarshallerDirection.In)] struct Native { public Native(S s) {} }"; - await VerifyCS.VerifyAnalyzerAsync(source); + await VerifyCS.VerifyCodeFixAsync(source, source); } [ConditionalFact] @@ -801,13 +915,13 @@ class S public byte c; } -[CustomTypeMarshaller(typeof(S))] +[CustomTypeMarshaller(typeof(S), Direction = CustomTypeMarshallerDirection.Out)] struct Native { public S ToManaged() => new S(); }"; - await VerifyCS.VerifyAnalyzerAsync(source); + await VerifyCS.VerifyCodeFixAsync(source, source); } [ConditionalFact] @@ -823,18 +937,43 @@ class S public byte c; } -[CustomTypeMarshaller(typeof(S), BufferSize = 0x100)] +[CustomTypeMarshaller(typeof(S), Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.CallerAllocatedBuffer, BufferSize = 0x100)] struct {|#0:Native|} { public Native(S s, Span buffer) {} }"; + string fixedSource = @" +using System; +using System.Runtime.InteropServices; - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(CallerAllocMarshallingShouldSupportAllocatingMarshallingFallbackRule).WithLocation(0).WithArguments("Native")); +[NativeMarshalling(typeof(Native))] +class S +{ + public byte c; +} + +[CustomTypeMarshaller(typeof(S), Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.CallerAllocatedBuffer, BufferSize = 0x100)] +struct Native +{ + public Native(S s, Span buffer) {} + + public Native(S managed) + { + throw new NotImplementedException(); + } +}"; + + await VerifyCS.VerifyCodeFixAsync(source, + new[] + { + VerifyCS.Diagnostic(CallerAllocMarshallingShouldSupportAllocatingMarshallingFallbackRule).WithLocation(0).WithArguments("Native", "S"), + VerifyCS.Diagnostic(ValueInRequiresOneParameterConstructorRule).WithLocation(0).WithArguments("Native", "S"), + }, + fixedSource); } [ConditionalFact] - public async Task TypeWithOnlyNativeStackallocConstructorAndGetPinnableReference_ReportsDiagnostics() + public async Task TypeWithOnlyGetPinnableReference_AndInSupport_ReportsDiagnostics() { string source = @" using System; @@ -847,17 +986,15 @@ class {|#0:S|} public ref byte GetPinnableReference() => ref c; } -[CustomTypeMarshaller(typeof(S), BufferSize = 0x100)] +[CustomTypeMarshaller(typeof(S), Direction = CustomTypeMarshallerDirection.Out)] struct {|#1:Native|} { - public Native(S s, Span buffer) {} - - public IntPtr Value => IntPtr.Zero; + public S ToManaged() => default; }"; - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(CallerAllocMarshallingShouldSupportAllocatingMarshallingFallbackRule).WithLocation(1).WithArguments("Native"), - VerifyCS.Diagnostic(GetPinnableReferenceShouldSupportAllocatingMarshallingFallbackRule).WithLocation(0).WithArguments("S", "Native")); + await VerifyCS.VerifyCodeFixAsync(source, + VerifyCS.Diagnostic(GetPinnableReferenceShouldSupportAllocatingMarshallingFallbackRule).WithLocation(0).WithArguments("S", "Native"), + source); } [ConditionalFact] @@ -873,7 +1010,7 @@ class S public byte c; } -[CustomTypeMarshaller(typeof(S))] +[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] struct {|#0:Native|} { public Native(S s) {} @@ -883,8 +1020,34 @@ public Native(S s) {} public S ToManaged() => new S(); }"; - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(ValuePropertyMustHaveGetterRule).WithLocation(0).WithArguments("Native")); + string fixedSource = @" +using System; +using System.Runtime.InteropServices; + +[NativeMarshalling(typeof(Native))] +class S +{ + public byte c; +} + +[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] +struct Native +{ + public Native(S s) {} + + public void FromNativeValue(IntPtr value) => throw null; + + public S ToManaged() => new S(); + + public IntPtr ToNativeValue() + { + throw new NotImplementedException(); + } +}"; + + await VerifyCS.VerifyCodeFixAsync(source, + VerifyCS.Diagnostic(InTwoStageMarshallingRequiresToNativeValueRule).WithLocation(0).WithArguments("Native"), + fixedSource); } [ConditionalFact] @@ -900,7 +1063,7 @@ class S public byte c; } -[CustomTypeMarshaller(typeof(S))] +[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] struct {|#0:Native|} { public Native(S managed) {} @@ -909,9 +1072,34 @@ public Native(S managed) {} public IntPtr ToNativeValue() => IntPtr.Zero; }"; + string fixedSource = @" +using System; +using System.Runtime.InteropServices; + +[NativeMarshalling(typeof(Native))] +class S +{ + public byte c; +} + +[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] +struct Native +{ + public Native(S managed) {} - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(ValuePropertyMustHaveSetterRule).WithLocation(0).WithArguments("Native")); + public S ToManaged() => new S(); + + public IntPtr ToNativeValue() => IntPtr.Zero; + + public void FromNativeValue(IntPtr value) + { + throw new NotImplementedException(); + } +}"; + + await VerifyCS.VerifyCodeFixAsync(source, + VerifyCS.Diagnostic(OutTwoStageMarshallingRequiresFromNativeValueRule).WithLocation(0).WithArguments("Native"), + fixedSource); } [ConditionalFact] @@ -946,7 +1134,7 @@ static void Foo([MarshalUsing(typeof(Native))] S s) {} } "; - await VerifyCS.VerifyAnalyzerAsync(source); + await VerifyCS.VerifyCodeFixAsync(source, source); } [ConditionalFact] @@ -980,8 +1168,9 @@ static void Foo([MarshalUsing(typeof(Native))] S s) {} } "; - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(NativeTypeMustBeBlittableRule).WithLocation(0).WithArguments("Native", "S")); + await VerifyCS.VerifyCodeFixAsync(source, + VerifyCS.Diagnostic(NativeTypeMustBeBlittableRule).WithLocation(0).WithArguments("Native", "S"), + source); } [ConditionalFact] @@ -1015,8 +1204,9 @@ static class Test static S Foo() => new S(); } "; - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(NativeTypeMustBeBlittableRule).WithLocation(0).WithArguments("Native", "S")); + await VerifyCS.VerifyCodeFixAsync(source, + VerifyCS.Diagnostic(NativeTypeMustBeBlittableRule).WithLocation(0).WithArguments("Native", "S"), + source); } [ConditionalFact] @@ -1032,7 +1222,7 @@ struct S public string s; } -[CustomTypeMarshaller(typeof(S))] +[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] struct Native where T : unmanaged { @@ -1045,7 +1235,7 @@ public Native(S s) public T ToNativeValue() => throw null; public void FromNativeValue(T value) => throw null; }"; - await VerifyCS.VerifyAnalyzerAsync(source); + await VerifyCS.VerifyCodeFixAsync(source, source); } [ConditionalFact] @@ -1061,7 +1251,7 @@ struct S public string s; } -[CustomTypeMarshaller(typeof(S))] +[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] struct Native where T : unmanaged { @@ -1074,7 +1264,9 @@ public Native(S s) public T ToNativeValue() => throw null; public void FromNativeValue(T value) => throw null; }"; - await VerifyCS.VerifyAnalyzerAsync(source, VerifyCS.Diagnostic(NativeGenericTypeMustBeClosedOrMatchArityRule).WithLocation(0).WithArguments("Native<>", "S")); + await VerifyCS.VerifyCodeFixAsync(source, + VerifyCS.Diagnostic(NativeGenericTypeMustBeClosedOrMatchArityRule).WithLocation(0).WithArguments("Native<>", "S"), + source); } [ConditionalFact] @@ -1089,7 +1281,7 @@ struct S public string s; } -[CustomTypeMarshaller(typeof(S))] +[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] struct Native where T : unmanaged { @@ -1108,7 +1300,9 @@ static class Test static void Foo([{|#0:MarshalUsing(typeof(Native<>))|}] S s) {} }"; - await VerifyCS.VerifyAnalyzerAsync(source, VerifyCS.Diagnostic(NativeGenericTypeMustBeClosedOrMatchArityRule).WithLocation(0).WithArguments("Native<>", "S")); + await VerifyCS.VerifyCodeFixAsync(source, + VerifyCS.Diagnostic(NativeGenericTypeMustBeClosedOrMatchArityRule).WithLocation(0).WithArguments("Native<>", "S"), + source); } [ConditionalFact] @@ -1123,7 +1317,7 @@ struct S public string s; } -[CustomTypeMarshaller(typeof(S<>))] +[CustomTypeMarshaller(typeof(S<>), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] struct {|#1:Native|} where T : new() { @@ -1136,9 +1330,13 @@ public Native(S s) public T ToNativeValue() => throw null; public void FromNativeValue(T value) => throw null; }"; - await VerifyCS.VerifyAnalyzerAsync(source, + await VerifyCS.VerifyCodeFixAsync(source, + new[] + { VerifyCS.Diagnostic(NativeTypeMustHaveCustomTypeMarshallerAttributeRule).WithLocation(0).WithArguments("S"), - VerifyCS.Diagnostic(NativeGenericTypeMustBeClosedOrMatchArityRule).WithLocation(1).WithArguments("Native", "S<>")); + VerifyCS.Diagnostic(NativeGenericTypeMustBeClosedOrMatchArityRule).WithLocation(1).WithArguments("Native", "S<>") + }, + source); } [ConditionalFact] @@ -1153,7 +1351,7 @@ struct S public T t; } -[CustomTypeMarshaller(typeof(S<>))] +[CustomTypeMarshaller(typeof(S<>), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] struct Native where T : new() { @@ -1166,7 +1364,7 @@ public Native(S s) public T ToNativeValue() => throw null; public void FromNativeValue(T value) => throw null; }"; - await VerifyCS.VerifyAnalyzerAsync(source); + await VerifyCS.VerifyCodeFixAsync(source, source); } [ConditionalFact] @@ -1182,15 +1380,16 @@ class S public byte c; } -[CustomTypeMarshaller(typeof(S))] +[CustomTypeMarshaller(typeof(S), Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.CallerAllocatedBuffer)] struct Native { public Native(S s) {} public {|#0:Native|}(S s, Span buffer) {} }"; - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(CallerAllocConstructorMustHaveBufferSizeRule).WithLocation(0).WithArguments("Native")); + await VerifyCS.VerifyCodeFixAsync(source, + VerifyCS.Diagnostic(CallerAllocConstructorMustHaveBufferSizeRule).WithLocation(0).WithArguments("Native"), + source); } [ConditionalFact] @@ -1200,13 +1399,13 @@ public async Task CustomTypeMarshallerForArrayTypeWithPlaceholder_DoesNotReportD using System; using System.Runtime.InteropServices; -[CustomTypeMarshaller(typeof(CustomTypeMarshallerAttribute.GenericPlaceholder[]))] +[CustomTypeMarshaller(typeof(CustomTypeMarshallerAttribute.GenericPlaceholder[]), Direction = CustomTypeMarshallerDirection.In)] struct Native { public Native(T[] a) {} }"; - await VerifyCS.VerifyAnalyzerAsync(source); + await VerifyCS.VerifyCodeFixAsync(source, source); } [ConditionalFact] @@ -1216,13 +1415,13 @@ public async Task CustomTypeMarshallerForPointerTypeWithPlaceholder_DoesNotRepor using System; using System.Runtime.InteropServices; -[CustomTypeMarshaller(typeof(CustomTypeMarshallerAttribute.GenericPlaceholder*))] +[CustomTypeMarshaller(typeof(CustomTypeMarshallerAttribute.GenericPlaceholder*), Direction = CustomTypeMarshallerDirection.In)] unsafe struct Native where T : unmanaged { public Native(T* a) {} }"; - await VerifyCS.VerifyAnalyzerAsync(source); + await VerifyCS.VerifyCodeFixAsync(source, source); } [ConditionalFact] @@ -1232,13 +1431,13 @@ public async Task CustomTypeMarshallerForArrayOfPointerTypeWithPlaceholder_DoesN using System; using System.Runtime.InteropServices; -[CustomTypeMarshaller(typeof(CustomTypeMarshallerAttribute.GenericPlaceholder*[]))] +[CustomTypeMarshaller(typeof(CustomTypeMarshallerAttribute.GenericPlaceholder*[]), Direction = CustomTypeMarshallerDirection.In)] unsafe struct Native where T : unmanaged { public Native(T*[] a) {} }"; - await VerifyCS.VerifyAnalyzerAsync(source); + await VerifyCS.VerifyCodeFixAsync(source, source); } } } 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 c2d256f51667f..155edea1bc9c2 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/NonBlittable.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/NonBlittable.cs @@ -17,7 +17,7 @@ public struct StringContainer public string str2; } - [CustomTypeMarshaller(typeof(StringContainer))] + [CustomTypeMarshaller(typeof(StringContainer), Features = CustomTypeMarshallerFeatures.UnmanagedResources)] public struct StringContainerNative { public IntPtr str1; @@ -45,7 +45,7 @@ public void FreeNative() } } - [CustomTypeMarshaller(typeof(double))] + [CustomTypeMarshaller(typeof(double), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] public struct DoubleToLongMarshaler { public long l; @@ -102,7 +102,7 @@ public class IntWrapper public ref int GetPinnableReference() => ref i; } - [CustomTypeMarshaller(typeof(IntWrapper))] + [CustomTypeMarshaller(typeof(IntWrapper), Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.TwoStageMarshalling)] public unsafe struct IntWrapperMarshaler { public IntWrapperMarshaler(IntWrapper managed) @@ -124,7 +124,7 @@ public void FreeNative() } } - [CustomTypeMarshaller(typeof(string), BufferSize = 0x100)] + [CustomTypeMarshaller(typeof(string), Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.TwoStageMarshalling | CustomTypeMarshallerFeatures.CallerAllocatedBuffer, BufferSize = 0x100)] public unsafe ref struct Utf16StringMarshaller { private ushort* allocated; @@ -193,7 +193,7 @@ public void FreeNative() } } - [CustomTypeMarshaller(typeof(string), BufferSize = 0x100)] + [CustomTypeMarshaller(typeof(string), Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.TwoStageMarshalling | CustomTypeMarshallerFeatures.CallerAllocatedBuffer, BufferSize = 0x100)] public unsafe ref struct Utf8StringMarshaller { private byte* allocated; @@ -249,7 +249,7 @@ public IntStructWrapperNative(IntStructWrapper managed) public IntStructWrapper ToManaged() => new IntStructWrapper { Value = value }; } - [CustomTypeMarshaller(typeof(List<>), CustomTypeMarshallerKind.LinearCollection, BufferSize = 0x200)] + [CustomTypeMarshaller(typeof(List<>), CustomTypeMarshallerKind.LinearCollection, Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.TwoStageMarshalling | CustomTypeMarshallerFeatures.CallerAllocatedBuffer, BufferSize = 0x200)] public unsafe ref struct ListMarshaller { private List managedList; From 2108321f29813ce0a312c95e63ca684abd1313fb Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 14 Mar 2022 17:12:20 -0700 Subject: [PATCH 18/24] Add diagnostics and tests for mismatched types and for declared methods without their corresponding feature flags. This does not add support for detecting missing CustomTypeMarshallerDirection values based on members since directions are opt-out instead of opt-in. --- .../Analyzers/AnalyzerDiagnostics.cs | 10 +- .../Analyzers/CustomTypeMarshallerAnalyzer.cs | 135 ++++++- .../Analyzers/CustomTypeMarshallerFixer.cs | 166 +++++++- .../Analyzers/SyntaxGeneratorExtensions.cs | 67 ++++ .../Resources.Designer.cs | 117 ++++++ .../gen/LibraryImportGenerator/Resources.resx | 39 ++ .../ManualTypeMarshallingHelper.cs | 2 +- .../CustomTypeMarshallerFixerTests.cs | 374 ++++++++++++++++-- .../Verifiers/CSharpCodeFixVerifier.cs | 28 ++ 9 files changed, 873 insertions(+), 65 deletions(-) create mode 100644 src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/SyntaxGeneratorExtensions.cs diff --git a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/AnalyzerDiagnostics.cs b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/AnalyzerDiagnostics.cs index b4ff5b12d7c4d..82de7c31daf97 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/AnalyzerDiagnostics.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/AnalyzerDiagnostics.cs @@ -23,16 +23,18 @@ public static class Ids public const string NativeTypeMustBePointerSized = Prefix + "006"; public const string CustomMarshallerTypeMustHaveRequiredShape = Prefix + "007"; public const string CustomMarshallerTypeMustSupportDirection = Prefix + "008"; - public const string Unused = Prefix + "009"; + public const string ProvidedMethodsNotSpecifiedInShape = Prefix + "009"; public const string GetPinnableReferenceShouldSupportAllocatingMarshallingFallback = Prefix + "010"; public const string CallerAllocMarshallingShouldSupportAllocatingMarshallingFallback = Prefix + "011"; public const string CallerAllocConstructorMustHaveStackBufferSize = Prefix + "012"; - public const string RefValuePropertyUnsupported = Prefix + "014"; + public const string TwoStageMarshallingNativeTypesMustMatch = Prefix + "013"; + public const string LinearCollectionElementTypesMustMatch = Prefix + "014"; + public const string RefValuePropertyUnsupported = Prefix + "015"; public const string NativeGenericTypeMustBeClosedOrMatchArity = Prefix + "016"; - public const string MarshallerGetPinnableReferenceRequiresTwoStageMarshalling = Prefix + "018"; + public const string MarshallerGetPinnableReferenceRequiresTwoStageMarshalling = Prefix + "017"; // Migration from DllImport to LibraryImport - public const string ConvertToLibraryImport = Prefix + "015"; + public const string ConvertToLibraryImport = Prefix + "018"; } internal static LocalizableResourceString GetResourceString(string resourceName) 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 0cc5c0d39a019..7274d0574210c 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/CustomTypeMarshallerAnalyzer.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/CustomTypeMarshallerAnalyzer.cs @@ -18,9 +18,11 @@ public class CustomTypeMarshallerAnalyzer : DiagnosticAnalyzer { private const string Category = "Usage"; + public const string MissingFeaturesKey = nameof(MissingFeaturesKey); + public static class MissingMemberNames { - public const string MissingMemberNameKey = nameof(MissingMemberNames); + public const string Key = nameof(MissingMemberNames); public const char Delimiter = ' '; public const string ValueManagedToNativeConstructor = nameof(ValueManagedToNativeConstructor); @@ -290,6 +292,66 @@ public static class MissingMemberNames isEnabledByDefault: true, description: GetResourceString(nameof(Resources.MarshallerGetPinnableReferenceRequiresTwoStageMarshallingDescription))); + public static readonly DiagnosticDescriptor FreeNativeMethodProvidedShouldSpecifyUnmanagedResourcesFeatureRule = + new DiagnosticDescriptor( + Ids.ProvidedMethodsNotSpecifiedInShape, + "ProvidedMethodsNotSpecifiedInShape", + GetResourceString(nameof(Resources.FreeNativeMethodProvidedShouldSpecifyUnmanagedResourcesFeatureMessage)), + Category, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: GetResourceString(nameof(Resources.FreeNativeMethodProvidedShouldSpecifyUnmanagedResourcesFeatureDescription))); + + public static readonly DiagnosticDescriptor ToNativeValueMethodProvidedShouldSpecifyTwoStageMarshallingFeatureRule = + new DiagnosticDescriptor( + Ids.ProvidedMethodsNotSpecifiedInShape, + "ProvidedMethodsNotSpecifiedInShape", + GetResourceString(nameof(Resources.ToNativeValueMethodProvidedShouldSpecifyTwoStageMarshallingFeatureMessage)), + Category, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: GetResourceString(nameof(Resources.ToNativeValueMethodProvidedShouldSpecifyTwoStageMarshallingFeatureDescription))); + + public static readonly DiagnosticDescriptor FromNativeValueMethodProvidedShouldSpecifyTwoStageMarshallingFeatureRule = + new DiagnosticDescriptor( + Ids.ProvidedMethodsNotSpecifiedInShape, + "ProvidedMethodsNotSpecifiedInShape", + GetResourceString(nameof(Resources.FromNativeValueMethodProvidedShouldSpecifyTwoStageMarshallingFeatureMessage)), + Category, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: GetResourceString(nameof(Resources.FromNativeValueMethodProvidedShouldSpecifyTwoStageMarshallingFeatureDescription))); + + public static readonly DiagnosticDescriptor CallerAllocatedBufferConstructorProvidedShouldSpecifyFeatureRule = + new DiagnosticDescriptor( + Ids.ProvidedMethodsNotSpecifiedInShape, + "ProvidedMethodsNotSpecifiedInShape", + GetResourceString(nameof(Resources.CallerAllocatedBufferConstructorProvidedShouldSpecifyFeatureMessage)), + Category, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: GetResourceString(nameof(Resources.CallerAllocatedBufferConstructorProvidedShouldSpecifyFeatureDescription))); + + public static readonly DiagnosticDescriptor TwoStageMarshallingNativeTypesMustMatchRule = + new DiagnosticDescriptor( + Ids.TwoStageMarshallingNativeTypesMustMatch, + "TwoStageMarshallingNativeTypesMustMatch", + GetResourceString(nameof(Resources.TwoStageMarshallingNativeTypesMustMatchMessage)), + Category, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: GetResourceString(nameof(Resources.TwoStageMarshallingNativeTypesMustMatchDescription))); + + public static readonly DiagnosticDescriptor LinearCollectionElementTypesMustMatchRule = + new DiagnosticDescriptor( + Ids.LinearCollectionElementTypesMustMatch, + "LinearCollectionElementTypesMustMatch", + GetResourceString(nameof(Resources.LinearCollectionElementTypesMustMatchMessage)), + Category, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: GetResourceString(nameof(Resources.LinearCollectionElementTypesMustMatchDescription))); + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create( MarshallerTypeMustSpecifyManagedTypeRule, @@ -314,7 +376,13 @@ public static class MissingMemberNames CallerAllocConstructorMustHaveBufferSizeRule, RefValuePropertyUnsupportedRule, NativeGenericTypeMustBeClosedOrMatchArityRule, - MarshallerGetPinnableReferenceRequiresTwoStageMarshallingRule); + MarshallerGetPinnableReferenceRequiresTwoStageMarshallingRule, + FreeNativeMethodProvidedShouldSpecifyUnmanagedResourcesFeatureRule, + ToNativeValueMethodProvidedShouldSpecifyTwoStageMarshallingFeatureRule, + FromNativeValueMethodProvidedShouldSpecifyTwoStageMarshallingFeatureRule, + CallerAllocatedBufferConstructorProvidedShouldSpecifyFeatureRule, + TwoStageMarshallingNativeTypesMustMatchRule, + LinearCollectionElementTypesMustMatchRule); public override void Initialize(AnalysisContext context) { @@ -538,7 +606,7 @@ public void AnalyzeMarshallerType(SymbolAnalysisContext context) marshallerType.CreateDiagnostic( GetInConstructorShapeRule(marshallerData.Kind), ImmutableDictionary.Empty.Add( - MissingMemberNames.MissingMemberNameKey, + MissingMemberNames.Key, GetInConstructorMissingMemberName(marshallerData.Kind)), marshallerType.ToDisplayString(), type.ToDisplayString())); @@ -551,7 +619,7 @@ public void AnalyzeMarshallerType(SymbolAnalysisContext context) marshallerType.CreateDiagnostic( GetCallerAllocatedBufferConstructorShapeRule(marshallerData.Kind), ImmutableDictionary.Empty.Add( - MissingMemberNames.MissingMemberNameKey, + MissingMemberNames.Key, GetCallerAllocatedBufferConstructorMissingMemberName(marshallerData.Kind)), marshallerType.ToDisplayString(), type.ToDisplayString())); @@ -564,6 +632,16 @@ public void AnalyzeMarshallerType(SymbolAnalysisContext context) marshallerType.ToDisplayString())); } } + else if (callerAllocatedSpanConstructor is not null) + { + context.ReportDiagnostic( + marshallerType.CreateDiagnostic( + CallerAllocatedBufferConstructorProvidedShouldSpecifyFeatureRule, + ImmutableDictionary.Empty.Add( + MissingFeaturesKey, + nameof(CustomTypeMarshallerFeatures.CallerAllocatedBuffer)), + marshallerType.ToDisplayString())); + } // Validate that this type can support marshalling when stackalloc is not usable. if (callerAllocatedSpanConstructor is not null && inConstructor is null) @@ -581,7 +659,7 @@ public void AnalyzeMarshallerType(SymbolAnalysisContext context) marshallerType.CreateDiagnostic( OutRequiresToManagedRule, ImmutableDictionary.Empty.Add( - MissingMemberNames.MissingMemberNameKey, + MissingMemberNames.Key, ShapeMemberNames.Value.ToManaged), marshallerType.ToDisplayString())); } @@ -605,7 +683,7 @@ public void AnalyzeMarshallerType(SymbolAnalysisContext context) marshallerType.CreateDiagnostic( LinearCollectionInRequiresCollectionMethodsRule, ImmutableDictionary.Empty.Add( - MissingMemberNames.MissingMemberNameKey, + MissingMemberNames.Key, missingMembers), marshallerType.ToDisplayString())); } @@ -623,7 +701,7 @@ public void AnalyzeMarshallerType(SymbolAnalysisContext context) marshallerType.CreateDiagnostic( LinearCollectionOutRequiresCollectionMethodsRule, ImmutableDictionary.Empty.Add( - MissingMemberNames.MissingMemberNameKey, + MissingMemberNames.Key, missingMembers), marshallerType.ToDisplayString())); } @@ -634,7 +712,7 @@ public void AnalyzeMarshallerType(SymbolAnalysisContext context) ((INamedTypeSymbol)getManagedValuesSourceMethod.ReturnType).TypeArguments[0], ((INamedTypeSymbol)getManagedValuesDestinationMethod.ReturnType).TypeArguments[0])) { - // TODO: Diagnostic for mismatched element collection type + context.ReportDiagnostic(getManagedValuesSourceMethod.CreateDiagnostic(LinearCollectionElementTypesMustMatchRule)); } if (marshallerData.Direction.HasFlag(CustomTypeMarshallerDirection.Out) && collectionOutConstructor is null) { @@ -642,7 +720,7 @@ public void AnalyzeMarshallerType(SymbolAnalysisContext context) marshallerType.CreateDiagnostic( LinearCollectionOutRequiresIntConstructorRule, ImmutableDictionary.Empty.Add( - MissingMemberNames.MissingMemberNameKey, + MissingMemberNames.Key, MissingMemberNames.CollectionNativeElementSizeConstructor), marshallerType.ToDisplayString())); } @@ -664,18 +742,27 @@ public void AnalyzeMarshallerType(SymbolAnalysisContext context) marshallerType.CreateDiagnostic( UnmanagedResourcesRequiresFreeNativeRule, ImmutableDictionary.Empty.Add( - MissingMemberNames.MissingMemberNameKey, + MissingMemberNames.Key, ShapeMemberNames.Value.FreeNative), marshallerType.ToDisplayString(), type.ToDisplayString())); } + else if (!marshallerData.Features.HasFlag(CustomTypeMarshallerFeatures.UnmanagedResources) && ManualTypeMarshallingHelper.HasFreeNativeMethod(marshallerType)) + { + context.ReportDiagnostic( + marshallerType.CreateDiagnostic( + FreeNativeMethodProvidedShouldSpecifyUnmanagedResourcesFeatureRule, + ImmutableDictionary.Empty.Add( + MissingFeaturesKey, + nameof(CustomTypeMarshallerFeatures.UnmanagedResources)), + marshallerType.ToDisplayString())); + } IMethodSymbol? toNativeValueMethod = ManualTypeMarshallingHelper.FindToNativeValueMethod(marshallerType); IMethodSymbol? fromNativeValueMethod = ManualTypeMarshallingHelper.FindFromNativeValueMethod(marshallerType); bool toNativeValueMethodIsRefReturn = toNativeValueMethod is { ReturnsByRef: true } or { ReturnsByRefReadonly: true }; ITypeSymbol nativeType = marshallerType; - // If either ToNativeValue or FromNativeValue is provided, validate the scenarios where they are required. if (marshallerData.Features.HasFlag(CustomTypeMarshallerFeatures.TwoStageMarshalling)) { if (marshallerData.Direction.HasFlag(CustomTypeMarshallerDirection.In) && toNativeValueMethod is null) @@ -683,7 +770,7 @@ public void AnalyzeMarshallerType(SymbolAnalysisContext context) context.ReportDiagnostic(marshallerType.CreateDiagnostic( InTwoStageMarshallingRequiresToNativeValueRule, ImmutableDictionary.Empty.Add( - MissingMemberNames.MissingMemberNameKey, + MissingMemberNames.Key, ShapeMemberNames.Value.ToNativeValue), marshallerType.ToDisplayString())); } @@ -692,7 +779,7 @@ public void AnalyzeMarshallerType(SymbolAnalysisContext context) context.ReportDiagnostic(marshallerType.CreateDiagnostic( OutTwoStageMarshallingRequiresFromNativeValueRule, ImmutableDictionary.Empty.Add( - MissingMemberNames.MissingMemberNameKey, + MissingMemberNames.Key, ShapeMemberNames.Value.FromNativeValue), marshallerType.ToDisplayString())); } @@ -702,9 +789,29 @@ public void AnalyzeMarshallerType(SymbolAnalysisContext context) && fromNativeValueMethod is not null && !SymbolEqualityComparer.Default.Equals(toNativeValueMethod.ReturnType, fromNativeValueMethod.Parameters[0].Type)) { - // TODO: Add diagnostic. + context.ReportDiagnostic(toNativeValueMethod.CreateDiagnostic(TwoStageMarshallingNativeTypesMustMatchRule)); } } + else if (fromNativeValueMethod is not null) + { + context.ReportDiagnostic( + marshallerType.CreateDiagnostic( + FromNativeValueMethodProvidedShouldSpecifyTwoStageMarshallingFeatureRule, + ImmutableDictionary.Empty.Add( + MissingFeaturesKey, + nameof(CustomTypeMarshallerFeatures.TwoStageMarshalling)), + marshallerType.ToDisplayString())); + } + else if (toNativeValueMethod is not null) + { + context.ReportDiagnostic( + marshallerType.CreateDiagnostic( + ToNativeValueMethodProvidedShouldSpecifyTwoStageMarshallingFeatureRule, + ImmutableDictionary.Empty.Add( + MissingFeaturesKey, + nameof(CustomTypeMarshallerFeatures.TwoStageMarshalling)), + marshallerType.ToDisplayString())); + } if (toNativeValueMethod is not null) { diff --git a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/CustomTypeMarshallerFixer.cs b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/CustomTypeMarshallerFixer.cs index 158b845126ce1..7b0bb9ec42016 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/CustomTypeMarshallerFixer.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/CustomTypeMarshallerFixer.cs @@ -6,6 +6,7 @@ using System.Collections.Immutable; using System.Composition; using System.Linq; +using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -13,12 +14,16 @@ using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.Text; namespace Microsoft.Interop.Analyzers { [ExportCodeFixProvider(LanguageNames.CSharp, LanguageNames.VisualBasic), Shared] public class CustomTypeMarshallerFixer : CodeFixProvider { + private const string AddMissingCustomTypeMarshallerMembersKey = nameof(AddMissingCustomTypeMarshallerMembersKey); + private const string AddMissingCustomTypeMarshallerFeaturesKey = nameof(AddMissingCustomTypeMarshallerFeaturesKey); + private class CustomFixAllProvider : DocumentBasedFixAllProvider { protected override async Task FixAllAsync(FixAllContext fixAllContext, Document document, ImmutableArray diagnostics) @@ -27,17 +32,65 @@ protected override async Task FixAllAsync(FixAllContext fixAllContext, if (root == null) return document; - var editor = await DocumentEditor.CreateAsync(document, fixAllContext.CancellationToken).ConfigureAwait(false); + DocumentEditor editor = await DocumentEditor.CreateAsync(document, fixAllContext.CancellationToken).ConfigureAwait(false); - foreach (var diagnosticsBySpan in diagnostics.GroupBy(d => d.Location.SourceSpan)) + switch (fixAllContext.CodeActionEquivalenceKey) { - SyntaxNode node = root.FindNode(diagnosticsBySpan.Key); - var (missingMemberNames, _) = GetRequiredShapeMissingMemberNames(diagnosticsBySpan); - ITypeSymbol marshallerType = (ITypeSymbol)editor.SemanticModel.GetDeclaredSymbol(node); - editor.ReplaceNode(node, (node, gen) => AddMissingMembers(node, marshallerType, missingMemberNames, editor.SemanticModel.Compilation, gen, fixAllContext.CancellationToken)); + case AddMissingCustomTypeMarshallerMembersKey: + foreach (IGrouping diagnosticsBySpan in diagnostics.GroupBy(d => d.Location.SourceSpan)) + { + SyntaxNode node = root.FindNode(diagnosticsBySpan.Key); + + AddMissingMembers(fixAllContext, editor, diagnosticsBySpan, node); + } + break; + case AddMissingCustomTypeMarshallerFeaturesKey: + foreach (IGrouping diagnosticsBySpan in diagnostics.GroupBy(d => d.Location.SourceSpan)) + { + SyntaxNode node = root.FindNode(diagnosticsBySpan.Key); + + await AddMissingFeatures(fixAllContext, editor, diagnosticsBySpan, node).ConfigureAwait(false); + } + break; + default: + break; } + return editor.GetChangedDocument(); } + + private static void AddMissingMembers(FixAllContext fixAllContext, DocumentEditor editor, IEnumerable diagnostics, SyntaxNode node) + { + var (missingMemberNames, _) = GetRequiredShapeMissingMemberNames(diagnostics); + ITypeSymbol marshallerType = (ITypeSymbol)editor.SemanticModel.GetDeclaredSymbol(node); + editor.ReplaceNode( + node, + (node, gen) => + CustomTypeMarshallerFixer.AddMissingMembers( + node, + marshallerType, + missingMemberNames, + editor.SemanticModel.Compilation, + gen)); + } + + private static async Task AddMissingFeatures(FixAllContext fixAllContext, DocumentEditor editor, IEnumerable diagnostics, SyntaxNode node) + { + var (featuresToAdd, _) = GetFeaturesToAdd(diagnostics); + ITypeSymbol marshallerType = (ITypeSymbol)editor.SemanticModel.GetDeclaredSymbol(node); + AttributeData customTypeMarshallerAttribute = marshallerType.GetAttributes().FirstOrDefault(attr => attr.AttributeClass.ToDisplayString() == TypeNames.CustomTypeMarshallerAttribute); + + SyntaxNode attributeSyntax = await customTypeMarshallerAttribute.ApplicationSyntaxReference!.GetSyntaxAsync(fixAllContext.CancellationToken).ConfigureAwait(false); + + editor.ReplaceNode( + attributeSyntax, + (node, gen) => + CustomTypeMarshallerFixer.AddMissingFeatures( + gen.GetName(node), + customTypeMarshallerAttribute, + featuresToAdd, + gen)); + } } public override FixAllProvider? GetFixAllProvider() => new CustomFixAllProvider(); @@ -45,7 +98,8 @@ protected override async Task FixAllAsync(FixAllContext fixAllContext, public override ImmutableArray FixableDiagnosticIds { get; } = ImmutableArray.Create( AnalyzerDiagnostics.Ids.CustomMarshallerTypeMustHaveRequiredShape, - AnalyzerDiagnostics.Ids.CallerAllocMarshallingShouldSupportAllocatingMarshallingFallback); + AnalyzerDiagnostics.Ids.CallerAllocMarshallingShouldSupportAllocatingMarshallingFallback, + AnalyzerDiagnostics.Ids.ProvidedMethodsNotSpecifiedInShape); public override async Task RegisterCodeFixesAsync(CodeFixContext context) { @@ -55,16 +109,28 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) return; SyntaxNode node = root.FindNode(context.Span); - var (missingMemberNames, diagnostics) = GetRequiredShapeMissingMemberNames(context.Diagnostics); + var (missingMemberNames, missingMembersDiagnostics) = GetRequiredShapeMissingMemberNames(context.Diagnostics); - if (diagnostics.Count > 0) + if (missingMembersDiagnostics.Count > 0) { context.RegisterCodeFix( CodeAction.Create( Resources.AddMissingCustomTypeMarshallerMembers, ct => AddMissingMembers(doc, node, missingMemberNames, ct), - nameof(Resources.AddMissingCustomTypeMarshallerMembers)), - diagnostics); + AddMissingCustomTypeMarshallerMembersKey), + missingMembersDiagnostics); + } + + var (featuresToAdd, featuresToAddDiagnostics) = GetFeaturesToAdd(context.Diagnostics); + + if (featuresToAddDiagnostics.Count > 0 && featuresToAdd != CustomTypeMarshallerFeatures.None) + { + context.RegisterCodeFix( + CodeAction.Create( + Resources.AddMissingFeaturesToCustomTypeMarshaller, + ct => AddMissingFeatures(doc, node, featuresToAdd, ct), + AddMissingCustomTypeMarshallerFeaturesKey), + featuresToAddDiagnostics); } } @@ -77,7 +143,7 @@ private static (List missingMembers, List fixedDiagnostics) if (diagnostic.Id == AnalyzerDiagnostics.Ids.CustomMarshallerTypeMustHaveRequiredShape) { requiredShapeDiagnostics.Add(diagnostic); - if (diagnostic.Properties.TryGetValue(CustomTypeMarshallerAnalyzer.MissingMemberNames.MissingMemberNameKey, out string missingMembers)) + if (diagnostic.Properties.TryGetValue(CustomTypeMarshallerAnalyzer.MissingMemberNames.Key, out string missingMembers)) { missingMemberNames.AddRange(missingMembers.Split(CustomTypeMarshallerAnalyzer.MissingMemberNames.Delimiter)); } @@ -86,13 +152,85 @@ private static (List missingMembers, List fixedDiagnostics) return (missingMemberNames, requiredShapeDiagnostics); } + private static (CustomTypeMarshallerFeatures featuresToAdd, List fixedDiagnostics) GetFeaturesToAdd(IEnumerable diagnostics) + { + List featuresToAddDiagnostics = new(); + CustomTypeMarshallerFeatures featuresToAdd = CustomTypeMarshallerFeatures.None; + foreach (var diagnostic in diagnostics) + { + if (diagnostic.Id == AnalyzerDiagnostics.Ids.ProvidedMethodsNotSpecifiedInShape) + { + featuresToAddDiagnostics.Add(diagnostic); + if (diagnostic.Properties.TryGetValue(CustomTypeMarshallerAnalyzer.MissingFeaturesKey, out string missingFeatures) + && Enum.TryParse(missingFeatures, out CustomTypeMarshallerFeatures featuresValue)) + { + featuresToAdd |= featuresValue; + } + } + } + + return (featuresToAdd, featuresToAddDiagnostics); + } + private static async Task AddMissingFeatures(Document doc, SyntaxNode node, CustomTypeMarshallerFeatures featuresToAdd, CancellationToken ct) + { + var editor = await DocumentEditor.CreateAsync(doc, ct).ConfigureAwait(false); + var gen = editor.Generator; + + ISymbol marshallerType = editor.SemanticModel.GetDeclaredSymbol(node, ct); + + AttributeData customTypeMarshallerAttribute = marshallerType.GetAttributes().FirstOrDefault(attr => attr.AttributeClass.ToDisplayString() == TypeNames.CustomTypeMarshallerAttribute); + + SyntaxNode attributeSyntax = await customTypeMarshallerAttribute.ApplicationSyntaxReference!.GetSyntaxAsync(ct).ConfigureAwait(false); + + SyntaxNode updatedDeclaration = AddMissingFeatures(gen.GetName(attributeSyntax), customTypeMarshallerAttribute, featuresToAdd, gen); + + editor.ReplaceNode(attributeSyntax, updatedDeclaration); + + return editor.GetChangedDocument(); + } + + private static SyntaxNode AddMissingFeatures(string attributeName, AttributeData? customTypeMarshallerAttribute, CustomTypeMarshallerFeatures featuresToAdd, SyntaxGenerator gen) + { + SyntaxNode newAttributeSyntax = gen.Attribute(attributeName); + + newAttributeSyntax = gen.AddAttributeArguments(newAttributeSyntax, customTypeMarshallerAttribute.ConstructorArguments.Select(a => gen.AttributeArgument(gen.TypedConstantExpression(a)))); + + CustomTypeMarshallerFeatures newFeaturesValue = featuresToAdd; + int? featuresArgLocation = null; + + newAttributeSyntax = gen.AddAttributeArguments(newAttributeSyntax, customTypeMarshallerAttribute.NamedArguments + .Where((a, i) => + { + if (a.Key == "Features") + { + // Capture the original location of the 'Features' named argument so we can update it "in place". + featuresArgLocation = customTypeMarshallerAttribute.ConstructorArguments.Length + i; + newFeaturesValue |= (CustomTypeMarshallerFeatures)a.Value.Value; + return false; + } + return true; + }) + .Select(a => gen.AttributeArgument(a.Key, gen.TypedConstantExpression(a.Value)))); + + SyntaxNode featureAttributeArgument = gen.AttributeArgument("Features", + gen.GetEnumValueAsFlagsExpression( + customTypeMarshallerAttribute.AttributeClass.GetMembers(ManualTypeMarshallingHelper.CustomMarshallerAttributeFields.Features).OfType().First().Type, + (int)newFeaturesValue, + includeZeroValueFlags: false)); + + newAttributeSyntax = featuresArgLocation is null + ? gen.AddAttributeArguments(newAttributeSyntax, new[] { featureAttributeArgument }) + : gen.InsertAttributeArguments(newAttributeSyntax, featuresArgLocation.Value, new[] { featureAttributeArgument }); + + return newAttributeSyntax; + } private static async Task AddMissingMembers(Document doc, SyntaxNode node, List missingMemberNames, CancellationToken ct) { var editor = await DocumentEditor.CreateAsync(doc, ct).ConfigureAwait(false); var gen = editor.Generator; - SyntaxNode updatedDeclaration = AddMissingMembers(node, (ITypeSymbol)editor.SemanticModel.GetDeclaredSymbol(node, ct), missingMemberNames, editor.SemanticModel.Compilation, gen, ct); + SyntaxNode updatedDeclaration = AddMissingMembers(node, (ITypeSymbol)editor.SemanticModel.GetDeclaredSymbol(node, ct), missingMemberNames, editor.SemanticModel.Compilation, gen); editor.ReplaceNode(node, updatedDeclaration); @@ -100,7 +238,7 @@ private static async Task AddMissingMembers(Document doc, SyntaxNode n } private static SyntaxNode AddMissingMembers(SyntaxNode node, ITypeSymbol - marshallerType, List missingMemberNames, Compilation compilation, SyntaxGenerator gen, CancellationToken ct) + marshallerType, List missingMemberNames, Compilation compilation, SyntaxGenerator gen) { INamedTypeSymbol @byte = compilation.GetSpecialType(SpecialType.System_Byte); INamedTypeSymbol @object = compilation.GetSpecialType(SpecialType.System_Object); diff --git a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/SyntaxGeneratorExtensions.cs b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/SyntaxGeneratorExtensions.cs new file mode 100644 index 0000000000000..1bda8e4cc7765 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/SyntaxGeneratorExtensions.cs @@ -0,0 +1,67 @@ +// 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; +using Microsoft.CodeAnalysis.Editing; + +namespace Microsoft.Interop.Analyzers +{ + internal static class SyntaxGeneratorExtensions + { + public static SyntaxNode GetEnumValueAsFlagsExpression(this SyntaxGenerator gen, ITypeSymbol enumType, object value, bool includeZeroValueFlags) + { + if (enumType.TypeKind != TypeKind.Enum) + { + throw new ArgumentException(nameof(enumType)); + } + + SpecialType underlyingType = ((INamedTypeSymbol)enumType).EnumUnderlyingType.SpecialType; + + if (!underlyingType.IsIntegralType()) + { + return gen.CastExpression(gen.TypeExpression(underlyingType), gen.LiteralExpression(value)); + } + + ulong valueToMatch = GetNumericValue(value); + + ulong currentlyMatchedFlags = 0; + SyntaxNode? enumValueSyntax = null; + foreach (ISymbol member in enumType.GetMembers()) + { + if (member is IFieldSymbol { HasConstantValue: true } enumValue) + { + ulong fieldNumericValue = GetNumericValue(enumValue.ConstantValue); + if (fieldNumericValue == 0 && !includeZeroValueFlags) + { + continue; + } + if ((fieldNumericValue & valueToMatch) == fieldNumericValue) + { + currentlyMatchedFlags |= fieldNumericValue; + enumValueSyntax = enumValueSyntax is null + ? gen.MemberAccessExpression(gen.TypeExpression(enumType), enumValue.Name) + : gen.BitwiseOrExpression(enumValueSyntax, gen.MemberAccessExpression(gen.TypeExpression(enumType), enumValue.Name)); + } + } + } + + // Unable to represent the value as the enum flags. Just use the literal value cast as the enum type. + if (currentlyMatchedFlags != valueToMatch) + { + return gen.CastExpression(gen.TypeExpression(underlyingType), gen.LiteralExpression(value)); + } + + return enumValueSyntax; + + static ulong GetNumericValue(object value) => value switch + { + byte or ushort or uint or ulong => Convert.ToUInt64(value), + sbyte or short or int or long => (ulong)Convert.ToInt64(value), + _ => throw new UnreachableException() + }; + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Resources.Designer.cs b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Resources.Designer.cs index 96920cb6093c4..1b6033d010003 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Resources.Designer.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Resources.Designer.cs @@ -69,6 +69,33 @@ internal static string AddMissingCustomTypeMarshallerMembers { } } + /// + /// Looks up a localized string similar to Add missing features to the 'CustomTypeMarshallerAttribute' attribute. + /// + internal static string AddMissingFeaturesToCustomTypeMarshaller { + get { + return ResourceManager.GetString("AddMissingFeaturesToCustomTypeMarshaller", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to A marshaller type that provides a constructor taking a caller-allocated 'Span<byte>' should specify that it supports the 'CallerAllocatedBuffer' feature.. + /// + internal static string CallerAllocatedBufferConstructorProvidedShouldSpecifyFeatureDescription { + get { + return ResourceManager.GetString("CallerAllocatedBufferConstructorProvidedShouldSpecifyFeatureDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The type '{0}' provides a constructor taking a caller-allocated 'Span<byte>' but does not specify that it supports the 'CallerAllocatedBuffer' feature. The constructor will not be used unless the feature is specified.. + /// + internal static string CallerAllocatedBufferConstructorProvidedShouldSpecifyFeatureMessage { + get { + return ResourceManager.GetString("CallerAllocatedBufferConstructorProvidedShouldSpecifyFeatureMessage", resourceCulture); + } + } + /// /// Looks up a localized string similar to When a constructor taking a ''Span<byte> is specified on the native type, the type must set the BufferSize field on the 'System.Runtime.InteropServices.CustomTypeMarshallerAttribute' to provide the size of the caller-allocated buffer.. /// @@ -321,6 +348,42 @@ internal static string CustomTypeMarshallingNativeToManagedUnsupported { } } + /// + /// Looks up a localized string similar to A marshaller type that provides a 'FreeNative' method should specify that it supports the 'UnmanagedResources' feature.. + /// + internal static string FreeNativeMethodProvidedShouldSpecifyUnmanagedResourcesFeatureDescription { + get { + return ResourceManager.GetString("FreeNativeMethodProvidedShouldSpecifyUnmanagedResourcesFeatureDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The type '{0}' provides a 'FreeNative' method but does not specify that it supports the 'UnmanagedResources' feature. The method will not be used unless the feature is specified.. + /// + internal static string FreeNativeMethodProvidedShouldSpecifyUnmanagedResourcesFeatureMessage { + get { + return ResourceManager.GetString("FreeNativeMethodProvidedShouldSpecifyUnmanagedResourcesFeatureMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to A marshaller type that provides a 'FromNativeValue' method should specify that it supports the 'TwoStageMarshalling' feature.. + /// + internal static string FromNativeValueMethodProvidedShouldSpecifyTwoStageMarshallingFeatureDescription { + get { + return ResourceManager.GetString("FromNativeValueMethodProvidedShouldSpecifyTwoStageMarshallingFeatureDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The type '{0}' provides a 'FromNativeValue' method but does not specify that it supports the 'TwoStageMarshalling' feature. The method will not be used unless the feature is specified.. + /// + internal static string FromNativeValueMethodProvidedShouldSpecifyTwoStageMarshallingFeatureMessage { + get { + return ResourceManager.GetString("FromNativeValueMethodProvidedShouldSpecifyTwoStageMarshallingFeatureMessage", resourceCulture); + } + } + /// /// Looks up a localized string similar to The return type of 'GetPinnableReference' (after accounting for 'ref') must be blittable.. /// @@ -447,6 +510,24 @@ internal static string InvalidStringMarshallingConfigurationNotCustom { } } + /// + /// Looks up a localized string similar to The element type of the 'ReadOnlySpan' returned by 'GetManagedValuesSource' must be the same as the element type returned by 'GetManagedValuesDestination'.. + /// + internal static string LinearCollectionElementTypesMustMatchDescription { + get { + return ResourceManager.GetString("LinearCollectionElementTypesMustMatchDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The element type of the 'ReadOnlySpan' returned by 'GetManagedValuesSource' must be the same as the element type returned by 'GetManagedValuesDestination'. + /// + internal static string LinearCollectionElementTypesMustMatchMessage { + get { + return ResourceManager.GetString("LinearCollectionElementTypesMustMatchMessage", resourceCulture); + } + } + /// /// Looks up a localized string similar to A 'LinearCollection'-kind native type that supports the 'CallerAllocatedBuffer' feature must provide a three-parameter constructor taking the managed type as the first parameter, a 'Span<byte>' as the second parameter, and the native size of the element as the second parameter. /// @@ -771,6 +852,42 @@ internal static string TargetFrameworkNotSupportedTitle { } } + /// + /// Looks up a localized string similar to A marshaller type that provides a 'ToNativeValue' method should specify that it supports the 'TwoStageMarshalling' feature.. + /// + internal static string ToNativeValueMethodProvidedShouldSpecifyTwoStageMarshallingFeatureDescription { + get { + return ResourceManager.GetString("ToNativeValueMethodProvidedShouldSpecifyTwoStageMarshallingFeatureDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The type '{0}' provides a 'ToNativeValue' method but does not specify that it supports the 'TwoStageMarshalling' feature. The method will not be used unless the feature is specified.. + /// + internal static string ToNativeValueMethodProvidedShouldSpecifyTwoStageMarshallingFeatureMessage { + get { + return ResourceManager.GetString("ToNativeValueMethodProvidedShouldSpecifyTwoStageMarshallingFeatureMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The return type of 'ToNativeValue' and the parameter type of 'FromNativeValue' must be the same.. + /// + internal static string TwoStageMarshallingNativeTypesMustMatchDescription { + get { + return ResourceManager.GetString("TwoStageMarshallingNativeTypesMustMatchDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The return type of 'ToNativeValue' and the parameter type of 'FromNativeValue' must be the same. + /// + internal static string TwoStageMarshallingNativeTypesMustMatchMessage { + get { + return ResourceManager.GetString("TwoStageMarshallingNativeTypesMustMatchMessage", resourceCulture); + } + } + /// /// Looks up a localized string similar to For types that are not supported by source-generated P/Invokes, the resulting P/Invoke will rely on the underlying runtime to marshal the specified type.. /// diff --git a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Resources.resx b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Resources.resx index 9f4f6611d149f..017b2fe0f9c71 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Resources.resx +++ b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Resources.resx @@ -398,4 +398,43 @@ Add missing custom type marshaller members + + Add missing features to the 'CustomTypeMarshallerAttribute' attribute + + + A marshaller type that provides a constructor taking a caller-allocated 'Span<byte>' should specify that it supports the 'CallerAllocatedBuffer' feature. + + + The type '{0}' provides a constructor taking a caller-allocated 'Span<byte>' but does not specify that it supports the 'CallerAllocatedBuffer' feature. The constructor will not be used unless the feature is specified. + + + A marshaller type that provides a 'FreeNative' method should specify that it supports the 'UnmanagedResources' feature. + + + The type '{0}' provides a 'FreeNative' method but does not specify that it supports the 'UnmanagedResources' feature. The method will not be used unless the feature is specified. + + + A marshaller type that provides a 'FromNativeValue' method should specify that it supports the 'TwoStageMarshalling' feature. + + + The type '{0}' provides a 'FromNativeValue' method but does not specify that it supports the 'TwoStageMarshalling' feature. The method will not be used unless the feature is specified. + + + The element type of the 'ReadOnlySpan' returned by 'GetManagedValuesSource' must be the same as the element type returned by 'GetManagedValuesDestination'. + + + The element type of the 'ReadOnlySpan' returned by 'GetManagedValuesSource' must be the same as the element type returned by 'GetManagedValuesDestination' + + + A marshaller type that provides a 'ToNativeValue' method should specify that it supports the 'TwoStageMarshalling' feature. + + + The type '{0}' provides a 'ToNativeValue' method but does not specify that it supports the 'TwoStageMarshalling' feature. The method will not be used unless the feature is specified. + + + The return type of 'ToNativeValue' and the parameter type of 'FromNativeValue' must be the same. + + + The return type of 'ToNativeValue' and the parameter type of 'FromNativeValue' must be the same + \ No newline at end of file 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 4b609d84dcac5..b675d0e7f1aea 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 @@ -60,7 +60,7 @@ public static (bool hasAttribute, ITypeSymbol? managedType, CustomTypeMarshaller } CustomTypeMarshallerKind kind = CustomTypeMarshallerKind.Value; ITypeSymbol? managedType = attr.ConstructorArguments[0].Value as ITypeSymbol; - if (attr.ConstructorArguments.Length >= 1) + if (attr.ConstructorArguments.Length > 1) { if (attr.ConstructorArguments[1].Value is not int i) { diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CustomTypeMarshallerFixerTests.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CustomTypeMarshallerFixerTests.cs index a050b5c80f25c..a94ec16a758d8 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CustomTypeMarshallerFixerTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CustomTypeMarshallerFixerTests.cs @@ -151,7 +151,7 @@ struct S public string s; } -[CustomTypeMarshaller(typeof(S))] +[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] struct Native { private IntPtr value; @@ -185,7 +185,7 @@ struct S public string s; } -[CustomTypeMarshaller(typeof(S))] +[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] struct Native { private string value; @@ -219,7 +219,7 @@ class S public ref string {|#0:GetPinnableReference|}() => ref s; } -[CustomTypeMarshaller(typeof(S))] +[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] unsafe struct Native { private IntPtr value; @@ -255,7 +255,7 @@ class S public ref byte GetPinnableReference() => ref c; } -[CustomTypeMarshaller(typeof(S))] +[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] unsafe struct Native { private IntPtr value; @@ -287,7 +287,7 @@ class S public char c; } -[CustomTypeMarshaller(typeof(S))] +[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] unsafe struct Native { private IntPtr value; @@ -321,7 +321,7 @@ class S public byte c; } -[CustomTypeMarshaller(typeof(S))] +[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] unsafe struct Native { private IntPtr value; @@ -357,7 +357,7 @@ class S public ref byte GetPinnableReference() => ref c; } -[CustomTypeMarshaller(typeof(S))] +[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] unsafe struct Native { private IntPtr value; @@ -393,7 +393,7 @@ class S public ref byte GetPinnableReference() => ref c; } -[CustomTypeMarshaller(typeof(S))] +[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] unsafe struct Native { private IntPtr value; @@ -716,12 +716,9 @@ public Native(S managed, Span buffer, int nativeElementSize) }"; await VerifyCS.VerifyCodeFixAsync(source, - new[] - { + fixedSource, VerifyCS.Diagnostic(LinearCollectionInRequiresTwoParameterConstructorRule).WithLocation(0).WithArguments("Native", "S"), - VerifyCS.Diagnostic(LinearCollectionInCallerAllocatedBufferRequiresSpanConstructorRule).WithLocation(0).WithArguments("Native", "S") - }, - fixedSource); + VerifyCS.Diagnostic(LinearCollectionInCallerAllocatedBufferRequiresSpanConstructorRule).WithLocation(0).WithArguments("Native", "S")); } [ConditionalFact] @@ -772,12 +769,9 @@ public Native(S managed, int nativeElementSize) }"; await VerifyCS.VerifyCodeFixAsync(source, - new[] - { + fixedSource, VerifyCS.Diagnostic(CallerAllocMarshallingShouldSupportAllocatingMarshallingFallbackRule).WithLocation(0).WithArguments("Native", "S"), - VerifyCS.Diagnostic(LinearCollectionInRequiresTwoParameterConstructorRule).WithLocation(0).WithArguments("Native", "S"), - }, - fixedSource); + VerifyCS.Diagnostic(LinearCollectionInRequiresTwoParameterConstructorRule).WithLocation(0).WithArguments("Native", "S")); } [ConditionalFact] @@ -793,7 +787,7 @@ class S public byte c; } -[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection, Direction = CustomTypeMarshallerDirection.In)] +[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection, Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] ref struct {|#0:Native|} { public Native(S s, int nativeElementSize) : this() {} @@ -811,7 +805,7 @@ class S public byte c; } -[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection, Direction = CustomTypeMarshallerDirection.In)] +[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection, Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] ref struct Native { public Native(S s, int nativeElementSize) : this() {} @@ -843,7 +837,7 @@ class S public byte c; } -[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection, Direction = CustomTypeMarshallerDirection.In)] +[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection, Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] ref struct {|#0:Native|} { public Native(S s, int nativeElementSize) : this() {} @@ -861,7 +855,7 @@ class S public byte c; } -[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection, Direction = CustomTypeMarshallerDirection.In)] +[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection, Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] ref struct Native { public Native(S s, int nativeElementSize) : this() {} @@ -880,6 +874,71 @@ await VerifyCS.VerifyCodeFixAsync(source, fixedSource); } + [ConditionalFact] + public async Task CollectionNativeTypeWithCorrectRefShape_DoesNotReportDiagnostic() + { + string source = @" +using System; +using System.Runtime.InteropServices; + +[NativeMarshalling(typeof(Native))] +class S +{ + public byte c; +} + +[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] +ref struct Native +{ + public Native(int nativeElementSize) : this() {} + public Native(S s, int nativeElementSize) : this() {} + + public ReadOnlySpan GetManagedValuesSource() => throw null; + public Span GetNativeValuesDestination() => throw null; + public ReadOnlySpan GetNativeValuesSource(int length) => throw null; + public Span GetManagedValuesDestination(int length) => throw null; + public IntPtr ToNativeValue() => throw null; + public void FromNativeValue(IntPtr value) => throw null; + public S ToManaged() => throw null; +}"; + + await VerifyCS.VerifyCodeFixAsync(source, source); + } + + [ConditionalFact] + public async Task CollectionNativeTypeWithMismatched_Element_Type_ReportsDiagnostic() + { + string source = @" +using System; +using System.Runtime.InteropServices; + +[NativeMarshalling(typeof(Native))] +class S +{ + public byte c; +} + +[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.LinearCollection, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] +ref struct Native +{ + public Native(int nativeElementSize) : this() {} + public Native(S s, int nativeElementSize) : this() {} + + public ReadOnlySpan {|#0:GetManagedValuesSource|}() => throw null; + public Span GetNativeValuesDestination() => throw null; + public ReadOnlySpan GetNativeValuesSource(int length) => throw null; + public Span GetManagedValuesDestination(int length) => throw null; + public IntPtr ToNativeValue() => throw null; + public void FromNativeValue(IntPtr value) => throw null; + public S ToManaged() => throw null; +}"; + + await VerifyCS.VerifyCodeFixAsync(source, + source, + VerifyCS.Diagnostic(LinearCollectionElementTypesMustMatchRule) + .WithLocation(0)); + } + [ConditionalFact] public async Task NativeTypeWithOnlyConstructor_DoesNotReportDiagnostic() { @@ -964,12 +1023,9 @@ public Native(S managed) }"; await VerifyCS.VerifyCodeFixAsync(source, - new[] - { + fixedSource, VerifyCS.Diagnostic(CallerAllocMarshallingShouldSupportAllocatingMarshallingFallbackRule).WithLocation(0).WithArguments("Native", "S"), - VerifyCS.Diagnostic(ValueInRequiresOneParameterConstructorRule).WithLocation(0).WithArguments("Native", "S"), - }, - fixedSource); + VerifyCS.Diagnostic(ValueInRequiresOneParameterConstructorRule).WithLocation(0).WithArguments("Native", "S")); } [ConditionalFact] @@ -1331,12 +1387,9 @@ public Native(S s) public void FromNativeValue(T value) => throw null; }"; await VerifyCS.VerifyCodeFixAsync(source, - new[] - { + source, VerifyCS.Diagnostic(NativeTypeMustHaveCustomTypeMarshallerAttributeRule).WithLocation(0).WithArguments("S"), - VerifyCS.Diagnostic(NativeGenericTypeMustBeClosedOrMatchArityRule).WithLocation(1).WithArguments("Native", "S<>") - }, - source); + VerifyCS.Diagnostic(NativeGenericTypeMustBeClosedOrMatchArityRule).WithLocation(1).WithArguments("Native", "S<>")); } [ConditionalFact] @@ -1439,5 +1492,262 @@ public Native(T*[] a) {} await VerifyCS.VerifyCodeFixAsync(source, source); } + + [ConditionalFact] + public async Task CustomTypeMarshallerWithFreeNativeMethod_NoUnmanagedResourcesFeatures_ReportsDiagnostic() + { + string source = @" +using System; +using System.Runtime.InteropServices; + +struct S { } + +[CustomTypeMarshaller(typeof(S), Direction = CustomTypeMarshallerDirection.In)] +unsafe struct {|#0:Native|} +{ + public Native(S s){} + + public void FreeNative() { } +}"; + string fixedSource = @" +using System; +using System.Runtime.InteropServices; + +struct S { } + +[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.Value, Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.UnmanagedResources)] +unsafe struct Native +{ + public Native(S s){} + + public void FreeNative() { } +}"; + await VerifyCS.VerifyCodeFixAsync(source, + fixedSource, + VerifyCS.Diagnostic(FreeNativeMethodProvidedShouldSpecifyUnmanagedResourcesFeatureRule) + .WithArguments("Native") + .WithLocation(0)); + } + [ConditionalFact] + public async Task CustomTypeMarshallerWithCallerAllocatedBufferConstructor_NoFeature_ReportsDiagnostic() + { + string source = @" +using System; +using System.Runtime.InteropServices; + +struct S { } + +[CustomTypeMarshaller(typeof(S), Direction = CustomTypeMarshallerDirection.In, BufferSize = 0x100)] +unsafe struct {|#0:Native|} +{ + public Native(S s){} + + public Native(S s, Span buffer) { } +}"; + string fixedSource = @" +using System; +using System.Runtime.InteropServices; + +struct S { } + +[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.Value, Direction = CustomTypeMarshallerDirection.In, BufferSize = 256, Features = CustomTypeMarshallerFeatures.CallerAllocatedBuffer)] +unsafe struct Native +{ + public Native(S s){} + + public Native(S s, Span buffer) { } +}"; + await VerifyCS.VerifyCodeFixAsync(source, + fixedSource, + VerifyCS.Diagnostic(CallerAllocatedBufferConstructorProvidedShouldSpecifyFeatureRule) + .WithArguments("Native") + .WithLocation(0)); + } + + [ConditionalFact] + public async Task Add_Feature_Declaration_Preserves_Attribute_Argument_Location() + { + string source = @" +using System; +using System.Runtime.InteropServices; + +struct S { } + +[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.None, Direction = CustomTypeMarshallerDirection.In)] +unsafe struct {|#0:Native|} +{ + public Native(S s){} + + public void FreeNative() { } +}"; + string fixedSource = @" +using System; +using System.Runtime.InteropServices; + +struct S { } + +[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.Value, Features = CustomTypeMarshallerFeatures.UnmanagedResources, Direction = CustomTypeMarshallerDirection.In)] +unsafe struct Native +{ + public Native(S s){} + + public void FreeNative() { } +}"; + await VerifyCS.VerifyCodeFixAsync(source, + fixedSource, + VerifyCS.Diagnostic(FreeNativeMethodProvidedShouldSpecifyUnmanagedResourcesFeatureRule) + .WithArguments("Native") + .WithLocation(0)); + } + + [ConditionalFact] + public async Task CustomTypeMarshallerWithTwoStageMarshallingMethod_NoFeature_ReportsDiagnostic() + { + string source = @" +using System; +using System.Runtime.InteropServices; + +struct S { } + +[CustomTypeMarshaller(typeof(S))] +unsafe struct {|#0:Native|} +{ + public Native(S s){} + + public int ToNativeValue() => throw null; + + public S ToManaged() => throw null; +} + +[CustomTypeMarshaller(typeof(S))] +unsafe struct {|#1:Native2|} +{ + public Native2(S s){} + + public void FromNativeValue(int value) { } + + public S ToManaged() => throw null; +}"; + string fixedSource = @" +using System; +using System.Runtime.InteropServices; + +struct S { } + +[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.Value, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] +unsafe struct Native +{ + public Native(S s){} + + public int ToNativeValue() => throw null; + + public S ToManaged() => throw null; + + public void FromNativeValue(int value) + { + throw new NotImplementedException(); + } +} + +[CustomTypeMarshaller(typeof(S), CustomTypeMarshallerKind.Value, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] +unsafe struct Native2 +{ + public Native2(S s){} + + public void FromNativeValue(int value) { } + + public S ToManaged() => throw null; + + public int ToNativeValue() + { + throw new NotImplementedException(); + } +}"; + await VerifyCS.VerifyCodeFixAsync(source, + new[] + { + VerifyCS.Diagnostic(ToNativeValueMethodProvidedShouldSpecifyTwoStageMarshallingFeatureRule) + .WithArguments("Native") + .WithLocation(0), + VerifyCS.Diagnostic(FromNativeValueMethodProvidedShouldSpecifyTwoStageMarshallingFeatureRule) + .WithArguments("Native2") + .WithLocation(1) + }, + fixedSource, + // One code-fix run is expected for each of the two diagnostics. + // Each fix of the "specifiy the feature" diagnostic will result in code that reports another diagnostic + // for the missing other member. + // The second two code-fix runs are the fixes for those diagnostics. + numIncrementalIterations: 4, + // The first run adds the feature flag and the second adds the missing members for the feature. + numFixAllIterations: 2); + } + + [ConditionalFact] + public async Task Mismatched_NativeValue_Type_ReportsDiagnostic() + { + string source = @" +using System; +using System.Runtime.InteropServices; + +[NativeMarshalling(typeof(Native))] +class S +{ + public byte c; +} + +[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] +unsafe struct Native +{ + public Native(S s) { } + + public S ToManaged() => new S(); + + public int {|#0:ToNativeValue|}() => throw null; + public void FromNativeValue(long value) => throw null; +}"; + + await VerifyCS.VerifyCodeFixAsync(source, + source, + VerifyCS.Diagnostic(TwoStageMarshallingNativeTypesMustMatchRule) + .WithLocation(0)); + } + + [ConditionalFact] + public async Task Same_NativeValue_Type_DifferentName_DoesNotReportDiagnostic() + { + string source = @" +using System; +using System.Runtime.InteropServices; +using Value2 = N.Value; + +namespace N +{ + struct Value + { + private int i; + } +} + +[NativeMarshalling(typeof(Native))] +class S +{ + public byte c; +} + +[CustomTypeMarshaller(typeof(S), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] +unsafe struct Native +{ + public Native(S s) { } + + public S ToManaged() => new S(); + + public N.Value ToNativeValue() => throw null; + public void FromNativeValue(Value2 value) => throw null; +}"; + + await VerifyCS.VerifyCodeFixAsync(source, + source); + } } } diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/Verifiers/CSharpCodeFixVerifier.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/Verifiers/CSharpCodeFixVerifier.cs index 1b43049882d20..3d3ec763c26d4 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/Verifiers/CSharpCodeFixVerifier.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/Verifiers/CSharpCodeFixVerifier.cs @@ -66,6 +66,34 @@ public static async Task VerifyCodeFixAsync(string source, DiagnosticResult[] ex await test.RunAsync(CancellationToken.None); } + /// + public static async Task VerifyCodeFixAsync(string source, string fixedSource, params DiagnosticResult[] expected) + { + var test = new Test + { + TestCode = source, + FixedCode = fixedSource, + }; + + test.ExpectedDiagnostics.AddRange(expected); + await test.RunAsync(CancellationToken.None); + } + + public static async Task VerifyCodeFixAsync(string source, DiagnosticResult[] expected, string fixedSource, + int numIncrementalIterations, int numFixAllIterations) + { + var test = new Test + { + TestCode = source, + FixedCode = fixedSource, + NumberOfIncrementalIterations = numIncrementalIterations, + NumberOfFixAllIterations = numFixAllIterations + }; + + test.ExpectedDiagnostics.AddRange(expected); + await test.RunAsync(CancellationToken.None); + } + internal class Test : CSharpCodeFixTest { public Test() From 8ea9328f23fb3e65c00d497e0d8f2e19e05e1e6d Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 14 Mar 2022 17:41:09 -0700 Subject: [PATCH 19/24] Allow using the generic placeholder on its own when the managed type is the first generic parameter. --- .../ManualTypeMarshallingHelper.cs | 2 +- .../CustomTypeMarshallerFixerTests.cs | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) 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 b675d0e7f1aea..3b190b6ad0433 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 @@ -84,7 +84,7 @@ public static (bool hasAttribute, ITypeSymbol? managedType, CustomTypeMarshaller /// The resolved managed type, or if the provided type did not have any placeholders. public static ITypeSymbol? ResolveManagedType(ITypeSymbol? managedType, INamedTypeSymbol marshallerType, Compilation compilation) { - if (managedType is null or INamedTypeSymbol || !marshallerType.IsGenericType) + if (managedType is null || !marshallerType.IsGenericType) { return managedType; } diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CustomTypeMarshallerFixerTests.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CustomTypeMarshallerFixerTests.cs index a94ec16a758d8..aa9029be4d2be 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CustomTypeMarshallerFixerTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CustomTypeMarshallerFixerTests.cs @@ -1445,6 +1445,22 @@ await VerifyCS.VerifyCodeFixAsync(source, source); } + [ConditionalFact] + public async Task CustomTypeMarshallerForTypeWithPlaceholder_DoesNotReportDiagnostic() + { + string source = @" +using System; +using System.Runtime.InteropServices; + +[CustomTypeMarshaller(typeof(CustomTypeMarshallerAttribute.GenericPlaceholder), Direction = CustomTypeMarshallerDirection.In)] +struct Native +{ + public Native(T a) {} +}"; + + await VerifyCS.VerifyCodeFixAsync(source, source); + } + [ConditionalFact] public async Task CustomTypeMarshallerForArrayTypeWithPlaceholder_DoesNotReportDiagnostic() { From 61673ac329aaf2b1545600314f14fb271156cd5a Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 17 Mar 2022 14:26:53 -0700 Subject: [PATCH 20/24] PR feedback. --- .../LibraryImportGenerator/SpanMarshallers.md | 53 +++++++++++++------ .../StructMarshalling.md | 11 ++-- docs/project/list-of-diagnostics.md | 33 ++++++------ .../CustomTypeMarshallerDirection.cs | 23 ++++++-- .../CustomTypeMarshallerFeatures.cs | 23 ++++++-- .../CustomTypeMarshallerKind.cs | 18 +++++-- .../GeneratedMarshallingAttribute.cs | 28 ++++++++++ 7 files changed, 137 insertions(+), 52 deletions(-) diff --git a/docs/design/libraries/LibraryImportGenerator/SpanMarshallers.md b/docs/design/libraries/LibraryImportGenerator/SpanMarshallers.md index a6badd54e0e2f..3bd01372d6ac3 100644 --- a/docs/design/libraries/LibraryImportGenerator/SpanMarshallers.md +++ b/docs/design/libraries/LibraryImportGenerator/SpanMarshallers.md @@ -24,11 +24,11 @@ We have decided to match the managed semantics of `(ReadOnly)Span` to provide As part of this design, we would also want to include some in-box marshallers that follow the design laid out in the [Struct Marshalling design doc](./StructMarshalling.md) to support some additional scenarios: -- A marshaler that marshals an empty span as a non-null pointer. +- A marshaller that marshals an empty span as a non-null pointer. - This marshaller would only support empty spans as it cannot correctly represent non-empty spans of non-blittable types. -- A marshaler that marshals out a pointer to the native memory as a Span instead of copying the data into a managed array. +- A marshaller that marshals out a pointer to the native memory as a Span instead of copying the data into a managed array. - This marshaller would only support blittable spans by design. - - This marshaler will require the user to manually release the memory. Since this will be an opt-in marshaler, this scenario is already advanced and that additional requirement should be understandable to users who use this marshaler. + - This marshaller will require the user to manually release the memory. Since this will be an opt-in marshaller, this scenario is already advanced and that additional requirement should be understandable to users who use this marshaller. - Since there is no mechansim to provide a collection length, the question of how to provide the span's length in this case is still unresolved. One option would be to always provide a length 1 span and require the user to create a new span with the correct size, but that feels like a bad design. ### Pros/Cons of Design 1 @@ -40,7 +40,7 @@ Pros: Cons: -- Defining custom marshalers for non-empty spans of non-blittable types generically is impossible since the marshalling rules of the element's type cannot be known. +- Defining custom marshallers for non-empty spans of non-blittable types generically is impossible since the marshalling rules of the element's type cannot be known. - Custom non-default marshalling of the span element types is impossible for non-built-in types. - Inlining the span marshalling fully into the stub increases on-disk IL size. - This design does not enable developers to easily define custom marshalling support for their own collection types, which may be desireable. @@ -83,30 +83,55 @@ namespace System.Runtime.InteropServices The attribute would be used with a collection type like `Span` as follows: ```csharp -[NativeTypeMarshalling(typeof(DefaultSpanMarshaler<>))] +[NativeTypeMarshalling(typeof(DefaultSpanMarshaller<>))] public ref struct Span { ... } [CustomTypeMarshaller(typeof(Span<>), CustomTypeMarshallerKind.LinearCollection)] -public ref struct DefaultSpanMarshaler +public ref struct DefaultSpanMarshaller { ... } ``` -The `CustomTypeMarshallerKind.LinearCollection` kind is applied to a generic marshaler type with the "LinearCollection marshaller shape" described below. +The `CustomTypeMarshallerKind.LinearCollection` kind is applied to a generic marshaller type with the "LinearCollection marshaller shape" described below. #### Supporting generics -Since generic parameters cannot be used in attributes, open generic types will be permitted in the `NativeTypeMarshallingAttribute` and the `CustomTypeMarshallerAttribute` as long as they have the same arity as the type the attribute is applied to and generic parameters provided to the applied-to type can also be used to construct the type passed as a parameter. +Since generic parameters cannot be used in attributes, open generic types will be permitted in the `NativeTypeMarshallingAttribute` and the `CustomTypeMarshallerAttribute` as long as they have the same arity as the type with the attribute and generic parameters provided to the type with the attribute can also be used to construct the type passed as a parameter. If a `CustomTypeMarshaller`-attributed type is a marshaller for a type for a pointer, an array, or a combination of pointers and arrays, the `CustomTypeMarshallerAttribute.GenericPlaceholder` type can be used in the place of the first generic parameter of the marshaller type. +For example: + +```csharp +[CustomTypeMarshaller(typeof(CustomTypeMarshallerAttribute.GenericPlaceholder), Direction = CustomTypeMarshallerDirection.In)] +struct Marshaller +{ + public Marshaller(T managed); +} +[CustomTypeMarshaller(typeof(CustomTypeMarshallerAttribute.GenericPlaceholder[]), Direction = CustomTypeMarshallerDirection.In)] +struct Marshaller +{ + public Marshaller(T[] managed); +} +[CustomTypeMarshaller(typeof(CustomTypeMarshallerAttribute.GenericPlaceholder*), Direction = CustomTypeMarshallerDirection.In)] +struct Marshaller where T : unmanaged +{ + public Marshaller(T* managed); +} +[CustomTypeMarshaller(typeof(CustomTypeMarshallerAttribute.GenericPlaceholder*[]), Direction = CustomTypeMarshallerDirection.In)] +struct Marshaller where T : unmanaged +{ + public Marshaller(T*[] managed); +} +``` + #### LinearCollection marshaller shape -A generic collection marshaller would be required to have the following shape, in addition to the requirements for marshaler types used with the `CustomTypeMarshallerKind.Value` shape, excluding the constructors. +A generic collection marshaller would be required to have the following shape, in addition to the requirements for marshaller types used with the `CustomTypeMarshallerKind.Value` shape, excluding the constructors. ```csharp [CustomTypeMarshaller(typeof(GenericCollection<, , ,...>), CustomTypeMarshallerKind.LinearCollection)] @@ -118,8 +143,6 @@ public struct GenericContiguousCollectionMarshallerImpl public GenericContiguousCollectionMarshallerImpl(GenericCollection collection, int nativeSizeOfElement); public GenericContiguousCollectionMarshallerImpl(GenericCollection collection, Span stackSpace, int nativeSizeOfElement); // optional - public const int StackBufferSize = /* */; // required if the span-based constructor is supplied. - /// /// A span that points to the memory where the managed values of the collection are stored (in the marshalling case) or should be stored (in the unmarshalling case). /// @@ -180,7 +203,7 @@ To support supplying information about collection element counts, a parameterles The `ElementIndirectionLevel` property is added to support supplying marshalling info for element types in a collection. For example, if the user is passing a `List>` from managed to native code, they could provide the following attributes to specify marshalling rules for the outer and inner lists and `Foo` separately: ```csharp -private static partial void Bar([MarshalUsing(typeof(ListAsArrayMarshaller>), CountElementName = nameof(count)), MarshalUsing(ConstantElementCount = 10, ElementIndirectionLevel = 1), MarshalUsing(typeof(FooMarshaler), ElementIndirectionLevel = 2)] List> foos, int count); +private static partial void Bar([MarshalUsing(typeof(ListAsArrayMarshaller>), CountElementName = nameof(count)), MarshalUsing(ConstantElementCount = 10, ElementIndirectionLevel = 1), MarshalUsing(typeof(FooMarshaller), ElementIndirectionLevel = 2)] List> foos, int count); ``` Multiple `MarshalUsing` attributes can only be supplied on the same parameter or return value if the `ElementIndirectionLevel` property is set to distinct values. One `MarshalUsing` attribute per parameter or return value can leave the `ElementIndirectionLevel` property unset. This attribute controls the marshalling of the collection object passed in as the parameter. The sequence of managed types for `ElementIndirectionLevel` is based on the elements of the `ManagedValues` span on the collection marshaller of the previous indirection level. For example, for the marshalling info for `ElementIndirectionLevel = 1` above, the managed type is the type of the following C# expression: `ListAsArrayMarshaller>.ManagedValues[0]`. @@ -193,13 +216,13 @@ This design could be used to provide a default marshaller for spans and arrays. ```csharp [CustomTypeMarshaller(typeof(Span<>), CustomTypeMarshallerKind.LinearCollection)] -public ref struct SpanMarshaler +public ref struct SpanMarshaller { private Span managedCollection; private int nativeElementSize; - public SpanMarshaler(Span collection, int nativeSizeOfElement) + public SpanMarshaller(Span collection, int nativeSizeOfElement) { managedCollection = collection; Value = Marshal.AllocCoTaskMem(collection.Length * nativeSizeOfElement); @@ -290,8 +313,6 @@ public struct GenericContiguousCollectionMarshallerImpl public GenericContiguousCollectionMarshallerImpl(GenericCollection collection, int nativeSizeOfElements); public GenericContiguousCollectionMarshallerImpl(GenericCollection collection, Span stackSpace, int nativeSizeOfElements); // optional - public const int StackBufferSize = /* */; // required if the span-based constructor is supplied. - - public Span ManagedValues { get; } - public void SetUnmarshalledCollectionLength(int length); diff --git a/docs/design/libraries/LibraryImportGenerator/StructMarshalling.md b/docs/design/libraries/LibraryImportGenerator/StructMarshalling.md index adb6f6b775f61..e7f394aebfe96 100644 --- a/docs/design/libraries/LibraryImportGenerator/StructMarshalling.md +++ b/docs/design/libraries/LibraryImportGenerator/StructMarshalling.md @@ -51,7 +51,6 @@ public sealed class CustomTypeMarshallerAttribute : Attribute public Type ManagedType { get; } public CustomTypeMarshallerKind MarshallerKind { get; } public int BufferSize { get; set; } - public bool RequiresStackBuffer { get; set; } } public enum CustomTypeMarshallerKind @@ -85,16 +84,16 @@ If a `Value` property is provided, the developer may also provide a ref-returnin A `ref` or `ref readonly` typed `Value` property is unsupported. If a ref-return is required, the type author can supply a `GetPinnableReference` method on the native type to pin the desired `ref` to return and then use `System.Runtime.CompilerServices.Unsafe.AsPointer` to get a pointer from the `ref` that will have already been pinned by the time the `Value` getter is called. ```csharp -[NativeMarshalling(typeof(TMarshaler))] +[NativeMarshalling(typeof(TMarshaller))] public struct TManaged { // ... } [CustomTypeMarshaller(typeof(TManaged))] -public struct TMarshaler +public struct TMarshaller { - public TMarshaler(TManaged managed) {} + public TMarshaller(TManaged managed) {} public TManaged ToManaged() {} public void FreeNative() {} @@ -117,14 +116,14 @@ Since C# 7.3 added a feature to enable custom pinning logic for user types, we s Custom marshalers of collection-like types or custom string encodings (such as UTF-32) may want to use stack space for extra storage for additional performance when possible. If the `TNative` type provides additional constructor with the following signature and sets the `BufferSize` field on the `CustomTypeMarshallerAttribute`, then it will opt in to using a caller-allocated buffer: ```csharp -[CustomTypeMarshaller(typeof(TManaged), BufferSize = /* */, RequiresStackBuffer = /* */)] +[CustomTypeMarshaller(typeof(TManaged), BufferSize = /* */)] partial struct TNative { public TNative(TManaged managed, Span buffer) {} } ``` -When these members are present, the source generator will call the two-parameter constructor with a possibly stack-allocated buffer of `BufferSize` bytes when a stack-allocated buffer is usable. If a stack-allocated buffer is a requirement, the `RequiresStackBuffer` field should be set to `true` and the `buffer` will be guaranteed to be allocated on the stack. Setting the `RequiresStackBuffer` field to `false` is the same as not specifying the value in the attribute. Since a dynamically allocated buffer is not usable in all scenarios, for example Reverse P/Invoke and struct marshalling, a one-parameter constructor must also be provided for usage in those scenarios. This may also be provided by providing a two-parameter constructor with a default value for the second parameter. +When these members are present, the source generator will call the two-parameter constructor with a possibly stack-allocated buffer of `BufferSize` bytes when a stack-allocated buffer is usable. Since a dynamically allocated buffer is not usable in all scenarios, for example Reverse P/Invoke and struct marshalling, a one-parameter constructor must also be provided for usage in those scenarios. Type authors can pass down the `buffer` pointer to native code by defining a `Value` property that returns a pointer to the first element, generally through code using `MemoryMarshal.GetReference()` and `Unsafe.AsPointer`. If `RequiresStackBuffer` is not provided or set to `false`, the `buffer` span must be pinned to be used safely. The `buffer` span can be pinned by defining a `GetPinnableReference()` method on the native type that returns a reference to the first element of the span. diff --git a/docs/project/list-of-diagnostics.md b/docs/project/list-of-diagnostics.md index 4e2b859114d98..f3644ab022375 100644 --- a/docs/project/list-of-diagnostics.md +++ b/docs/project/list-of-diagnostics.md @@ -158,18 +158,21 @@ The diagnostic id values reserved for .NET Libraries analyzer warnings are `SYSL | __`SYSLIB1052`__ | Specified configuration is not supported by source-generated P/Invokes | | __`SYSLIB1053`__ | Current target framework is not supported by source-generated P/Invokes | | __`SYSLIB1054`__ | Specified LibraryImportAttribute arguments cannot be forwarded to DllImportAttribute | -| __`SYSLIB1055`__ | *_`SYSLIB1055`-`SYSLIB1069` reserved for Microsoft.Interop.LibraryImportGenerator._* | -| __`SYSLIB1056`__ | *_`SYSLIB1055`-`SYSLIB1069` reserved for Microsoft.Interop.LibraryImportGenerator._* | -| __`SYSLIB1057`__ | *_`SYSLIB1055`-`SYSLIB1069` reserved for Microsoft.Interop.LibraryImportGenerator._* | -| __`SYSLIB1058`__ | *_`SYSLIB1055`-`SYSLIB1069` reserved for Microsoft.Interop.LibraryImportGenerator._* | -| __`SYSLIB1059`__ | *_`SYSLIB1055`-`SYSLIB1069` reserved for Microsoft.Interop.LibraryImportGenerator._* | -| __`SYSLIB1060`__ | *_`SYSLIB1055`-`SYSLIB1069` reserved for Microsoft.Interop.LibraryImportGenerator._* | -| __`SYSLIB1061`__ | *_`SYSLIB1055`-`SYSLIB1069` reserved for Microsoft.Interop.LibraryImportGenerator._* | -| __`SYSLIB1062`__ | *_`SYSLIB1055`-`SYSLIB1069` reserved for Microsoft.Interop.LibraryImportGenerator._* | -| __`SYSLIB1063`__ | *_`SYSLIB1055`-`SYSLIB1069` reserved for Microsoft.Interop.LibraryImportGenerator._* | -| __`SYSLIB1064`__ | *_`SYSLIB1055`-`SYSLIB1069` reserved for Microsoft.Interop.LibraryImportGenerator._* | -| __`SYSLIB1065`__ | *_`SYSLIB1055`-`SYSLIB1069` reserved for Microsoft.Interop.LibraryImportGenerator._* | -| __`SYSLIB1066`__ | *_`SYSLIB1055`-`SYSLIB1069` reserved for Microsoft.Interop.LibraryImportGenerator._* | -| __`SYSLIB1067`__ | *_`SYSLIB1055`-`SYSLIB1069` reserved for Microsoft.Interop.LibraryImportGenerator._* | -| __`SYSLIB1068`__ | *_`SYSLIB1055`-`SYSLIB1069` reserved for Microsoft.Interop.LibraryImportGenerator._* | -| __`SYSLIB1069`__ | *_`SYSLIB1055`-`SYSLIB1069` reserved for Microsoft.Interop.LibraryImportGenerator._* | +| __`SYSLIB1055`__ | *_`SYSLIB1055`-`SYSLIB1072` reserved for Microsoft.Interop.LibraryImportGenerator._* | +| __`SYSLIB1056`__ | *_`SYSLIB1055`-`SYSLIB1072` reserved for Microsoft.Interop.LibraryImportGenerator._* | +| __`SYSLIB1057`__ | *_`SYSLIB1055`-`SYSLIB1072` reserved for Microsoft.Interop.LibraryImportGenerator._* | +| __`SYSLIB1058`__ | *_`SYSLIB1055`-`SYSLIB1072` reserved for Microsoft.Interop.LibraryImportGenerator._* | +| __`SYSLIB1059`__ | *_`SYSLIB1055`-`SYSLIB1072` reserved for Microsoft.Interop.LibraryImportGenerator._* | +| __`SYSLIB1060`__ | *_`SYSLIB1055`-`SYSLIB1072` reserved for Microsoft.Interop.LibraryImportGenerator._* | +| __`SYSLIB1061`__ | *_`SYSLIB1055`-`SYSLIB1072` reserved for Microsoft.Interop.LibraryImportGenerator._* | +| __`SYSLIB1062`__ | *_`SYSLIB1055`-`SYSLIB1072` reserved for Microsoft.Interop.LibraryImportGenerator._* | +| __`SYSLIB1063`__ | *_`SYSLIB1055`-`SYSLIB1072` reserved for Microsoft.Interop.LibraryImportGenerator._* | +| __`SYSLIB1064`__ | *_`SYSLIB1055`-`SYSLIB1072` reserved for Microsoft.Interop.LibraryImportGenerator._* | +| __`SYSLIB1065`__ | *_`SYSLIB1055`-`SYSLIB1072` reserved for Microsoft.Interop.LibraryImportGenerator._* | +| __`SYSLIB1066`__ | *_`SYSLIB1055`-`SYSLIB1072` reserved for Microsoft.Interop.LibraryImportGenerator._* | +| __`SYSLIB1067`__ | *_`SYSLIB1055`-`SYSLIB1072` reserved for Microsoft.Interop.LibraryImportGenerator._* | +| __`SYSLIB1068`__ | *_`SYSLIB1055`-`SYSLIB1072` reserved for Microsoft.Interop.LibraryImportGenerator._* | +| __`SYSLIB1069`__ | *_`SYSLIB1055`-`SYSLIB1072` reserved for Microsoft.Interop.LibraryImportGenerator._* | +| __`SYSLIB1070`__ | *_`SYSLIB1055`-`SYSLIB1072` reserved for Microsoft.Interop.LibraryImportGenerator._* | +| __`SYSLIB1071`__ | *_`SYSLIB1055`-`SYSLIB1072` reserved for Microsoft.Interop.LibraryImportGenerator._* | +| __`SYSLIB1072`__ | *_`SYSLIB1055`-`SYSLIB1072` reserved for Microsoft.Interop.LibraryImportGenerator._* | diff --git a/src/libraries/Common/src/System/Runtime/InteropServices/CustomTypeMarshallerDirection.cs b/src/libraries/Common/src/System/Runtime/InteropServices/CustomTypeMarshallerDirection.cs index 6b31f7afb95ff..056b0acf0e6b6 100644 --- a/src/libraries/Common/src/System/Runtime/InteropServices/CustomTypeMarshallerDirection.cs +++ b/src/libraries/Common/src/System/Runtime/InteropServices/CustomTypeMarshallerDirection.cs @@ -1,17 +1,18 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#nullable enable - #if !TEST_CORELIB using System.ComponentModel; #endif -// -// Types in this file are used for generated p/invokes (docs/design/features/source-generator-pinvokes.md). -// namespace System.Runtime.InteropServices { + /// + /// A direction of marshalling data into or out of the managed environment + /// + /// + /// + /// [Flags] #if LIBRARYIMPORT_GENERATOR_TEST public @@ -20,12 +21,24 @@ namespace System.Runtime.InteropServices #endif enum CustomTypeMarshallerDirection { + /// + /// No marshalling direction + /// #if !TEST_CORELIB [EditorBrowsable(EditorBrowsableState.Never)] #endif None = 0, + /// + /// Marshalling from a managed environment to an unmanaged environment + /// In = 0x1, + /// + /// Marshalling from an unmanaged environment to a managed environment + /// Out = 0x2, + /// + /// Marshalling to and from managed and unmanaged environments + /// Ref = In | Out, } } diff --git a/src/libraries/Common/src/System/Runtime/InteropServices/CustomTypeMarshallerFeatures.cs b/src/libraries/Common/src/System/Runtime/InteropServices/CustomTypeMarshallerFeatures.cs index 292f6337b163a..ea844fd97820e 100644 --- a/src/libraries/Common/src/System/Runtime/InteropServices/CustomTypeMarshallerFeatures.cs +++ b/src/libraries/Common/src/System/Runtime/InteropServices/CustomTypeMarshallerFeatures.cs @@ -1,13 +1,14 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#nullable enable - -// -// Types in this file are used for generated p/invokes (docs/design/features/source-generator-pinvokes.md). -// namespace System.Runtime.InteropServices { + /// + /// Optional features supported by custom type marshallers. + /// + /// + /// + /// [Flags] #if LIBRARYIMPORT_GENERATOR_TEST public @@ -16,9 +17,21 @@ namespace System.Runtime.InteropServices #endif enum CustomTypeMarshallerFeatures { + /// + /// No optional features supported + /// None = 0, + /// + /// The marshaller owns unmanaged resources that must be freed + /// UnmanagedResources = 0x1, + /// + /// The marshaller can use a caller-allocated buffer instead of allocating in some scenarios + /// CallerAllocatedBuffer = 0x2, + /// + /// The marshaller uses the two-stage marshalling design for its instead of the one-stage design. + /// TwoStageMarshalling = 0x4 } } diff --git a/src/libraries/Common/src/System/Runtime/InteropServices/CustomTypeMarshallerKind.cs b/src/libraries/Common/src/System/Runtime/InteropServices/CustomTypeMarshallerKind.cs index b1f26d283991e..390744269f034 100644 --- a/src/libraries/Common/src/System/Runtime/InteropServices/CustomTypeMarshallerKind.cs +++ b/src/libraries/Common/src/System/Runtime/InteropServices/CustomTypeMarshallerKind.cs @@ -1,13 +1,15 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#nullable enable - -// -// Types in this file are used for generated p/invokes (docs/design/features/source-generator-pinvokes.md). -// namespace System.Runtime.InteropServices { + /// + /// The shape of a custom type marshaller for usage in source-generated interop scenarios. + /// + /// + /// + /// + /// #if LIBRARYIMPORT_GENERATOR_TEST public #else @@ -15,7 +17,13 @@ namespace System.Runtime.InteropServices #endif enum CustomTypeMarshallerKind { + /// + /// This custom type marshaller represents a single value. + /// Value, + /// + /// This custom type marshaller represents a container of values that are placed sequentially in memory. + /// LinearCollection } } diff --git a/src/libraries/Common/src/System/Runtime/InteropServices/GeneratedMarshallingAttribute.cs b/src/libraries/Common/src/System/Runtime/InteropServices/GeneratedMarshallingAttribute.cs index b7fc0ff8dc4d9..6815c7eaf84fb 100644 --- a/src/libraries/Common/src/System/Runtime/InteropServices/GeneratedMarshallingAttribute.cs +++ b/src/libraries/Common/src/System/Runtime/InteropServices/GeneratedMarshallingAttribute.cs @@ -64,6 +64,14 @@ public MarshalUsingAttribute(Type nativeType) public const string ReturnsCountValue = "return-value"; } + /// + /// Attribute used to indicate that the type can be used to convert a value of the provided to a native representation. + /// + /// + /// This attribute is recognized by the runtime-provided source generators for source-generated interop scenarios. + /// It is not used by the interop marshalling system at runtime. + /// + /// [AttributeUsage(AttributeTargets.Struct)] #if LIBRARYIMPORT_GENERATOR_TEST public @@ -78,10 +86,30 @@ public CustomTypeMarshallerAttribute(Type managedType, CustomTypeMarshallerKind MarshallerKind = marshallerKind; } + /// + /// The managed type for which the attributed type is a marshaller + /// public Type ManagedType { get; } + + /// + /// The required shape of the attributed type + /// public CustomTypeMarshallerKind MarshallerKind { get; } + + /// + /// When the flag is set on the size of the caller-allocated buffer in number of elements. + /// public int BufferSize { get; set; } + + /// + /// The marshalling directions this custom type marshaller supports. + /// + /// Default is public CustomTypeMarshallerDirection Direction { get; set; } = CustomTypeMarshallerDirection.Ref; + + /// + /// The optional features for the that the marshaller supports. + /// public CustomTypeMarshallerFeatures Features { get; set; } /// From bd32b160d9c3eaae1421c4f25fd6bbf2005260ca Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 17 Mar 2022 15:42:43 -0700 Subject: [PATCH 21/24] Fix docs. --- .../Runtime/InteropServices/CustomTypeMarshallerDirection.cs | 3 --- .../Runtime/InteropServices/CustomTypeMarshallerFeatures.cs | 3 --- .../System/Runtime/InteropServices/CustomTypeMarshallerKind.cs | 1 - 3 files changed, 7 deletions(-) diff --git a/src/libraries/Common/src/System/Runtime/InteropServices/CustomTypeMarshallerDirection.cs b/src/libraries/Common/src/System/Runtime/InteropServices/CustomTypeMarshallerDirection.cs index 056b0acf0e6b6..0dde44098e4a9 100644 --- a/src/libraries/Common/src/System/Runtime/InteropServices/CustomTypeMarshallerDirection.cs +++ b/src/libraries/Common/src/System/Runtime/InteropServices/CustomTypeMarshallerDirection.cs @@ -10,9 +10,6 @@ namespace System.Runtime.InteropServices /// /// A direction of marshalling data into or out of the managed environment /// - /// - /// - /// [Flags] #if LIBRARYIMPORT_GENERATOR_TEST public diff --git a/src/libraries/Common/src/System/Runtime/InteropServices/CustomTypeMarshallerFeatures.cs b/src/libraries/Common/src/System/Runtime/InteropServices/CustomTypeMarshallerFeatures.cs index ea844fd97820e..fe58f386f72f3 100644 --- a/src/libraries/Common/src/System/Runtime/InteropServices/CustomTypeMarshallerFeatures.cs +++ b/src/libraries/Common/src/System/Runtime/InteropServices/CustomTypeMarshallerFeatures.cs @@ -6,9 +6,6 @@ namespace System.Runtime.InteropServices /// /// Optional features supported by custom type marshallers. /// - /// - /// - /// [Flags] #if LIBRARYIMPORT_GENERATOR_TEST public diff --git a/src/libraries/Common/src/System/Runtime/InteropServices/CustomTypeMarshallerKind.cs b/src/libraries/Common/src/System/Runtime/InteropServices/CustomTypeMarshallerKind.cs index 390744269f034..06c73cdf7b608 100644 --- a/src/libraries/Common/src/System/Runtime/InteropServices/CustomTypeMarshallerKind.cs +++ b/src/libraries/Common/src/System/Runtime/InteropServices/CustomTypeMarshallerKind.cs @@ -7,7 +7,6 @@ namespace System.Runtime.InteropServices /// The shape of a custom type marshaller for usage in source-generated interop scenarios. /// /// - /// /// /// #if LIBRARYIMPORT_GENERATOR_TEST From 04e8cb1d0538cd7ec34b04c5f402e20ed9b76479 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 21 Mar 2022 14:11:38 -0700 Subject: [PATCH 22/24] PR feedback --- .../LibraryImportGenerator/SpanMarshallers.md | 88 +++---- .../StructMarshalling.md | 136 +++++++---- eng/generators.targets | 12 +- .../src/System/Runtime/InternalCalls.cs | 54 +++-- .../InteropServices/MarshalAsAttribute.cs | 26 -- .../Runtime/InteropServices/UnmanagedType.cs | 2 +- .../src/System/Runtime/RuntimeExports.cs | 5 +- .../src/System/Runtime/RuntimeImports.cs | 4 +- .../Test.CoreLib/src/Test.CoreLib.csproj | 15 +- .../CustomTypeMarshallerDirection.cs | 4 - .../GeneratedMarshallingAttribute.cs | 10 - .../Analyzers/AnalyzerDiagnostics.cs | 4 +- .../Analyzers/CustomTypeMarshallerAnalyzer.cs | 16 +- .../Resources.Designer.cs | 225 +++++++----------- .../gen/LibraryImportGenerator/Resources.resx | 68 +++--- .../ICustomNativeTypeMarshallingStrategy.cs | 4 +- .../Resources.Designer.cs | 8 +- .../CustomTypeMarshallerFixerTests.cs | 4 +- 18 files changed, 319 insertions(+), 366 deletions(-) delete mode 100644 src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InteropServices/MarshalAsAttribute.cs diff --git a/docs/design/libraries/LibraryImportGenerator/SpanMarshallers.md b/docs/design/libraries/LibraryImportGenerator/SpanMarshallers.md index 3bd01372d6ac3..b787c172e6c38 100644 --- a/docs/design/libraries/LibraryImportGenerator/SpanMarshallers.md +++ b/docs/design/libraries/LibraryImportGenerator/SpanMarshallers.md @@ -144,26 +144,32 @@ public struct GenericContiguousCollectionMarshallerImpl public GenericContiguousCollectionMarshallerImpl(GenericCollection collection, Span stackSpace, int nativeSizeOfElement); // optional /// - /// A span that points to the memory where the managed values of the collection are stored (in the marshalling case) or should be stored (in the unmarshalling case). + /// A span that points to the memory where the managed values of the collection are stored. /// - public Span ManagedValues { get; } - + public ReadOnlySpan GetManagedValuesSource(); /// - /// Set the expected length of the managed collection based on the parameter/return value/field marshalling information. - /// Required only when unmarshalling is supported. + /// A span that points to the memory where the unmarshalled managed values of the collection should be stored. /// - public void SetUnmarshalledCollectionLength(int length); - - public IntPtr Value { get; set; } + public Span GetManagedValuesDestination(int length); + /// + /// A span that points to the memory where the native values of the collection are stored after the native call. + /// + public ReadOnlySpan GetNativeValuesSource(int length); + /// + /// A span that points to the memory where the native values of the collection should be stored. + /// + public Span GetNativeValuesDestination(); /// /// A span that points to the memory where the native values of the collection should be stored. /// public unsafe Span NativeValueStorage { get; } - // The requirements on the Value property are the same as when used with `NativeTypeMarshallingAttribute`. + // The requirements on the TNative type are the same as when used with `NativeTypeMarshallingAttribute`. // The property is required with the generic collection marshalling. - public TNative Value { get; set; } + public TNative ToNativeValue(); + + public void FromNativeValue(TNative value); } ``` @@ -215,12 +221,14 @@ Alternatively, the `MarshalUsingAttribute` could provide a `Type ElementNativeTy This design could be used to provide a default marshaller for spans and arrays. Below is an example simple marshaller for `Span`. This design does not include all possible optimizations, such as stack allocation, for simpilicity of the example. ```csharp -[CustomTypeMarshaller(typeof(Span<>), CustomTypeMarshallerKind.LinearCollection)] +[CustomTypeMarshaller(typeof(Span<>), CustomTypeMarshallerKind.LinearCollection, Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.TwoStageMarshalling)] public ref struct SpanMarshaller { private Span managedCollection; private int nativeElementSize; + + private IntPtr Value { get; set; } public SpanMarshaller(Span collection, int nativeSizeOfElement) { @@ -230,19 +238,20 @@ public ref struct SpanMarshaller nativeElementSize = nativeSizeOfElement; } - public Span ManagedValues => managedCollection; + public ReadOnlySpan GetManagedValuesSource() => managedCollection; + + public Span GetManagedValuesDestination(int length) => managedCollection = new T[length]; - public void SetUnmarshalledCollectionLength(int length) - { - managedCollection = new T[value]; - } - - public IntPtr Value { get; set; } + public unsafe Span GetNativeValuesDestination() => MemoryMarshal.CreateSpan(ref *(byte*)(Value), managedCollection.Length); - public unsafe Span NativeValueStorage => MemoryMarshal.CreateSpan(ref *(byte*)(Value), Length); + public unsafe Span GetNativeValuesSource(int length) => MemoryMarshal.CreateSpan(ref *(byte*)(Value), length); public Span ToManaged() => managedCollection; + public IntPtr ToNativeValue() => Value; + + public void FromNativeValue(IntPtr value) => Value = value; + public void FreeNative() { if (Value != IntPtr.Zero) @@ -276,19 +285,20 @@ public static partial Span DuplicateValues([MarshalUsing(typeof(WrappedInt) public static partial unsafe Span DuplicateValues(Span values, int length) { SpanMarshaller __values_marshaller = new SpanMarshaller(values, sizeof(WrappedInt)); - for (int i = 0; i < __values_marshaller.ManagedValues.Length; ++i) { - WrappedInt native = new WrappedInt(__values_marshaller.ManagedValues[i]); - MemoryMarshal.Write(__values_marshaller.NativeValueStorage.Slice(sizeof(WrappedInt) * i), ref native); + ReadOnlySpan __values_managedSpan = __values_marshaller.GetManagedValuesSource(); + Span __values_nativeSpan = __values_marshaller.GetNativeValuesDestination(); + for (int i = 0; i < __values_managedSpan.Length; ++i) + { + WrappedInt native = new WrappedInt(__values_managedSpan[i]); + MemoryMarshal.Write(__values_nativeSpan.Slice(sizeof(WrappedInt) * i), ref native); + } } - IntPtr __retVal_native = __PInvoke__(__values_marshaller.Value, length); - SpanMarshaller __retVal_marshaller = new - { - Value = __retVal_native - }; - __retVal_marshaller.SetUnmarshalledCollectionLength(length); - MemoryMarshal.Cast(__retVal_marshaller.NativeValueStorage).CopyTo(__retVal_marshaller.ManagedValues); + IntPtr __retVal_native = __PInvoke__(__values_marshaller.ToNativeValue(), length); + SpanMarshaller __retVal_marshaller = new(); + __retVal_marshaller.FromNativeValue(__retVal_native); + MemoryMarshal.Cast(__retVal_marshaller.GetNativeValuesSource(length)).CopyTo(__retVal_marshaller.GetManagedValuesDestination(length)); return __retVal_marshaller.ToManaged(); [DllImport("Native", EntryPoint="DuplicateValues")] @@ -313,17 +323,13 @@ public struct GenericContiguousCollectionMarshallerImpl public GenericContiguousCollectionMarshallerImpl(GenericCollection collection, int nativeSizeOfElements); public GenericContiguousCollectionMarshallerImpl(GenericCollection collection, Span stackSpace, int nativeSizeOfElements); // optional -- public Span ManagedValues { get; } + public TNative ToNativeValue(); + public void FromNativeValue(TNative value); -- public void SetUnmarshalledCollectionLength(int length); - - public IntPtr Value { get; set; } - -- public unsafe Span NativeValueStorage { get; } - - // The requirements on the Value property are the same as when used with `NativeTypeMarshallingAttribute`. - // The property is required with the generic collection marshalling. - public TNative Value { get; set; } +- public ReadOnlySpan GetManagedValuesSource(); +- public Span GetManagedValuesDestination(int length); +- public ReadOnlySpan GetNativeValuesSource(int length); +- public Span GetNativeValuesDestination(); + public ref byte GetOffsetForNativeValueAtIndex(int index); + public TCollectionElement GetManagedValueAtIndex(int index); @@ -332,9 +338,9 @@ public struct GenericContiguousCollectionMarshallerImpl } ``` -The `GetManagedValueAtIndex` method and `Count` getter are used in the process of marshalling from managed to native. The generated code will iterate through `Count` elements (retrieved through `GetManagedValueAtIndex`) and assign their marshalled result to the address represented by `GetOffsetForNativeValueAtIndex` called with the same index. Then either the `Value` property getter will be called or the marshaller's `GetPinnableReference` method will be called, depending on if pinning is supported in the current scenario. +The `GetManagedValueAtIndex` method and `Count` getter are used in the process of marshalling from managed to native. The generated code will iterate through `Count` elements (retrieved through `GetManagedValueAtIndex`) and assign their marshalled result to the address represented by `GetOffsetForNativeValueAtIndex` called with the same index. Then the `ToNativeValue` method will be called to get the value to pass to native code. -The `SetManagedValueAtIndex` method and the `Count` setter are used in the process of marshalling from native to managed. The `Count` property will be set to the number of elements that the native collection contains, and the `Value` property will be assigned the result value from native code. Then the stub will iterate through the native collection `Count` times, calling `GetOffsetForNativeValueAtIndex` to get the offset of the native value and calling `SetManagedValueAtIndex` to set the unmarshalled managed value at that index. +The `SetManagedValueAtIndex` method and the `Count` setter are used in the process of marshalling from native to managed. The `Count` property will be set to the number of elements that the native collection contains, and the `FromNativeValue` property will be called with the result value from native code. Then the stub will iterate through the native collection `Count` times, calling `GetOffsetForNativeValueAtIndex` to get the offset of the native value and calling `SetManagedValueAtIndex` to set the unmarshalled managed value at that index. ### Pros/Cons of Design 2 diff --git a/docs/design/libraries/LibraryImportGenerator/StructMarshalling.md b/docs/design/libraries/LibraryImportGenerator/StructMarshalling.md index e7f394aebfe96..7028227fedd2e 100644 --- a/docs/design/libraries/LibraryImportGenerator/StructMarshalling.md +++ b/docs/design/libraries/LibraryImportGenerator/StructMarshalling.md @@ -51,81 +51,93 @@ public sealed class CustomTypeMarshallerAttribute : Attribute public Type ManagedType { get; } public CustomTypeMarshallerKind MarshallerKind { get; } public int BufferSize { get; set; } + public CustomTypeMarshallerDirection Direction { get; set; } = CustomTypeMarshallerDirection.Ref; + public CustomTypeMarshallerFeatures Features { get; set; } } public enum CustomTypeMarshallerKind { Value } + +[Flags] +public enum CustomTypeMarshallerFeatures +{ + None = 0, + /// + /// The marshaller owns unmanaged resources that must be freed + /// + UnmanagedResources = 0x1, + /// + /// The marshaller can use a caller-allocated buffer instead of allocating in some scenarios + /// + CallerAllocatedBuffer = 0x2, + /// + /// The marshaller uses the two-stage marshalling design for its instead of the one-stage design. + /// + TwoStageMarshalling = 0x4 +} +[Flags] +public enum CustomTypeMarshallerDirection +{ + /// + /// No marshalling direction + /// + [EditorBrowsable(EditorBrowsableState.Never)] + None = 0, + /// + /// Marshalling from a managed environment to an unmanaged environment + /// + In = 0x1, + /// + /// Marshalling from an unmanaged environment to a managed environment + /// + Out = 0x2, + /// + /// Marshalling to and from managed and unmanaged environments + /// + Ref = In | Out, +} ``` -The `NativeMarshallingAttribute` and `MarshalUsingAttribute` attributes would require that the provided native type `TNative` is a `struct` that does not require any marshalling and has the `CustomTypeMarshallerAttribute` with the first parameter being a `typeof()` of the managed type and a subset of three methods with the following names and shapes (with the managed type named TManaged): +The `NativeMarshallingAttribute` and `MarshalUsingAttribute` attributes would require that the provided native type `TNative` is a `struct` that does not require any marshalling and has the `CustomTypeMarshallerAttribute` with the first parameter being a `typeof()` of the managed type (with the managed type named TManaged in this example), an optional `CustomTypeMarshallerKind`, `CustomTypeMarshallerDirection`, and optional `CustomTypeMarshallerFeatures`: ```csharp -[CustomTypeMarshaller(typeof(TManaged))] +[CustomTypeMarshaller(typeof(TManaged), CustomTypeMarshallerKind.Value, Direction = CustomTypeMarshallerDirection.Ref, Features = CustomTypeMarshallerFeatures.None)] partial struct TNative { public TNative(TManaged managed) {} public TManaged ToManaged() {} - - public void FreeNative() {} } ``` -The analyzer will report an error if neither the constructor nor the `ToManaged` method is defined. When one of those two methods is missing, the direction of marshalling (managed to native/native to managed) that relies on the missing method is considered unsupported for the corresponding managed type. The `FreeNative` method is only required when there are resources that need to be released. +If the attribute specifies the `Direction` is either `In` or `Ref`, then the example constructor above must be provided. If the attribute specifies the `Direction` is `Out` or `Ref`, then the `ToManaged` method must be provided. If the `Direction` property is unspecified, then it will be treated as if the user provided `CustomTypeMarshallerDirection.Ref`. The analyzer will report an error if the attribute provides `CustomTypeMarshallerDirection.None`, as a marshaller that supports no direction is unusable. +If the attribute provides the `CustomTypeMarshallerFeatures.UnmanagedResources` flag to the `Features` property then a `void`-returning parameterless instance method name `FreeNative` must be provided. This method can be used to release any non-managed resources used during marshalling. > :question: Does this API surface and shape work for all marshalling scenarios we plan on supporting? It may have issues with the current "layout class" by-value `[Out]` parameter marshalling where the runtime updates a `class` typed object in place. We already recommend against using classes for interop for performance reasons and a struct value passed via `ref` or `out` with the same members would cover this scenario. -If the native type `TNative` also has a public `Value` property, then the value of the `Value` property will be passed to native code instead of the `TNative` value itself. As a result, the type `TNative` will be allowed to require marshalling and the type of the `Value` property will be required be passable to native code without any additional marshalling. When the `Value` property is provided, the `CustomTypeMarshallerAttribute` will still need to be provided on `TNative`. If the `Value` property is settable, then when marshalling in the native-to-managed direction, a default value of `TNative` will have its `Value` property set to the native value. If `Value` does not have a setter, then marshalling from native to managed is not supported. - -If a `Value` property is provided, the developer may also provide a ref-returning or readonly-ref-returning `GetPinnableReference` method. The `GetPinnableReference` method will be called before the `Value` property getter is called. The ref returned by `GetPinnableReference` will be pinned with a `fixed` statement, but the pinned value will not be used (it acts exclusively as a side-effect). - -A `ref` or `ref readonly` typed `Value` property is unsupported. If a ref-return is required, the type author can supply a `GetPinnableReference` method on the native type to pin the desired `ref` to return and then use `System.Runtime.CompilerServices.Unsafe.AsPointer` to get a pointer from the `ref` that will have already been pinned by the time the `Value` getter is called. - -```csharp -[NativeMarshalling(typeof(TMarshaller))] -public struct TManaged -{ - // ... -} - -[CustomTypeMarshaller(typeof(TManaged))] -public struct TMarshaller -{ - public TMarshaller(TManaged managed) {} - public TManaged ToManaged() {} - - public void FreeNative() {} - - public ref TNative GetPinnableReference() {} - - public TNative* Value { get; set; } -} - -``` - ### Performance features #### Pinning -Since C# 7.3 added a feature to enable custom pinning logic for user types, we should also add support for custom pinning logic. If the user provides a `GetPinnableReference` method on the managed type that matches the requirements to be used in a `fixed` statement and the pointed-to type would not require any additional marshalling, then we will support using pinning to marshal the managed value when possible. The analyzer should issue a warning when the pointed-to type would not match the final native type, accounting for the `Value` property on the native type. Since `MarshalUsingAttribute` is applied at usage time instead of at type authoring time, we will not enable the pinning feature since the implementation of `GetPinnableReference` is likely designed to match the default marshalling rules provided by the type author, not the rules provided by the marshaller provided by the `MarshalUsingAttribute`. +Since C# 7.3 added a feature to enable custom pinning logic for user types, we should also add support for custom pinning logic. If the user provides a `GetPinnableReference` method on the managed type that matches the requirements to be used in a `fixed` statement and the pointed-to type would not require any additional marshalling, then we will support using pinning to marshal the managed value when possible. The analyzer should issue a warning when the pointed-to type would not match the final native type, accounting for any optional features used by the custom marshaller type. Since `MarshalUsingAttribute` is applied at usage time instead of at type authoring time, we will not enable the pinning feature since the implementation of `GetPinnableReference` is likely designed to match the default marshalling rules provided by the type author, not the rules provided by the marshaller provided by the `MarshalUsingAttribute`. #### Caller-allocated memory -Custom marshalers of collection-like types or custom string encodings (such as UTF-32) may want to use stack space for extra storage for additional performance when possible. If the `TNative` type provides additional constructor with the following signature and sets the `BufferSize` field on the `CustomTypeMarshallerAttribute`, then it will opt in to using a caller-allocated buffer: +Custom marshalers of collection-like types or custom string encodings (such as UTF-32) may want to use stack space for extra storage for additional performance when possible. If the `[CustomTypeMarshaller]` attribute sets the `Features` property to value with the `CustomTypeMarshallerFeatures.CallerAllocatedBuffer` flag, then `TNative` type must provide additional constructor with the following signature and set the `BufferSize` field on the `CustomTypeMarshallerAttribute`. It will then be opted-in to using a caller-allocated buffer: ```csharp -[CustomTypeMarshaller(typeof(TManaged), BufferSize = /* */)] +[CustomTypeMarshaller(typeof(TManaged), BufferSize = /* */, Features = CustomTypeMarshallerFeatures.CallerAllocatedBuffer)] partial struct TNative { public TNative(TManaged managed, Span buffer) {} } ``` -When these members are present, the source generator will call the two-parameter constructor with a possibly stack-allocated buffer of `BufferSize` bytes when a stack-allocated buffer is usable. Since a dynamically allocated buffer is not usable in all scenarios, for example Reverse P/Invoke and struct marshalling, a one-parameter constructor must also be provided for usage in those scenarios. +When these `CallerAllocatedBuffer` feature flag is present, the source generator will call the two-parameter constructor with a possibly stack-allocated buffer of `BufferSize` bytes when a stack-allocated buffer is usable. Since a dynamically allocated buffer is not usable in all scenarios, for example Reverse P/Invoke and struct marshalling, a one-parameter constructor must also be provided for usage in those scenarios. -Type authors can pass down the `buffer` pointer to native code by defining a `Value` property that returns a pointer to the first element, generally through code using `MemoryMarshal.GetReference()` and `Unsafe.AsPointer`. If `RequiresStackBuffer` is not provided or set to `false`, the `buffer` span must be pinned to be used safely. The `buffer` span can be pinned by defining a `GetPinnableReference()` method on the native type that returns a reference to the first element of the span. +Type authors can pass down the `buffer` pointer to native code by using the `TwoStageMarshalling` feature to provide a `ToNativeValue` method that returns a pointer to the first element, generally through code using `MemoryMarshal.GetReference()` and `Unsafe.AsPointer`. The `buffer` span must be pinned to be used safely. The `buffer` span can be pinned by defining a `GetPinnableReference()` method on the native type that returns a reference to the first element of the span. ### Determining if a type is doesn't need marshalling @@ -256,7 +268,7 @@ All generated stubs will be marked with [`SkipLocalsInitAttribute`](https://docs ### Special case: Transparent Structures -There has been discussion about Transparent Structures, structure types that are treated as their underlying types when passed to native code. The support for a `Value` property on a generated marshalling type supports the transparent struct support. For example, we could support strongly typed `HRESULT` returns with this model as shown below: +There has been discussion about Transparent Structures, structure types that are treated as their underlying types when passed to native code. The source-generated model supports this design through the `TwoStageMarshalling` feature flag on the `CustomTypeMarshaller` attribute. ```csharp [NativeMarshalling(typeof(HRESULT))] @@ -269,29 +281,59 @@ struct HResult public readonly int Result; } -[CustomTypeMarshaller(typeof(HResult))] +[CustomTypeMarshaller(typeof(HResult), Features = CustomTypeMarshallerFeatures.TwoStageMarshalling)] struct HRESULT { + private HResult managed; public HRESULT(HResult hr) { - Value = hr; + managed = hr; } - public HResult ToManaged() => new HResult(Value); - public int Value { get; set; } + public HResult ToManaged() => managed; + public int ToNativeValue() => managed.Result; +} +``` + +For the more detailed specification, we will use the example below: + +```csharp +[NativeMarshalling(typeof(TMarshaller))] +public struct TManaged +{ + // ... +} + +[CustomTypeMarshaller(typeof(TManaged))] +public struct TMarshaller +{ + public TMarshaller(TManaged managed) {} + public TManaged ToManaged() {} + + public ref T GetPinnableReference() {} + + public unsafe TNative* ToNativeValue(); + public unsafe void FromNativeValue(TNative*); } + ``` In this case, the underlying native type would actually be an `int`, but the user could use the strongly-typed `HResult` type as the public surface area. -> :question: Should we support transparent structures on manually annotated types that wouldn't need marshalling otherwise? If we do, we should do so in an opt-in manner to make it possible to have a `Value` property on the type without assuming that it is for interop in all cases. +If a type `TMarshaller` with the `CustomTypeMarshaller` attribute specifies the `TwoStageMarshalling` feature, then it must provide the `ToNativeValue` feature if it supports the `In` direction, and the `FromNativeValue` method if it supports the `Out` direction. The return value of the `ToNativeValue` method will be passed to native code instead of the `TMarshaller` value itself. As a result, the type `TMarshaller` will be allowed to require marshalling and the return type of the `ToNativeValue` method, will be required be passable to native code without any additional marshalling. When marshalling in the native-to-managed direction, a default value of `TMarshaller` will have the `FromNativeValu` method called with the native value. If we are marshalling a scenario where a single frame covers the whole native call and we are marshalling in and out, then the same instance of the marshller will be reused. + +If the `TwoStageMarshalling` feature is specified, the developer may also provide a ref-returning or readonly-ref-returning `GetPinnableReference` method. The `GetPinnableReference` method will be called before the `ToNativeValue` method is called. The ref returned by `GetPinnableReference` will be pinned with a `fixed` statement, but the pinned value will not be used (it acts exclusively as a side-effect). As a result, `GetPinnableReference` can return a `ref` to any `T` that can be used in a fixed statement (a C# `unmanaged` type). + +A `ref` or `ref readonly` typed `ToNativeValue` method is unsupported. If a ref-return is required, the type author can supply a `GetPinnableReference` method on the native type to pin the desired `ref` to return and then use `System.Runtime.CompilerServices.Unsafe.AsPointer` to get a pointer from the `ref` that will have already been pinned by the time the `ToNativeValue` method is called. + +> :question: Should we support transparent structures on manually annotated types that wouldn't need marshalling otherwise? If we do, we should do so in an opt-in manner to make it possible to have a `ToNativeValue` method on the type without assuming that it is for interop in all cases. #### Example: ComWrappers marshalling with Transparent Structures Building on this Transparent Structures support, we can also support ComWrappers marshalling with this proposal via the manually-decorated types approach: ```csharp -[NativeMarshalling(typeof(ComWrappersMarshaler))] +[NativeMarshalling(typeof(ComWrappersMarshaler), Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.TwoStageMarshalling)] class Foo {} @@ -306,7 +348,9 @@ struct FooComWrappersMarshaler nativeObj = ComWrappers.GetOrCreateComInterfaceForObject(obj, CreateComInterfaceFlags.None); } - public IntPtr Value { get => nativeObj; set => nativeObj = value; } + public IntPtr ToNativeValue() => nativeObj; + + public void FromNativeValue(IntPtr value) => nativeObj = value; public Foo ToManaged() => (Foo)ComWrappers.GetOrCreateObjectForComInstance(nativeObj, CreateObjectFlags.None); diff --git a/eng/generators.targets b/eng/generators.targets index c1f16d39f305a..4b55dfbaafe2a 100644 --- a/eng/generators.targets +++ b/eng/generators.targets @@ -44,9 +44,6 @@ - - - - + + + + RhNewArray(pEEType.ToPointer(), length); - [LibraryImport(RuntimeLibrary)] - internal static unsafe partial void RhAllocateNewObject(IntPtr pEEType, uint flags, void* pResult); + [DllImport(RuntimeLibrary)] + internal static unsafe extern void RhAllocateNewObject(IntPtr pEEType, uint flags, void* pResult); [MethodImpl(MethodImplOptions.InternalCall)] [RuntimeImport(RuntimeLibrary, "RhpFallbackFailFast")] diff --git a/src/coreclr/nativeaot/Test.CoreLib/src/Test.CoreLib.csproj b/src/coreclr/nativeaot/Test.CoreLib/src/Test.CoreLib.csproj index 2ac88f28c51f9..9d53870e996a1 100644 --- a/src/coreclr/nativeaot/Test.CoreLib/src/Test.CoreLib.csproj +++ b/src/coreclr/nativeaot/Test.CoreLib/src/Test.CoreLib.csproj @@ -3,12 +3,6 @@ false false netstandard2.0 - true - $(DefineConstants);TEST_CORELIB - - $(NoWarn);SYSLIB1053 FEATURE_GC_STRESS;$(DefineConstants) @@ -34,6 +28,9 @@ $(ArtifactsObjDir)\coreclr\$(TargetOS).$(TargetArchitecture).$(CoreCLRConfiguration) $(IntermediatesDir)\ide + + $([MSBuild]::NormalizeDirectory('$(LibrariesProjectRoot)', 'Common', 'src')) + Runtime.Base\src\System\Runtime\CachedInterfaceDispatch.cs @@ -68,9 +65,6 @@ Runtime.Base\src\System\Runtime\TypeCast.cs - - Runtime.Base\src\System\Runtime\InteropServices\MarshalAsAttribute.cs - Runtime.Base\src\System\Runtime\InteropServices\UnsafeGCHandle.cs @@ -83,6 +77,9 @@ Common\TransitionBlock.cs + + Common\Interop\Windows\Interop.BOOL.cs + diff --git a/src/libraries/Common/src/System/Runtime/InteropServices/CustomTypeMarshallerDirection.cs b/src/libraries/Common/src/System/Runtime/InteropServices/CustomTypeMarshallerDirection.cs index 0dde44098e4a9..0ee347d72b96e 100644 --- a/src/libraries/Common/src/System/Runtime/InteropServices/CustomTypeMarshallerDirection.cs +++ b/src/libraries/Common/src/System/Runtime/InteropServices/CustomTypeMarshallerDirection.cs @@ -1,9 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#if !TEST_CORELIB using System.ComponentModel; -#endif namespace System.Runtime.InteropServices { @@ -21,9 +19,7 @@ enum CustomTypeMarshallerDirection /// /// No marshalling direction /// -#if !TEST_CORELIB [EditorBrowsable(EditorBrowsableState.Never)] -#endif None = 0, /// /// Marshalling from a managed environment to an unmanaged environment diff --git a/src/libraries/Common/src/System/Runtime/InteropServices/GeneratedMarshallingAttribute.cs b/src/libraries/Common/src/System/Runtime/InteropServices/GeneratedMarshallingAttribute.cs index 6815c7eaf84fb..4a0aaf99e23e2 100644 --- a/src/libraries/Common/src/System/Runtime/InteropServices/GeneratedMarshallingAttribute.cs +++ b/src/libraries/Common/src/System/Runtime/InteropServices/GeneratedMarshallingAttribute.cs @@ -8,16 +8,6 @@ // namespace System.Runtime.InteropServices { - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)] -#if LIBRARYIMPORT_GENERATOR_TEST - public -#else - internal -#endif - sealed class GeneratedMarshallingAttribute : Attribute - { - } - [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class | AttributeTargets.Enum | AttributeTargets.Delegate)] #if LIBRARYIMPORT_GENERATOR_TEST public diff --git a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/AnalyzerDiagnostics.cs b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/AnalyzerDiagnostics.cs index 82de7c31daf97..d09d2386dde52 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/AnalyzerDiagnostics.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/AnalyzerDiagnostics.cs @@ -26,10 +26,10 @@ public static class Ids public const string ProvidedMethodsNotSpecifiedInShape = Prefix + "009"; public const string GetPinnableReferenceShouldSupportAllocatingMarshallingFallback = Prefix + "010"; public const string CallerAllocMarshallingShouldSupportAllocatingMarshallingFallback = Prefix + "011"; - public const string CallerAllocConstructorMustHaveStackBufferSize = Prefix + "012"; + public const string CallerAllocConstructorMustHaveBufferSize = Prefix + "012"; public const string TwoStageMarshallingNativeTypesMustMatch = Prefix + "013"; public const string LinearCollectionElementTypesMustMatch = Prefix + "014"; - public const string RefValuePropertyUnsupported = Prefix + "015"; + public const string RefNativeValueUnsupported = Prefix + "015"; public const string NativeGenericTypeMustBeClosedOrMatchArity = Prefix + "016"; public const string MarshallerGetPinnableReferenceRequiresTwoStageMarshalling = Prefix + "017"; 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 7274d0574210c..01764475a3615 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/CustomTypeMarshallerAnalyzer.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/CustomTypeMarshallerAnalyzer.cs @@ -254,7 +254,7 @@ public static class MissingMemberNames public static readonly DiagnosticDescriptor CallerAllocConstructorMustHaveBufferSizeRule = new DiagnosticDescriptor( - Ids.CallerAllocConstructorMustHaveStackBufferSize, + Ids.CallerAllocConstructorMustHaveBufferSize, "CallerAllocConstructorMustHaveBufferSize", GetResourceString(nameof(Resources.CallerAllocConstructorMustHaveBufferSizeMessage)), Category, @@ -262,15 +262,15 @@ public static class MissingMemberNames isEnabledByDefault: true, description: GetResourceString(nameof(Resources.CallerAllocConstructorMustHaveBufferSizeDescription))); - public static readonly DiagnosticDescriptor RefValuePropertyUnsupportedRule = + public static readonly DiagnosticDescriptor RefNativeValueUnsupportedRule = new DiagnosticDescriptor( - Ids.RefValuePropertyUnsupported, - "RefValuePropertyUnsupported", - GetResourceString(nameof(Resources.RefValuePropertyUnsupportedMessage)), + Ids.RefNativeValueUnsupported, + "RefNativeValueUnsupported", + GetResourceString(nameof(Resources.RefNativeValueUnsupportedMessage)), Category, DiagnosticSeverity.Error, isEnabledByDefault: true, - description: GetResourceString(nameof(Resources.RefValuePropertyUnsupportedDescription))); + description: GetResourceString(nameof(Resources.RefNativeValueUnsupportedDescription))); public static readonly DiagnosticDescriptor NativeGenericTypeMustBeClosedOrMatchArityRule = new DiagnosticDescriptor( @@ -374,7 +374,7 @@ public static class MissingMemberNames GetPinnableReferenceShouldSupportAllocatingMarshallingFallbackRule, CallerAllocMarshallingShouldSupportAllocatingMarshallingFallbackRule, CallerAllocConstructorMustHaveBufferSizeRule, - RefValuePropertyUnsupportedRule, + RefNativeValueUnsupportedRule, NativeGenericTypeMustBeClosedOrMatchArityRule, MarshallerGetPinnableReferenceRequiresTwoStageMarshallingRule, FreeNativeMethodProvidedShouldSpecifyUnmanagedResourcesFeatureRule, @@ -819,7 +819,7 @@ public void AnalyzeMarshallerType(SymbolAnalysisContext context) { context.ReportDiagnostic( toNativeValueMethod.CreateDiagnostic( - RefValuePropertyUnsupportedRule, + RefNativeValueUnsupportedRule, marshallerType.ToDisplayString())); } diff --git a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Resources.Designer.cs b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Resources.Designer.cs index 7b87fc588b57d..5e4628b58dde0 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Resources.Designer.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Resources.Designer.cs @@ -10,8 +10,8 @@ namespace Microsoft.Interop { using System; - - + + /// /// A strongly-typed resource class, for looking up localized strings, etc. /// @@ -23,15 +23,15 @@ namespace Microsoft.Interop { [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resources { - + private static global::System.Resources.ResourceManager resourceMan; - + private static global::System.Globalization.CultureInfo resourceCulture; - + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal Resources() { } - + /// /// Returns the cached ResourceManager instance used by this class. /// @@ -45,7 +45,7 @@ internal Resources() { return resourceMan; } } - + /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. @@ -59,7 +59,7 @@ internal Resources() { resourceCulture = value; } } - + /// /// Looks up a localized string similar to Add missing custom type marshaller members. /// @@ -68,7 +68,7 @@ internal static string AddMissingCustomTypeMarshallerMembers { return ResourceManager.GetString("AddMissingCustomTypeMarshallerMembers", resourceCulture); } } - + /// /// Looks up a localized string similar to Add missing features to the 'CustomTypeMarshallerAttribute' attribute. /// @@ -77,7 +77,7 @@ internal static string AddMissingFeaturesToCustomTypeMarshaller { return ResourceManager.GetString("AddMissingFeaturesToCustomTypeMarshaller", resourceCulture); } } - + /// /// Looks up a localized string similar to A marshaller type that provides a constructor taking a caller-allocated 'Span<byte>' should specify that it supports the 'CallerAllocatedBuffer' feature.. /// @@ -86,7 +86,7 @@ internal static string CallerAllocatedBufferConstructorProvidedShouldSpecifyFeat return ResourceManager.GetString("CallerAllocatedBufferConstructorProvidedShouldSpecifyFeatureDescription", resourceCulture); } } - + /// /// Looks up a localized string similar to The type '{0}' provides a constructor taking a caller-allocated 'Span<byte>' but does not specify that it supports the 'CallerAllocatedBuffer' feature. The constructor will not be used unless the feature is specified.. /// @@ -97,7 +97,7 @@ internal static string CallerAllocatedBufferConstructorProvidedShouldSpecifyFeat } /// - /// Looks up a localized string similar to When a constructor taking a ''Span<byte> is specified on the native type, the type must set the BufferSize field on the 'System.Runtime.InteropServices.CustomTypeMarshallerAttribute' to provide the size of the caller-allocated buffer.. + /// Looks up a localized string similar to When a constructor taking a 'Span<byte>' is specified on the native type, the type must set the BufferSize field on the 'System.Runtime.InteropServices.CustomTypeMarshallerAttribute' to provide the size of the caller-allocated buffer.. /// internal static string CallerAllocConstructorMustHaveBufferSizeDescription { get { @@ -113,7 +113,7 @@ internal static string CallerAllocConstructorMustHaveBufferSizeMessage { return ResourceManager.GetString("CallerAllocConstructorMustHaveBufferSizeMessage", resourceCulture); } } - + /// /// Looks up a localized string similar to A type that supports marshalling from managed to native using a caller-allocated buffer should also support marshalling from managed to native where using a caller-allocated buffer is impossible.. /// @@ -122,7 +122,7 @@ internal static string CallerAllocMarshallingShouldSupportAllocatingMarshallingF return ResourceManager.GetString("CallerAllocMarshallingShouldSupportAllocatingMarshallingFallbackDescription", resourceCulture); } } - + /// /// Looks up a localized string similar to Native type '{0}' has a constructor taking a caller-allocated buffer, but does not support marshalling in scenarios where using a caller-allocated buffer is impossible. /// @@ -131,7 +131,7 @@ internal static string CallerAllocMarshallingShouldSupportAllocatingMarshallingF return ResourceManager.GetString("CallerAllocMarshallingShouldSupportAllocatingMarshallingFallbackMessage", resourceCulture); } } - + /// /// Looks up a localized string similar to The generated 'DllImportAttribute' will not have a value corresponding to '{0}'.. /// @@ -140,7 +140,7 @@ internal static string CannotForwardToDllImportDescription { return ResourceManager.GetString("CannotForwardToDllImportDescription", resourceCulture); } } - + /// /// Looks up a localized string similar to '{0}' has no equivalent in 'DllImportAtttribute' and will not be forwarded. /// @@ -149,7 +149,7 @@ internal static string CannotForwardToDllImportMessage { return ResourceManager.GetString("CannotForwardToDllImportMessage", resourceCulture); } } - + /// /// Looks up a localized string similar to Specified 'LibraryImportAttribute' arguments cannot be forwarded to 'DllImportAttribute'. /// @@ -158,43 +158,7 @@ internal static string CannotForwardToDllImportTitle { return ResourceManager.GetString("CannotForwardToDllImportTitle", resourceCulture); } } - - /// - /// Looks up a localized string similar to The 'BlittableTypeAttribute' and 'NativeMarshallingAttribute' attributes are mutually exclusive.. - /// - internal static string CannotHaveMultipleMarshallingAttributesDescription { - get { - return ResourceManager.GetString("CannotHaveMultipleMarshallingAttributesDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Type '{0}' is marked with 'BlittableTypeAttribute' and 'NativeMarshallingAttribute'. A type can only have one of these two attributes.. - /// - internal static string CannotHaveMultipleMarshallingAttributesMessage { - get { - return ResourceManager.GetString("CannotHaveMultipleMarshallingAttributesMessage", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to A native type with the 'GenericContiguousCollectionMarshallerAttribute' must have at least one of the two marshalling methods as well as a 'ManagedValues' property of type 'Span<T>' for some 'T' and a 'NativeValueStorage' property of type 'Span<byte>' to enable marshalling the managed type.. - /// - internal static string CollectionNativeTypeMustHaveRequiredShapeDescription { - get { - return ResourceManager.GetString("CollectionNativeTypeMustHaveRequiredShapeDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The native type '{0}' must be a value type and have a constructor that takes two parameters, one of type '{1}' and an 'int', or have a parameterless instance method named 'ToManaged' that returns '{1}' as well as a 'ManagedValues' property of type 'Span<T>' for some 'T' and a 'NativeValueStorage' property of type 'Span<byte>'. - /// - internal static string CollectionNativeTypeMustHaveRequiredShapeMessage { - get { - return ResourceManager.GetString("CollectionNativeTypeMustHaveRequiredShapeMessage", resourceCulture); - } - } - + /// /// Looks up a localized string similar to Source-generated P/Invokes will ignore any configuration that is not supported.. /// @@ -203,7 +167,7 @@ internal static string ConfigurationNotSupportedDescription { return ResourceManager.GetString("ConfigurationNotSupportedDescription", resourceCulture); } } - + /// /// Looks up a localized string similar to The '{0}' configuration is not supported by source-generated P/Invokes. If the specified configuration is required, use a regular `DllImport` instead.. /// @@ -212,7 +176,7 @@ internal static string ConfigurationNotSupportedMessage { return ResourceManager.GetString("ConfigurationNotSupportedMessage", resourceCulture); } } - + /// /// Looks up a localized string similar to The specified marshalling configuration is not supported by source-generated P/Invokes. {0}.. /// @@ -221,7 +185,7 @@ internal static string ConfigurationNotSupportedMessageMarshallingInfo { return ResourceManager.GetString("ConfigurationNotSupportedMessageMarshallingInfo", resourceCulture); } } - + /// /// Looks up a localized string similar to The specified '{0}' configuration for parameter '{1}' is not supported by source-generated P/Invokes. If the specified configuration is required, use a regular `DllImport` instead.. /// @@ -230,7 +194,7 @@ internal static string ConfigurationNotSupportedMessageParameter { return ResourceManager.GetString("ConfigurationNotSupportedMessageParameter", resourceCulture); } } - + /// /// Looks up a localized string similar to The specified '{0}' configuration for the return value of method '{1}' is not supported by source-generated P/Invokes. If the specified configuration is required, use a regular `DllImport` instead.. /// @@ -239,7 +203,7 @@ internal static string ConfigurationNotSupportedMessageReturn { return ResourceManager.GetString("ConfigurationNotSupportedMessageReturn", resourceCulture); } } - + /// /// Looks up a localized string similar to The specified value '{0}' for '{1}' is not supported by source-generated P/Invokes. If the specified configuration is required, use a regular `DllImport` instead.. /// @@ -248,7 +212,7 @@ internal static string ConfigurationNotSupportedMessageValue { return ResourceManager.GetString("ConfigurationNotSupportedMessageValue", resourceCulture); } } - + /// /// Looks up a localized string similar to Specified configuration is not supported by source-generated P/Invokes.. /// @@ -257,7 +221,7 @@ internal static string ConfigurationNotSupportedTitle { return ResourceManager.GetString("ConfigurationNotSupportedTitle", resourceCulture); } } - + /// /// Looks up a localized string similar to Only one of 'ConstantElementCount' or 'ElementCountInfo' may be used in a 'MarshalUsingAttribute' for a given 'ElementIndirectionDepth'. /// @@ -266,7 +230,7 @@ internal static string ConstantAndElementCountInfoDisallowed { return ResourceManager.GetString("ConstantAndElementCountInfoDisallowed", resourceCulture); } } - + /// /// Looks up a localized string similar to Automatically converting a P/Invoke with 'PreserveSig' set to 'false' to a source-generated P/Invoke may produce invalid code. /// @@ -275,7 +239,7 @@ internal static string ConvertNoPreserveSigDllImportToGeneratedMayProduceInvalid return ResourceManager.GetString("ConvertNoPreserveSigDllImportToGeneratedMayProduceInvalidCode", resourceCulture); } } - + /// /// Looks up a localized string similar to Convert to 'LibraryImport'. /// @@ -284,7 +248,7 @@ internal static string ConvertToLibraryImport { return ResourceManager.GetString("ConvertToLibraryImport", resourceCulture); } } - + /// /// Looks up a localized string similar to Use 'LibraryImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time. /// @@ -293,7 +257,7 @@ internal static string ConvertToLibraryImportDescription { return ResourceManager.GetString("ConvertToLibraryImportDescription", resourceCulture); } } - + /// /// Looks up a localized string similar to Mark the method '{0}' with 'LibraryImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time. /// @@ -302,7 +266,7 @@ internal static string ConvertToLibraryImportMessage { return ResourceManager.GetString("ConvertToLibraryImportMessage", resourceCulture); } } - + /// /// Looks up a localized string similar to Use 'LibraryImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time. /// @@ -311,7 +275,7 @@ internal static string ConvertToLibraryImportTitle { return ResourceManager.GetString("ConvertToLibraryImportTitle", resourceCulture); } } - + /// /// Looks up a localized string similar to Conversion to 'LibraryImport' may change behavior and compatibility. See {0} for more information.. /// @@ -320,7 +284,7 @@ internal static string ConvertToLibraryImportWarning { return ResourceManager.GetString("ConvertToLibraryImportWarning", resourceCulture); } } - + /// /// Looks up a localized string similar to Convert to 'LibraryImport' with '{0}' suffix. /// @@ -329,7 +293,7 @@ internal static string ConvertToLibraryImportWithSuffix { return ResourceManager.GetString("ConvertToLibraryImportWithSuffix", resourceCulture); } } - + /// /// Looks up a localized string similar to A native must set the 'Direction' property on the 'CustomTypeMarshallerAttribute' to a value that sets at least one known flag value on the 'CustomTypeMarshallerDirection' enum. /// @@ -374,7 +338,7 @@ internal static string CustomTypeMarshallingManagedToNativeUnsupported { return ResourceManager.GetString("CustomTypeMarshallingManagedToNativeUnsupported", resourceCulture); } } - + /// /// Looks up a localized string similar to The specified parameter needs to be marshalled from native to managed, but the native type '{0}' does not support it.. /// @@ -383,7 +347,7 @@ internal static string CustomTypeMarshallingNativeToManagedUnsupported { return ResourceManager.GetString("CustomTypeMarshallingNativeToManagedUnsupported", resourceCulture); } } - + /// /// Looks up a localized string similar to A marshaller type that provides a 'FreeNative' method should specify that it supports the 'UnmanagedResources' feature.. /// @@ -428,7 +392,7 @@ internal static string GetPinnableReferenceReturnTypeBlittableDescription { return ResourceManager.GetString("GetPinnableReferenceReturnTypeBlittableDescription", resourceCulture); } } - + /// /// Looks up a localized string similar to The dereferenced type of the return type of the 'GetPinnableReference' method must be blittable. /// @@ -437,7 +401,7 @@ internal static string GetPinnableReferenceReturnTypeBlittableMessage { return ResourceManager.GetString("GetPinnableReferenceReturnTypeBlittableMessage", resourceCulture); } } - + /// /// Looks up a localized string similar to A type that supports marshalling from managed to native by pinning should also support marshalling from managed to native where pinning is impossible.. /// @@ -446,7 +410,7 @@ internal static string GetPinnableReferenceShouldSupportAllocatingMarshallingFal return ResourceManager.GetString("GetPinnableReferenceShouldSupportAllocatingMarshallingFallbackDescription", resourceCulture); } } - + /// /// Looks up a localized string similar to Type '{0}' has a 'GetPinnableReference' method but its native type does not support marshalling in scenarios where pinning is impossible. /// @@ -455,9 +419,9 @@ internal static string GetPinnableReferenceShouldSupportAllocatingMarshallingFal return ResourceManager.GetString("GetPinnableReferenceShouldSupportAllocatingMarshallingFallbackMessage", resourceCulture); } } - + /// - /// Looks up a localized string similar to The 'TwoStageMarshalling' feature requires a TNativeType FromNativeValue()' method for the 'In' direction.. + /// Looks up a localized string similar to The 'TwoStageMarshalling' feature requires a 'TNativeType ToNativeValue()' method for the 'In' direction.. /// internal static string InTwoStageMarshallingRequiresToNativeValueDescription { get { @@ -482,7 +446,7 @@ internal static string InvalidAttributedMethodContainingTypeMissingModifiersMess return ResourceManager.GetString("InvalidAttributedMethodContainingTypeMissingModifiersMessage", resourceCulture); } } - + /// /// Looks up a localized string similar to Methods marked with 'LibraryImportAttribute' should be 'static', 'partial', and non-generic. P/Invoke source generation will ignore methods that are non-'static', non-'partial', or generic.. /// @@ -491,7 +455,7 @@ internal static string InvalidAttributedMethodDescription { return ResourceManager.GetString("InvalidAttributedMethodDescription", resourceCulture); } } - + /// /// Looks up a localized string similar to Method '{0}' should be 'static', 'partial', and non-generic when marked with 'LibraryImportAttribute'. P/Invoke source generation will ignore method '{0}'.. /// @@ -500,7 +464,7 @@ internal static string InvalidAttributedMethodSignatureMessage { return ResourceManager.GetString("InvalidAttributedMethodSignatureMessage", resourceCulture); } } - + /// /// Looks up a localized string similar to Invalid 'LibraryImportAttribute' usage. /// @@ -509,7 +473,7 @@ internal static string InvalidLibraryImportAttributeUsageTitle { return ResourceManager.GetString("InvalidLibraryImportAttributeUsageTitle", resourceCulture); } } - + /// /// Looks up a localized string similar to The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' is invalid.. /// @@ -518,7 +482,7 @@ internal static string InvalidStringMarshallingConfigurationDescription { return ResourceManager.GetString("InvalidStringMarshallingConfigurationDescription", resourceCulture); } } - + /// /// Looks up a localized string similar to The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' on method '{0}' is invalid. {1}. /// @@ -527,7 +491,7 @@ internal static string InvalidStringMarshallingConfigurationMessage { return ResourceManager.GetString("InvalidStringMarshallingConfigurationMessage", resourceCulture); } } - + /// /// Looks up a localized string similar to 'StringMarshallingCustomType' must be specified when 'StringMarshalling' is set to 'StringMarshalling.Custom'.. /// @@ -536,7 +500,7 @@ internal static string InvalidStringMarshallingConfigurationMissingCustomType { return ResourceManager.GetString("InvalidStringMarshallingConfigurationMissingCustomType", resourceCulture); } } - + /// /// Looks up a localized string similar to 'StringMarshalling' should be set to 'StringMarshalling.Custom' when 'StringMarshallingCustomType' is specified.. /// @@ -545,7 +509,7 @@ internal static string InvalidStringMarshallingConfigurationNotCustom { return ResourceManager.GetString("InvalidStringMarshallingConfigurationNotCustom", resourceCulture); } } - + /// /// Looks up a localized string similar to The element type of the 'ReadOnlySpan' returned by 'GetManagedValuesSource' must be the same as the element type returned by 'GetManagedValuesDestination'.. /// @@ -554,7 +518,7 @@ internal static string LinearCollectionElementTypesMustMatchDescription { return ResourceManager.GetString("LinearCollectionElementTypesMustMatchDescription", resourceCulture); } } - + /// /// Looks up a localized string similar to The element type of the 'ReadOnlySpan' returned by 'GetManagedValuesSource' must be the same as the element type returned by 'GetManagedValuesDestination'. /// @@ -563,9 +527,9 @@ internal static string LinearCollectionElementTypesMustMatchMessage { return ResourceManager.GetString("LinearCollectionElementTypesMustMatchMessage", resourceCulture); } } - + /// - /// Looks up a localized string similar to A 'LinearCollection'-kind native type that supports the 'CallerAllocatedBuffer' feature must provide a three-parameter constructor taking the managed type as the first parameter, a 'Span<byte>' as the second parameter, and the native size of the element as the second parameter. + /// Looks up a localized string similar to A 'LinearCollection'-kind native type that supports the 'CallerAllocatedBuffer' feature must provide a three-parameter constructor taking the managed type as the first parameter, a 'Span<byte>' as the second parameter, and the native size of the element as the third parameter. /// internal static string LinearCollectionInCallerAllocatedBufferRequiresSpanConstructorDescription { get { @@ -655,7 +619,7 @@ internal static string LinearCollectionOutRequiresIntConstructorMessage { } /// - /// Looks up a localized string similar to The specified marshaller direction must be valid value of the 'System.Runtime.InteropServices.CustomMarshallerDirection' enum.. + /// Looks up a localized string similar to The specified marshaller direction must be a valid value of the 'System.Runtime.InteropServices.CustomMarshallerDirection' enum.. /// internal static string MarshallerDirectionMustBeValidDescription { get { @@ -691,7 +655,7 @@ internal static string MarshallerGetPinnableReferenceRequiresTwoStageMarshalling } /// - /// Looks up a localized string similar to The specified marshaller kind must be valid value of the 'System.Runtime.InteropServices.CustomMarshallerKind' enum.. + /// Looks up a localized string similar to The specified marshaller kind must be a valid value of the 'System.Runtime.InteropServices.CustomMarshallerKind' enum.. /// internal static string MarshallerKindMustBeValidDescription { get { @@ -725,7 +689,7 @@ internal static string MarshallerTypeMustSpecifyManagedTypeMessage { return ResourceManager.GetString("MarshallerTypeMustSpecifyManagedTypeMessage", resourceCulture); } } - + /// /// Looks up a localized string similar to The native type '{0}' must be a closed generic or have the same number of generic parameters as the managed type so the emitted code can use a specific instantiation.. /// @@ -734,7 +698,7 @@ internal static string NativeGenericTypeMustBeClosedOrMatchArityDescription { return ResourceManager.GetString("NativeGenericTypeMustBeClosedOrMatchArityDescription", resourceCulture); } } - + /// /// Looks up a localized string similar to The native type '{0}' for managed type '{1}' must be a closed generic type or have the same arity as the managed type.. /// @@ -743,7 +707,7 @@ internal static string NativeGenericTypeMustBeClosedOrMatchArityMessage { return ResourceManager.GetString("NativeGenericTypeMustBeClosedOrMatchArityMessage", resourceCulture); } } - + /// /// Looks up a localized string similar to A native type must be blittable.. /// @@ -752,7 +716,7 @@ internal static string NativeTypeMustBeBlittableDescription { return ResourceManager.GetString("NativeTypeMustBeBlittableDescription", resourceCulture); } } - + /// /// Looks up a localized string similar to The native type '{0}' for type '{1}' must be blittable. /// @@ -761,25 +725,7 @@ internal static string NativeTypeMustBeBlittableMessage { return ResourceManager.GetString("NativeTypeMustBeBlittableMessage", resourceCulture); } } - - /// - /// Looks up a localized string similar to A native type for a given type must be non-null.. - /// - internal static string NativeTypeMustBeNonNullDescription { - get { - return ResourceManager.GetString("NativeTypeMustBeNonNullDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The native type for the type '{0}' is null. - /// - internal static string NativeTypeMustBeNonNullMessage { - get { - return ResourceManager.GetString("NativeTypeMustBeNonNullMessage", resourceCulture); - } - } - + /// /// Looks up a localized string similar to The native type must be pointer sized so the pinned result of 'GetPinnableReference' can be cast to the native type.. /// @@ -788,7 +734,7 @@ internal static string NativeTypeMustBePointerSizedDescription { return ResourceManager.GetString("NativeTypeMustBePointerSizedDescription", resourceCulture); } } - + /// /// Looks up a localized string similar to The native type '{0}' must be pointer sized because the managed type '{1}' has a 'GetPinnableReference' method. /// @@ -797,7 +743,7 @@ internal static string NativeTypeMustBePointerSizedMessage { return ResourceManager.GetString("NativeTypeMustBePointerSizedMessage", resourceCulture); } } - + /// /// Looks up a localized string similar to A native type for a given type must have the 'System.Runtime.InteropServices.CustomTypeMarshallerAttribute' that specifies this type as the managed type.. /// @@ -806,7 +752,7 @@ internal static string NativeTypeMustHaveCustomTypeMarshallerAttributeDescriptio return ResourceManager.GetString("NativeTypeMustHaveCustomTypeMarshallerAttributeDescription", resourceCulture); } } - + /// /// Looks up a localized string similar to The native type for the type '{0}' must be a type with the 'System.Runtime.InteropServices.CustomTypeMarshallerAttribute' that specifies this type as the managed type. /// @@ -851,34 +797,25 @@ internal static string OutTwoStageMarshallingRequiresFromNativeValueMessage { return ResourceManager.GetString("OutTwoStageMarshallingRequiresFromNativeValueMessage", resourceCulture); } } - + /// /// Looks up a localized string similar to The 'Value' property must not be a 'ref' or 'readonly ref' property.. /// - internal static string RefValuePropertyUnsupportedDescription { + internal static string RefNativeValueUnsupportedDescription { get { - return ResourceManager.GetString("RefValuePropertyUnsupportedDescription", resourceCulture); + return ResourceManager.GetString("RefNativeValueUnsupportedDescription", resourceCulture); } } - + /// /// Looks up a localized string similar to The 'Value' property on the native type '{0}' must not be a 'ref' or 'readonly ref' property.. /// - internal static string RefValuePropertyUnsupportedMessage { + internal static string RefNativeValueUnsupportedMessage { get { - return ResourceManager.GetString("RefValuePropertyUnsupportedMessage", resourceCulture); + return ResourceManager.GetString("RefNativeValueUnsupportedMessage", resourceCulture); } } - - /// - /// Looks up a localized string similar to . - /// - internal static string RuntimeMarshallingMustBeDisabled { - get { - return ResourceManager.GetString("RuntimeMarshallingMustBeDisabled", resourceCulture); - } - } - + /// /// Looks up a localized string similar to An abstract type derived from 'SafeHandle' cannot be marshalled by reference. The provided type must be concrete.. /// @@ -887,7 +824,7 @@ internal static string SafeHandleByRefMustBeConcrete { return ResourceManager.GetString("SafeHandleByRefMustBeConcrete", resourceCulture); } } - + /// /// Looks up a localized string similar to P/Invoke source generation is not supported on unknown target framework v{0}. The generated source will not be compatible with other frameworks.. /// @@ -896,7 +833,7 @@ internal static string TargetFrameworkNotSupportedDescription { return ResourceManager.GetString("TargetFrameworkNotSupportedDescription", resourceCulture); } } - + /// /// Looks up a localized string similar to 'LibraryImportAttribute' cannot be used for source-generated P/Invokes on an unknown target framework v{0}.. /// @@ -905,7 +842,7 @@ internal static string TargetFrameworkNotSupportedMessage { return ResourceManager.GetString("TargetFrameworkNotSupportedMessage", resourceCulture); } } - + /// /// Looks up a localized string similar to Current target framework is not supported by source-generated P/Invokes. /// @@ -914,7 +851,7 @@ internal static string TargetFrameworkNotSupportedTitle { return ResourceManager.GetString("TargetFrameworkNotSupportedTitle", resourceCulture); } } - + /// /// Looks up a localized string similar to A marshaller type that provides a 'ToNativeValue' method should specify that it supports the 'TwoStageMarshalling' feature.. /// @@ -959,7 +896,7 @@ internal static string TypeNotSupportedDescription { return ResourceManager.GetString("TypeNotSupportedDescription", resourceCulture); } } - + /// /// Looks up a localized string similar to The type '{0}' is not supported by source-generated P/Invokes. The generated source will not handle marshalling of parameter '{1}'.. /// @@ -968,7 +905,7 @@ internal static string TypeNotSupportedMessageParameter { return ResourceManager.GetString("TypeNotSupportedMessageParameter", resourceCulture); } } - + /// /// Looks up a localized string similar to {0} The generated source will not handle marshalling of parameter '{1}'.. /// @@ -977,7 +914,7 @@ internal static string TypeNotSupportedMessageParameterWithDetails { return ResourceManager.GetString("TypeNotSupportedMessageParameterWithDetails", resourceCulture); } } - + /// /// Looks up a localized string similar to The type '{0}' is not supported by source-generated P/Invokes. The generated source will not handle marshalling of the return value of method '{1}'.. /// @@ -986,7 +923,7 @@ internal static string TypeNotSupportedMessageReturn { return ResourceManager.GetString("TypeNotSupportedMessageReturn", resourceCulture); } } - + /// /// Looks up a localized string similar to {0} The generated source will not handle marshalling of the return value of method '{1}'.. /// @@ -995,7 +932,7 @@ internal static string TypeNotSupportedMessageReturnWithDetails { return ResourceManager.GetString("TypeNotSupportedMessageReturnWithDetails", resourceCulture); } } - + /// /// Looks up a localized string similar to Specified type is not supported by source-generated P/Invokes. /// @@ -1004,7 +941,7 @@ internal static string TypeNotSupportedTitle { return ResourceManager.GetString("TypeNotSupportedTitle", resourceCulture); } } - + /// /// Looks up a localized string similar to The 'UnmanagedResources' feature requires a 'void FreeNative()' method.. /// @@ -1013,7 +950,7 @@ internal static string UnmanagedResourcesRequiresFreeNativeDescription { return ResourceManager.GetString("UnmanagedResourcesRequiresFreeNativeDescription", resourceCulture); } } - + /// /// Looks up a localized string similar to The marshaller type '{0}' supports marshalling with the 'UnmanagedResources' feature, but it does not provide a parameterless 'FreeNative' instance method that returns 'void'. /// @@ -1022,7 +959,7 @@ internal static string UnmanagedResourcesRequiresFreeNativeMessage { return ResourceManager.GetString("UnmanagedResourcesRequiresFreeNativeMessage", resourceCulture); } } - + /// /// Looks up a localized string similar to A 'Value'-kind native type that supports the 'CallerAllocatedBuffer' feature must provide a two-parameter constructor taking the managed type and a 'Span<byte>' as parameters. /// @@ -1031,7 +968,7 @@ internal static string ValueInCallerAllocatedBufferRequiresSpanConstructorDescri return ResourceManager.GetString("ValueInCallerAllocatedBufferRequiresSpanConstructorDescription", resourceCulture); } } - + /// /// Looks up a localized string similar to The type '{0}' specifies that it supports 'In' marshalling with the 'CallerAllocatedBuffer' feature for '{1}' but does not provide a one-parameter constructor that takes a '{1}' and 'Span<byte>' as parameters. /// diff --git a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Resources.resx b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Resources.resx index 00206a8668088..cb1b973807da4 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Resources.resx +++ b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Resources.resx @@ -1,17 +1,17 @@  - @@ -118,7 +118,7 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - When a constructor taking a ''Span<byte> is specified on the native type, the type must set the BufferSize field on the 'System.Runtime.InteropServices.CustomTypeMarshallerAttribute' to provide the size of the caller-allocated buffer. + When a constructor taking a 'Span<byte>' is specified on the native type, the type must set the BufferSize field on the 'System.Runtime.InteropServices.CustomTypeMarshallerAttribute' to provide the size of the caller-allocated buffer. The native type '{0}' must set the 'BufferSize' field on the applied 'System.Runtime.InteropServices.CustomTypeMarshallerAttribute' to specify the size of the caller-allocated buffer because it has a constructor that takes a caller-allocated 'Span<byte>' @@ -234,7 +234,7 @@ The 'GetPinnableReference' method cannot be provided on the native type '{0}' unless the 'TwoStageMarshalling' feature is also supported - The specified marshaller kind must be valid value of the 'System.Runtime.InteropServices.CustomMarshallerKind' enum. + The specified marshaller kind must be a valid value of the 'System.Runtime.InteropServices.CustomMarshallerKind' enum. The specified custom marshaller kind for '{0}' is invalid @@ -275,10 +275,10 @@ The native type '{0}' must set the 'Direction' property on the 'CustomTypeMarshallerAttribute' to a value that sets at least one known flag value on the 'CustomTypeMarshallerDirection' enum - + The 'Value' property must not be a 'ref' or 'readonly ref' property. - + The 'Value' property on the native type '{0}' must not be a 'ref' or 'readonly ref' property. @@ -318,7 +318,7 @@ Specified type is not supported by source-generated P/Invokes - The 'TwoStageMarshalling' feature requires a TNativeType FromNativeValue()' method for the 'In' direction. + The 'TwoStageMarshalling' feature requires a 'TNativeType ToNativeValue()' method for the 'In' direction. The marshaller type '{0}' supports marshalling in the 'In' direction with the 'TwoStageMarshalling' feature must provide a 'ToNativeValue' instance method that returns the native value @@ -336,7 +336,7 @@ The 'CustomTypeMarshallerAttribute' on '{0}' is semantically invalid - A 'LinearCollection'-kind native type that supports the 'CallerAllocatedBuffer' feature must provide a three-parameter constructor taking the managed type as the first parameter, a 'Span<byte>' as the second parameter, and the native size of the element as the second parameter + A 'LinearCollection'-kind native type that supports the 'CallerAllocatedBuffer' feature must provide a three-parameter constructor taking the managed type as the first parameter, a 'Span<byte>' as the second parameter, and the native size of the element as the third parameter The type '{0}' specifies that it supports 'In' marshalling with the 'CallerAllocatedBuffer' feature for '{1}' but does not provide a three-parameter constructor that takes a '{1}' , a 'Span<byte>', and an 'int' @@ -348,7 +348,7 @@ The type '{0}' specifies that it supports 'In' marshalling of '{1}' but does not provide a two-parameter constructor that takes a '{1}' as the first parameter and an 'int' as the second parameter - The specified marshaller direction must be valid value of the 'System.Runtime.InteropServices.CustomMarshallerDirection' enum. + The specified marshaller direction must be a valid value of the 'System.Runtime.InteropServices.CustomMarshallerDirection' enum. The specified custom marshaller direction for '{0}' is invalid diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ICustomNativeTypeMarshallingStrategy.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ICustomNativeTypeMarshallingStrategy.cs index 205e3ae2fcb8d..494a1de0e8bb4 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ICustomNativeTypeMarshallingStrategy.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/ICustomNativeTypeMarshallingStrategy.cs @@ -146,7 +146,7 @@ public override (string managed, string native) GetIdentifiers(TypePositionInfo } /// - /// Marshaller that enables support of a Value property on a native type. + /// Marshaller that enables support of ToNativeValue/FromNativeValue methods on a native type. /// internal sealed class CustomNativeTypeWithToFromNativeValueMarshalling : ICustomNativeTypeMarshallingStrategy { @@ -1111,6 +1111,7 @@ private StatementSyntax GenerateByValueUnmarshalStatement(TypePositionInfo info, // but this is an uncommon case so we don't want to design the API around enabling just it. var (_, nativeIdentifier) = context.GetIdentifiers(info); string numElementsIdentifier = context.GetAdditionalIdentifier(info, "numElements"); + // int = .GetManagedValuesSource().Length; LocalDeclarationStatementSyntax numElementsDeclaration = LocalDeclarationStatement( VariableDeclaration( PredefinedType(Token(SyntaxKind.IntKeyword)), @@ -1125,6 +1126,7 @@ private StatementSyntax GenerateByValueUnmarshalStatement(TypePositionInfo info, IdentifierName("Length"))))))); string managedSpanIdentifier = MarshallerHelpers.GetManagedSpanIdentifier(info, context); + // Span = MemoryMarshal.CreateSpan(ref Unsafe.AsRef(ref .GetManagedValuesSource().GetPinnableReference(), )); LocalDeclarationStatementSyntax managedValuesDeclaration = LocalDeclarationStatement(VariableDeclaration( GenericName( Identifier(TypeNames.System_Span), diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources.Designer.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources.Designer.cs index 5bb39dd9f10d0..626e683b776e9 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources.Designer.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Resources.Designer.cs @@ -297,18 +297,18 @@ internal static string OutByValueNotSupportedMessage { /// /// Looks up a localized string similar to The 'Value' property must not be a 'ref' or 'readonly ref' property.. /// - internal static string RefValuePropertyUnsupportedDescription { + internal static string RefNativeValueUnsupportedDescription { get { - return ResourceManager.GetString("RefValuePropertyUnsupportedDescription", resourceCulture); + return ResourceManager.GetString("RefNativeValueUnsupportedDescription", resourceCulture); } } /// /// Looks up a localized string similar to The 'Value' property on the native type '{0}' must not be a 'ref' or 'readonly ref' property.. /// - internal static string RefValuePropertyUnsupportedMessage { + internal static string RefNativeValueUnsupportedMessage { get { - return ResourceManager.GetString("RefValuePropertyUnsupportedMessage", resourceCulture); + return ResourceManager.GetString("RefNativeValueUnsupportedMessage", resourceCulture); } } diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CustomTypeMarshallerFixerTests.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CustomTypeMarshallerFixerTests.cs index 8835a38b6d8e7..9707345179c44 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CustomTypeMarshallerFixerTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CustomTypeMarshallerFixerTests.cs @@ -440,7 +440,7 @@ public Native(S s) : this() }"; await VerifyCS.VerifyCodeFixAsync(source, - VerifyCS.Diagnostic(RefValuePropertyUnsupportedRule).WithLocation(0).WithArguments("Native"), + VerifyCS.Diagnostic(RefNativeValueUnsupportedRule).WithLocation(0).WithArguments("Native"), source); } @@ -473,7 +473,7 @@ public Native(S s) : this() }"; await VerifyCS.VerifyCodeFixAsync(source, - VerifyCS.Diagnostic(RefValuePropertyUnsupportedRule).WithLocation(0).WithArguments("Native"), + VerifyCS.Diagnostic(RefNativeValueUnsupportedRule).WithLocation(0).WithArguments("Native"), source); } From 8b8d52a04ae43466de2d95d8bb52dbe54d4442a7 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 21 Mar 2022 14:17:10 -0700 Subject: [PATCH 23/24] Merge some diagnostic IDs --- docs/project/list-of-diagnostics.md | 30 +++++++---------- .../Analyzers/AnalyzerDiagnostics.cs | 32 ++++++++----------- .../Analyzers/CustomTypeMarshallerAnalyzer.cs | 30 ++++++++--------- 3 files changed, 40 insertions(+), 52 deletions(-) diff --git a/docs/project/list-of-diagnostics.md b/docs/project/list-of-diagnostics.md index f3644ab022375..edd42ecefb5c0 100644 --- a/docs/project/list-of-diagnostics.md +++ b/docs/project/list-of-diagnostics.md @@ -158,21 +158,15 @@ The diagnostic id values reserved for .NET Libraries analyzer warnings are `SYSL | __`SYSLIB1052`__ | Specified configuration is not supported by source-generated P/Invokes | | __`SYSLIB1053`__ | Current target framework is not supported by source-generated P/Invokes | | __`SYSLIB1054`__ | Specified LibraryImportAttribute arguments cannot be forwarded to DllImportAttribute | -| __`SYSLIB1055`__ | *_`SYSLIB1055`-`SYSLIB1072` reserved for Microsoft.Interop.LibraryImportGenerator._* | -| __`SYSLIB1056`__ | *_`SYSLIB1055`-`SYSLIB1072` reserved for Microsoft.Interop.LibraryImportGenerator._* | -| __`SYSLIB1057`__ | *_`SYSLIB1055`-`SYSLIB1072` reserved for Microsoft.Interop.LibraryImportGenerator._* | -| __`SYSLIB1058`__ | *_`SYSLIB1055`-`SYSLIB1072` reserved for Microsoft.Interop.LibraryImportGenerator._* | -| __`SYSLIB1059`__ | *_`SYSLIB1055`-`SYSLIB1072` reserved for Microsoft.Interop.LibraryImportGenerator._* | -| __`SYSLIB1060`__ | *_`SYSLIB1055`-`SYSLIB1072` reserved for Microsoft.Interop.LibraryImportGenerator._* | -| __`SYSLIB1061`__ | *_`SYSLIB1055`-`SYSLIB1072` reserved for Microsoft.Interop.LibraryImportGenerator._* | -| __`SYSLIB1062`__ | *_`SYSLIB1055`-`SYSLIB1072` reserved for Microsoft.Interop.LibraryImportGenerator._* | -| __`SYSLIB1063`__ | *_`SYSLIB1055`-`SYSLIB1072` reserved for Microsoft.Interop.LibraryImportGenerator._* | -| __`SYSLIB1064`__ | *_`SYSLIB1055`-`SYSLIB1072` reserved for Microsoft.Interop.LibraryImportGenerator._* | -| __`SYSLIB1065`__ | *_`SYSLIB1055`-`SYSLIB1072` reserved for Microsoft.Interop.LibraryImportGenerator._* | -| __`SYSLIB1066`__ | *_`SYSLIB1055`-`SYSLIB1072` reserved for Microsoft.Interop.LibraryImportGenerator._* | -| __`SYSLIB1067`__ | *_`SYSLIB1055`-`SYSLIB1072` reserved for Microsoft.Interop.LibraryImportGenerator._* | -| __`SYSLIB1068`__ | *_`SYSLIB1055`-`SYSLIB1072` reserved for Microsoft.Interop.LibraryImportGenerator._* | -| __`SYSLIB1069`__ | *_`SYSLIB1055`-`SYSLIB1072` reserved for Microsoft.Interop.LibraryImportGenerator._* | -| __`SYSLIB1070`__ | *_`SYSLIB1055`-`SYSLIB1072` reserved for Microsoft.Interop.LibraryImportGenerator._* | -| __`SYSLIB1071`__ | *_`SYSLIB1055`-`SYSLIB1072` reserved for Microsoft.Interop.LibraryImportGenerator._* | -| __`SYSLIB1072`__ | *_`SYSLIB1055`-`SYSLIB1072` reserved for Microsoft.Interop.LibraryImportGenerator._* | +| __`SYSLIB1055`__ | *_`SYSLIB1055`-`SYSLIB1066` reserved for Microsoft.Interop.LibraryImportGenerator._* | +| __`SYSLIB1056`__ | *_`SYSLIB1055`-`SYSLIB1066` reserved for Microsoft.Interop.LibraryImportGenerator._* | +| __`SYSLIB1057`__ | *_`SYSLIB1055`-`SYSLIB1066` reserved for Microsoft.Interop.LibraryImportGenerator._* | +| __`SYSLIB1058`__ | *_`SYSLIB1055`-`SYSLIB1066` reserved for Microsoft.Interop.LibraryImportGenerator._* | +| __`SYSLIB1059`__ | *_`SYSLIB1055`-`SYSLIB1066` reserved for Microsoft.Interop.LibraryImportGenerator._* | +| __`SYSLIB1060`__ | *_`SYSLIB1055`-`SYSLIB1066` reserved for Microsoft.Interop.LibraryImportGenerator._* | +| __`SYSLIB1061`__ | *_`SYSLIB1055`-`SYSLIB1066` reserved for Microsoft.Interop.LibraryImportGenerator._* | +| __`SYSLIB1062`__ | *_`SYSLIB1055`-`SYSLIB1066` reserved for Microsoft.Interop.LibraryImportGenerator._* | +| __`SYSLIB1063`__ | *_`SYSLIB1055`-`SYSLIB1066` reserved for Microsoft.Interop.LibraryImportGenerator._* | +| __`SYSLIB1064`__ | *_`SYSLIB1055`-`SYSLIB1066` reserved for Microsoft.Interop.LibraryImportGenerator._* | +| __`SYSLIB1065`__ | *_`SYSLIB1055`-`SYSLIB1066` reserved for Microsoft.Interop.LibraryImportGenerator._* | +| __`SYSLIB1066`__ | *_`SYSLIB1055`-`SYSLIB1066` reserved for Microsoft.Interop.LibraryImportGenerator._* | diff --git a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/AnalyzerDiagnostics.cs b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/AnalyzerDiagnostics.cs index d09d2386dde52..e365435b742e4 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/AnalyzerDiagnostics.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/AnalyzerDiagnostics.cs @@ -14,27 +14,21 @@ public static class Ids { public const string Prefix = "DLLIMPORTGENANALYZER"; + // Migration from DllImport to LibraryImport + public const string ConvertToLibraryImport = Prefix + "001"; + // ManualTypeMarshalling - public const string MarshallerTypeMustSpecifyManagedType = Prefix + "001"; - public const string CustomTypeMarshallerAttributeMustBeValid = Prefix + "002"; - public const string NativeTypeMustHaveCustomTypeMarshallerAttribute = Prefix + "003"; - public const string NativeTypeMustBeBlittable = Prefix + "004"; + public const string MarshallerTypeMustSpecifyManagedType = Prefix + "002"; + public const string CustomTypeMarshallerAttributeMustBeValid = Prefix + "003"; + public const string InvalidNativeType = Prefix + "004"; public const string GetPinnableReferenceReturnTypeBlittable = Prefix + "005"; - public const string NativeTypeMustBePointerSized = Prefix + "006"; - public const string CustomMarshallerTypeMustHaveRequiredShape = Prefix + "007"; - public const string CustomMarshallerTypeMustSupportDirection = Prefix + "008"; - public const string ProvidedMethodsNotSpecifiedInShape = Prefix + "009"; - public const string GetPinnableReferenceShouldSupportAllocatingMarshallingFallback = Prefix + "010"; - public const string CallerAllocMarshallingShouldSupportAllocatingMarshallingFallback = Prefix + "011"; - public const string CallerAllocConstructorMustHaveBufferSize = Prefix + "012"; - public const string TwoStageMarshallingNativeTypesMustMatch = Prefix + "013"; - public const string LinearCollectionElementTypesMustMatch = Prefix + "014"; - public const string RefNativeValueUnsupported = Prefix + "015"; - public const string NativeGenericTypeMustBeClosedOrMatchArity = Prefix + "016"; - public const string MarshallerGetPinnableReferenceRequiresTwoStageMarshalling = Prefix + "017"; - - // Migration from DllImport to LibraryImport - public const string ConvertToLibraryImport = Prefix + "018"; + public const string CustomMarshallerTypeMustHaveRequiredShape = Prefix + "006"; + public const string CustomMarshallerTypeMustSupportDirection = Prefix + "007"; + public const string ProvidedMethodsNotSpecifiedInShape = Prefix + "008"; + public const string MissingAllocatingMarshallingFallback = Prefix + "009"; + public const string CallerAllocConstructorMustHaveBufferSize = Prefix + "010"; + public const string InvalidSignaturesInMarshallerShape = Prefix + "011"; + public const string MarshallerGetPinnableReferenceRequiresTwoStageMarshalling = Prefix + "012"; } internal static LocalizableResourceString GetResourceString(string resourceName) 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 01764475a3615..d9889c941c838 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/CustomTypeMarshallerAnalyzer.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/CustomTypeMarshallerAnalyzer.cs @@ -74,8 +74,8 @@ public static class MissingMemberNames public static readonly DiagnosticDescriptor NativeTypeMustHaveCustomTypeMarshallerAttributeRule = new DiagnosticDescriptor( - Ids.NativeTypeMustHaveCustomTypeMarshallerAttribute, - "NativeTypeMustHaveCustomTypeMarshallerAttribute", + Ids.InvalidNativeType, + "InvalidNativeType", GetResourceString(nameof(Resources.NativeTypeMustHaveCustomTypeMarshallerAttributeMessage)), Category, DiagnosticSeverity.Error, @@ -84,8 +84,8 @@ public static class MissingMemberNames public static readonly DiagnosticDescriptor NativeTypeMustBeBlittableRule = new DiagnosticDescriptor( - Ids.NativeTypeMustBeBlittable, - "NativeTypeMustBeBlittable", + Ids.InvalidNativeType, + "InvalidNativeType", GetResourceString(nameof(Resources.NativeTypeMustBeBlittableMessage)), Category, DiagnosticSeverity.Error, @@ -104,8 +104,8 @@ public static class MissingMemberNames public static readonly DiagnosticDescriptor NativeTypeMustBePointerSizedRule = new DiagnosticDescriptor( - Ids.NativeTypeMustBePointerSized, - "NativeTypeMustBePointerSized", + Ids.InvalidNativeType, + "InvalidNativeType", GetResourceString(nameof(Resources.NativeTypeMustBePointerSizedMessage)), Category, DiagnosticSeverity.Error, @@ -234,7 +234,7 @@ public static class MissingMemberNames public static readonly DiagnosticDescriptor GetPinnableReferenceShouldSupportAllocatingMarshallingFallbackRule = new DiagnosticDescriptor( - Ids.GetPinnableReferenceShouldSupportAllocatingMarshallingFallback, + Ids.MissingAllocatingMarshallingFallback, "GetPinnableReferenceShouldSupportAllocatingMarshallingFallback", GetResourceString(nameof(Resources.GetPinnableReferenceShouldSupportAllocatingMarshallingFallbackMessage)), Category, @@ -244,7 +244,7 @@ public static class MissingMemberNames public static readonly DiagnosticDescriptor CallerAllocMarshallingShouldSupportAllocatingMarshallingFallbackRule = new DiagnosticDescriptor( - Ids.CallerAllocMarshallingShouldSupportAllocatingMarshallingFallback, + Ids.MissingAllocatingMarshallingFallback, "CallerAllocMarshallingShouldSupportAllocatingMarshallingFallback", GetResourceString(nameof(Resources.CallerAllocMarshallingShouldSupportAllocatingMarshallingFallbackMessage)), Category, @@ -264,8 +264,8 @@ public static class MissingMemberNames public static readonly DiagnosticDescriptor RefNativeValueUnsupportedRule = new DiagnosticDescriptor( - Ids.RefNativeValueUnsupported, - "RefNativeValueUnsupported", + Ids.InvalidSignaturesInMarshallerShape, + "InvalidSignaturesInMarshallerShape", GetResourceString(nameof(Resources.RefNativeValueUnsupportedMessage)), Category, DiagnosticSeverity.Error, @@ -274,7 +274,7 @@ public static class MissingMemberNames public static readonly DiagnosticDescriptor NativeGenericTypeMustBeClosedOrMatchArityRule = new DiagnosticDescriptor( - Ids.NativeGenericTypeMustBeClosedOrMatchArity, + Ids.InvalidNativeType, "NativeGenericTypeMustBeClosedOrMatchArity", GetResourceString(nameof(Resources.NativeGenericTypeMustBeClosedOrMatchArityMessage)), Category, @@ -334,8 +334,8 @@ public static class MissingMemberNames public static readonly DiagnosticDescriptor TwoStageMarshallingNativeTypesMustMatchRule = new DiagnosticDescriptor( - Ids.TwoStageMarshallingNativeTypesMustMatch, - "TwoStageMarshallingNativeTypesMustMatch", + Ids.InvalidSignaturesInMarshallerShape, + "InvalidSignaturesInMarshallerShape", GetResourceString(nameof(Resources.TwoStageMarshallingNativeTypesMustMatchMessage)), Category, DiagnosticSeverity.Warning, @@ -344,8 +344,8 @@ public static class MissingMemberNames public static readonly DiagnosticDescriptor LinearCollectionElementTypesMustMatchRule = new DiagnosticDescriptor( - Ids.LinearCollectionElementTypesMustMatch, - "LinearCollectionElementTypesMustMatch", + Ids.InvalidSignaturesInMarshallerShape, + "InvalidSignaturesInMarshallerShape", GetResourceString(nameof(Resources.LinearCollectionElementTypesMustMatchMessage)), Category, DiagnosticSeverity.Warning, From 23613c38a743f880b10a24295d46e0c81ad43e63 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 21 Mar 2022 15:21:26 -0700 Subject: [PATCH 24/24] Fix id reference --- .../Analyzers/CustomTypeMarshallerFixer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/CustomTypeMarshallerFixer.cs b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/CustomTypeMarshallerFixer.cs index 7b0bb9ec42016..08cc2550ed4e2 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/CustomTypeMarshallerFixer.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/CustomTypeMarshallerFixer.cs @@ -98,7 +98,7 @@ private static async Task AddMissingFeatures(FixAllContext fixAllContext, Docume public override ImmutableArray FixableDiagnosticIds { get; } = ImmutableArray.Create( AnalyzerDiagnostics.Ids.CustomMarshallerTypeMustHaveRequiredShape, - AnalyzerDiagnostics.Ids.CallerAllocMarshallingShouldSupportAllocatingMarshallingFallback, + AnalyzerDiagnostics.Ids.MissingAllocatingMarshallingFallback, AnalyzerDiagnostics.Ids.ProvidedMethodsNotSpecifiedInShape); public override async Task RegisterCodeFixesAsync(CodeFixContext context)