diff --git a/DllImportGenerator/Ancillary.Interop/Ancillary.Interop.csproj b/DllImportGenerator/Ancillary.Interop/Ancillary.Interop.csproj index e19d9b4e5d9b..b04f4ba3c089 100644 --- a/DllImportGenerator/Ancillary.Interop/Ancillary.Interop.csproj +++ b/DllImportGenerator/Ancillary.Interop/Ancillary.Interop.csproj @@ -6,6 +6,7 @@ System.Runtime.InteropServices enable true + APIs required for usage of the DllImportGenerator and related tools. diff --git a/DllImportGenerator/Benchmarks/Benchmarks.csproj b/DllImportGenerator/Benchmarks/Benchmarks.csproj index 02ec9f973bfd..fb1e9ea05338 100644 --- a/DllImportGenerator/Benchmarks/Benchmarks.csproj +++ b/DllImportGenerator/Benchmarks/Benchmarks.csproj @@ -18,6 +18,7 @@ + diff --git a/DllImportGenerator/Demo/Demo.csproj b/DllImportGenerator/Demo/Demo.csproj index 11805179e79d..1ffe230ada81 100644 --- a/DllImportGenerator/Demo/Demo.csproj +++ b/DllImportGenerator/Demo/Demo.csproj @@ -16,6 +16,7 @@ + diff --git a/DllImportGenerator/DllImportGenerator.IntegrationTests/DllImportGenerator.IntegrationTests.csproj b/DllImportGenerator/DllImportGenerator.IntegrationTests/DllImportGenerator.IntegrationTests.csproj index 4588c962fc81..ecd9ee7adf63 100644 --- a/DllImportGenerator/DllImportGenerator.IntegrationTests/DllImportGenerator.IntegrationTests.csproj +++ b/DllImportGenerator/DllImportGenerator.IntegrationTests/DllImportGenerator.IntegrationTests.csproj @@ -18,6 +18,7 @@ + diff --git a/DllImportGenerator/DllImportGenerator.sln b/DllImportGenerator/DllImportGenerator.sln index 20d7d1b61884..79455991304f 100644 --- a/DllImportGenerator/DllImportGenerator.sln +++ b/DllImportGenerator/DllImportGenerator.sln @@ -29,6 +29,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Benchmarks", "Benchmarks\Be EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DllImportGenerator.Vsix", "DllImportGenerator.Vsix\DllImportGenerator.Vsix.csproj", "{F9215CDD-7B47-4C17-9166-0BE5066CA6BB}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Interop.SourceGeneration", "Microsoft.Interop.SourceGeneration\Microsoft.Interop.SourceGeneration.csproj", "{03FAE24C-728D-419C-B6EC-5C7AE8C45705}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -79,6 +81,10 @@ Global {F9215CDD-7B47-4C17-9166-0BE5066CA6BB}.Debug|Any CPU.Build.0 = Debug|Any CPU {F9215CDD-7B47-4C17-9166-0BE5066CA6BB}.Release|Any CPU.ActiveCfg = Release|Any CPU {F9215CDD-7B47-4C17-9166-0BE5066CA6BB}.Release|Any CPU.Build.0 = Release|Any CPU + {03FAE24C-728D-419C-B6EC-5C7AE8C45705}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {03FAE24C-728D-419C-B6EC-5C7AE8C45705}.Debug|Any CPU.Build.0 = Debug|Any CPU + {03FAE24C-728D-419C-B6EC-5C7AE8C45705}.Release|Any CPU.ActiveCfg = Release|Any CPU + {03FAE24C-728D-419C-B6EC-5C7AE8C45705}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/DllImportGenerator/DllImportGenerator/DllImportGenerator.cs b/DllImportGenerator/DllImportGenerator/DllImportGenerator.cs index 92668a646d46..70dfbaef0120 100644 --- a/DllImportGenerator/DllImportGenerator/DllImportGenerator.cs +++ b/DllImportGenerator/DllImportGenerator/DllImportGenerator.cs @@ -105,16 +105,18 @@ public void Initialize(IncrementalGeneratorInitializationContext context) } }); + var stubOptions = context.AnalyzerConfigOptionsProvider.Select((options, ct) => new DllImportGeneratorOptions(options.GlobalOptions)); + var stubEnvironment = compilationAndTargetFramework - .Combine(context.AnalyzerConfigOptionsProvider) + .Combine(stubOptions) .Select( static (data, ct) => new StubEnvironment( data.Left.compilation, data.Left.isSupported, data.Left.targetFrameworkVersion, - data.Right.GlobalOptions, - data.Left.compilation.SourceModule.GetAttributes().Any(attr => attr.AttributeClass?.ToDisplayString() == TypeNames.System_Runtime_CompilerServices_SkipLocalsInitAttribute)) + data.Left.compilation.SourceModule.GetAttributes().Any(attr => attr.AttributeClass?.ToDisplayString() == TypeNames.System_Runtime_CompilerServices_SkipLocalsInitAttribute), + data.Right) ); var methodSourceAndDiagnostics = methodsToGenerate @@ -133,12 +135,12 @@ public void Initialize(IncrementalGeneratorInitializationContext context) } ) .WithComparer(Comparers.CalculatedContextWithSyntax) - .Combine(context.AnalyzerConfigOptionsProvider) + .Combine(stubOptions) .Select( (data, ct) => { IncrementalTracker?.RecordExecutedStep(new IncrementalityTracker.ExecutedStepInfo(IncrementalityTracker.StepName.GenerateSingleStub, data)); - return GenerateSource(data.Left.StubContext, data.Left.Syntax, data.Right.GlobalOptions); + return GenerateSource(data.Left.StubContext, data.Left.Syntax, data.Right); } ) .WithComparer(Comparers.GeneratedSyntax) @@ -444,24 +446,196 @@ private static IncrementalStubGenerationContext CalculateStubInformation(MethodD private (MemberDeclarationSyntax, ImmutableArray) GenerateSource( IncrementalStubGenerationContext dllImportStub, MethodDeclarationSyntax originalSyntax, - AnalyzerConfigOptions options) + DllImportGeneratorOptions options) { var diagnostics = new GeneratorDiagnostics(); // Generate stub code - var stubGenerator = new StubCodeGenerator( - dllImportStub.DllImportData, + var stubGenerator = new PInvokeStubCodeGenerator( dllImportStub.StubContext.ElementTypeInformation, - options, - (elementInfo, ex) => diagnostics.ReportMarshallingNotSupported(originalSyntax, elementInfo, ex.NotSupportedDetails)); + dllImportStub.DllImportData.SetLastError && !options.GenerateForwarders, + (elementInfo, ex) => diagnostics.ReportMarshallingNotSupported(originalSyntax, elementInfo, ex.NotSupportedDetails), + dllImportStub.StubContext.GeneratorFactory); ImmutableArray forwardedAttributes = dllImportStub.ForwardedAttributes; - var code = stubGenerator.GenerateBody(originalSyntax.Identifier.Text, forwardedAttributes: forwardedAttributes.Length != 0 ? AttributeList(SeparatedList(forwardedAttributes)) : null); + const string innerPInvokeName = "__PInvoke__"; + + var code = stubGenerator.GeneratePInvokeBody(innerPInvokeName); + + var dllImport = CreateTargetFunctionAsLocalStatement( + stubGenerator, + dllImportStub.StubContext.Options, + dllImportStub.DllImportData, + innerPInvokeName, + originalSyntax.Identifier.Text); + + if (!forwardedAttributes.IsEmpty) + { + dllImport = dllImport.AddAttributeLists(AttributeList(SeparatedList(forwardedAttributes))); + } + + code = code.AddStatements(dllImport); return (PrintGeneratedSource(originalSyntax, dllImportStub.StubContext, code), dllImportStub.Diagnostics.AddRange(diagnostics.Diagnostics)); } + + private static LocalFunctionStatementSyntax CreateTargetFunctionAsLocalStatement( + PInvokeStubCodeGenerator stubGenerator, + DllImportGeneratorOptions options, + GeneratedDllImportData dllImportData, + string stubTargetName, + string stubMethodName) + { + var (parameterList, returnType, returnTypeAttributes) = stubGenerator.GenerateTargetMethodSignatureData(); + var localDllImport = LocalFunctionStatement(returnType, stubTargetName) + .AddModifiers( + Token(SyntaxKind.ExternKeyword), + Token(SyntaxKind.StaticKeyword), + Token(SyntaxKind.UnsafeKeyword)) + .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)) + .WithAttributeLists( + SingletonList(AttributeList( + SingletonSeparatedList( + CreateDllImportAttributeForTarget( + GetTargetDllImportDataFromStubData( + dllImportData, + stubMethodName, + options.GenerateForwarders)))))) + .WithParameterList(parameterList); + if (returnTypeAttributes is not null) + { + localDllImport = localDllImport.AddAttributeLists(returnTypeAttributes.WithTarget(AttributeTargetSpecifier(Token(SyntaxKind.ReturnKeyword)))); + } + return localDllImport; + } + + private static AttributeSyntax CreateDllImportAttributeForTarget(GeneratedDllImportData targetDllImportData) + { + var newAttributeArgs = new List + { + AttributeArgument(LiteralExpression( + SyntaxKind.StringLiteralExpression, + Literal(targetDllImportData.ModuleName))), + AttributeArgument( + NameEquals(nameof(DllImportAttribute.EntryPoint)), + null, + CreateStringExpressionSyntax(targetDllImportData.EntryPoint!)) + }; + + if (targetDllImportData.IsUserDefined.HasFlag(DllImportMember.BestFitMapping)) + { + var name = NameEquals(nameof(DllImportAttribute.BestFitMapping)); + var value = CreateBoolExpressionSyntax(targetDllImportData.BestFitMapping); + newAttributeArgs.Add(AttributeArgument(name, null, value)); + } + if (targetDllImportData.IsUserDefined.HasFlag(DllImportMember.CallingConvention)) + { + var name = NameEquals(nameof(DllImportAttribute.CallingConvention)); + var value = CreateEnumExpressionSyntax(targetDllImportData.CallingConvention); + newAttributeArgs.Add(AttributeArgument(name, null, value)); + } + if (targetDllImportData.IsUserDefined.HasFlag(DllImportMember.CharSet)) + { + var name = NameEquals(nameof(DllImportAttribute.CharSet)); + var value = CreateEnumExpressionSyntax(targetDllImportData.CharSet); + newAttributeArgs.Add(AttributeArgument(name, null, value)); + } + if (targetDllImportData.IsUserDefined.HasFlag(DllImportMember.ExactSpelling)) + { + var name = NameEquals(nameof(DllImportAttribute.ExactSpelling)); + var value = CreateBoolExpressionSyntax(targetDllImportData.ExactSpelling); + newAttributeArgs.Add(AttributeArgument(name, null, value)); + } + if (targetDllImportData.IsUserDefined.HasFlag(DllImportMember.PreserveSig)) + { + var name = NameEquals(nameof(DllImportAttribute.PreserveSig)); + var value = CreateBoolExpressionSyntax(targetDllImportData.PreserveSig); + newAttributeArgs.Add(AttributeArgument(name, null, value)); + } + if (targetDllImportData.IsUserDefined.HasFlag(DllImportMember.SetLastError)) + { + var name = NameEquals(nameof(DllImportAttribute.SetLastError)); + var value = CreateBoolExpressionSyntax(targetDllImportData.SetLastError); + newAttributeArgs.Add(AttributeArgument(name, null, value)); + } + if (targetDllImportData.IsUserDefined.HasFlag(DllImportMember.ThrowOnUnmappableChar)) + { + var name = NameEquals(nameof(DllImportAttribute.ThrowOnUnmappableChar)); + var value = CreateBoolExpressionSyntax(targetDllImportData.ThrowOnUnmappableChar); + newAttributeArgs.Add(AttributeArgument(name, null, value)); + } + + // Create new attribute + return Attribute( + ParseName(typeof(DllImportAttribute).FullName), + AttributeArgumentList(SeparatedList(newAttributeArgs))); + + static ExpressionSyntax CreateBoolExpressionSyntax(bool trueOrFalse) + { + return LiteralExpression( + trueOrFalse + ? SyntaxKind.TrueLiteralExpression + : SyntaxKind.FalseLiteralExpression); + } + + static ExpressionSyntax CreateStringExpressionSyntax(string str) + { + return LiteralExpression( + SyntaxKind.StringLiteralExpression, + Literal(str)); + } + + static ExpressionSyntax CreateEnumExpressionSyntax(T value) where T : Enum + { + return MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + IdentifierName(typeof(T).FullName), + IdentifierName(value.ToString())); + } + } + + private static GeneratedDllImportData GetTargetDllImportDataFromStubData(GeneratedDllImportData dllImportData, string originalMethodName, bool forwardAll) + { + DllImportMember membersToForward = DllImportMember.All + // https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.dllimportattribute.preservesig + // If PreserveSig=false (default is true), the P/Invoke stub checks/converts a returned HRESULT to an exception. + & ~DllImportMember.PreserveSig + // https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.dllimportattribute.setlasterror + // If SetLastError=true (default is false), the P/Invoke stub gets/caches the last error after invoking the native function. + & ~DllImportMember.SetLastError; + if (forwardAll) + { + membersToForward = DllImportMember.All; + } + + var targetDllImportData = new GeneratedDllImportData(dllImportData.ModuleName) + { + CharSet = dllImportData.CharSet, + BestFitMapping = dllImportData.BestFitMapping, + CallingConvention = dllImportData.CallingConvention, + EntryPoint = dllImportData.EntryPoint, + ExactSpelling = dllImportData.ExactSpelling, + SetLastError = dllImportData.SetLastError, + PreserveSig = dllImportData.PreserveSig, + ThrowOnUnmappableChar = dllImportData.ThrowOnUnmappableChar, + IsUserDefined = dllImportData.IsUserDefined & membersToForward + }; + + // If the EntryPoint property is not set, we will compute and + // add it based on existing semantics (i.e. method name). + // + // N.B. The export discovery logic is identical regardless of where + // the name is defined (i.e. method name vs EntryPoint property). + if (!targetDllImportData.IsUserDefined.HasFlag(DllImportMember.EntryPoint)) + { + targetDllImportData = targetDllImportData with { EntryPoint = originalMethodName }; + } + + return targetDllImportData; + } + private static bool ShouldVisitNode(SyntaxNode syntaxNode) { // We only support C# method declarations. diff --git a/DllImportGenerator/DllImportGenerator/DllImportGenerator.csproj b/DllImportGenerator/DllImportGenerator/DllImportGenerator.csproj index cda46f7058dc..ca0f1512667e 100644 --- a/DllImportGenerator/DllImportGenerator/DllImportGenerator.csproj +++ b/DllImportGenerator/DllImportGenerator/DllImportGenerator.csproj @@ -35,12 +35,12 @@ - + + - - + @@ -58,4 +58,14 @@ + + + + + + + + + + diff --git a/DllImportGenerator/DllImportGenerator/OptionsHelper.cs b/DllImportGenerator/DllImportGenerator/DllImportGeneratorOptions.cs similarity index 77% rename from DllImportGenerator/DllImportGenerator/OptionsHelper.cs rename to DllImportGenerator/DllImportGenerator/DllImportGeneratorOptions.cs index 13130369b41d..d63f7c880b9c 100644 --- a/DllImportGenerator/DllImportGenerator/OptionsHelper.cs +++ b/DllImportGenerator/DllImportGenerator/DllImportGeneratorOptions.cs @@ -1,12 +1,15 @@ -using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Runtime.InteropServices; namespace Microsoft.Interop { + record DllImportGeneratorOptions(bool GenerateForwarders, bool UseMarshalType, bool UseInternalUnsafeType) + { + public DllImportGeneratorOptions(AnalyzerConfigOptions options) + : this(options.GenerateForwarders(), options.UseMarshalType(), options.UseInternalUnsafeType()) + { + } + } + public static class OptionsHelper { public const string UseMarshalTypeOption = "build_property.DllImportGenerator_UseMarshalType"; diff --git a/DllImportGenerator/DllImportGenerator/DllImportStubContext.cs b/DllImportGenerator/DllImportGenerator/DllImportStubContext.cs index e86b8fdef826..c3bfef462d52 100644 --- a/DllImportGenerator/DllImportGenerator/DllImportStubContext.cs +++ b/DllImportGenerator/DllImportGenerator/DllImportStubContext.cs @@ -17,8 +17,8 @@ internal record StubEnvironment( Compilation Compilation, bool SupportedTargetFramework, Version TargetFrameworkVersion, - AnalyzerConfigOptions Options, - bool ModuleSkipLocalsInit); + bool ModuleSkipLocalsInit, + DllImportGeneratorOptions Options); internal sealed class DllImportStubContext : IEquatable { @@ -58,6 +58,10 @@ public IEnumerable StubParameters public ImmutableArray AdditionalAttributes { get; init; } + public DllImportGeneratorOptions Options { get; init; } + + public IMarshallingGeneratorFactory GeneratorFactory { get; init; } + public static DllImportStubContext Create( IMethodSymbol method, GeneratedDllImportData dllImportData, @@ -95,7 +99,7 @@ public static DllImportStubContext Create( currType = currType.ContainingType; } - var typeInfos = GenerateTypeInformation(method, dllImportData, diagnostics, env); + var (typeInfos, generatorFactory) = GenerateTypeInformation(method, dllImportData, diagnostics, env); var additionalAttrs = ImmutableArray.CreateBuilder(); @@ -120,10 +124,12 @@ public static DllImportStubContext Create( StubTypeNamespace = stubTypeNamespace, StubContainingTypes = containingTypes.ToImmutable(), AdditionalAttributes = additionalAttrs.ToImmutable(), + Options = env.Options, + GeneratorFactory = generatorFactory }; } - private static ImmutableArray GenerateTypeInformation(IMethodSymbol method, GeneratedDllImportData dllImportData, GeneratorDiagnostics diagnostics, StubEnvironment env) + private static (ImmutableArray, IMarshallingGeneratorFactory) GenerateTypeInformation(IMethodSymbol method, GeneratedDllImportData dllImportData, GeneratorDiagnostics diagnostics, StubEnvironment env) { // Compute the current default string encoding value. var defaultEncoding = CharEncoding.Undefined; @@ -165,36 +171,52 @@ private static ImmutableArray GenerateTypeInformation(IMethodS NativeIndex = TypePositionInfo.ReturnIndex }; - var managedRetTypeInfo = retTypeInfo; - // Do not manually handle PreserveSig when generating forwarders. - // We want the runtime to handle everything. - if (!dllImportData.PreserveSig && !env.Options.GenerateForwarders()) + InteropGenerationOptions options = new(env.Options.UseMarshalType, env.Options.UseInternalUnsafeType); + IMarshallingGeneratorFactory generatorFactory; + + if (env.Options.GenerateForwarders) { - // Create type info for native HRESULT return - retTypeInfo = new TypePositionInfo(SpecialTypeInfo.Int32, NoMarshallingInfo.Instance); - retTypeInfo = retTypeInfo with + generatorFactory = new ForwarderMarshallingGeneratorFactory(); + } + else + { + generatorFactory = new DefaultMarshallingGeneratorFactory(options); + AttributedMarshallingModelGeneratorFactory attributedMarshallingFactory = new(generatorFactory, options); + generatorFactory = attributedMarshallingFactory; + if (!dllImportData.PreserveSig) { - NativeIndex = TypePositionInfo.ReturnIndex - }; + // Create type info for native out param + if (!method.ReturnsVoid) + { + // Transform the managed return type info into an out parameter and add it as the last param + TypePositionInfo nativeOutInfo = retTypeInfo with + { + InstanceIdentifier = PInvokeStubCodeGenerator.ReturnIdentifier, + RefKind = RefKind.Out, + RefKindSyntax = SyntaxKind.OutKeyword, + ManagedIndex = TypePositionInfo.ReturnIndex, + NativeIndex = typeInfos.Count + }; + typeInfos.Add(nativeOutInfo); + } - // Create type info for native out param - if (!method.ReturnsVoid) - { - // Transform the managed return type info into an out parameter and add it as the last param - TypePositionInfo nativeOutInfo = managedRetTypeInfo with + // Use a marshalling generator that supports the HRESULT return->exception marshalling. + generatorFactory = new NoPreserveSigMarshallingGeneratorFactory(generatorFactory); + + // Create type info for native HRESULT return + retTypeInfo = new TypePositionInfo(SpecialTypeInfo.Int32, NoMarshallingInfo.Instance); + retTypeInfo = retTypeInfo with { - InstanceIdentifier = StubCodeGenerator.ReturnIdentifier, - RefKind = RefKind.Out, - RefKindSyntax = SyntaxKind.OutKeyword, - ManagedIndex = TypePositionInfo.ReturnIndex, - NativeIndex = typeInfos.Count + NativeIndex = TypePositionInfo.ReturnIndex }; - typeInfos.Add(nativeOutInfo); } + + generatorFactory = new ByValueContentsMarshalKindValidator(generatorFactory); + attributedMarshallingFactory.ElementMarshallingGeneratorFactory = generatorFactory; } typeInfos.Add(retTypeInfo); - return typeInfos.ToImmutable(); + return (typeInfos.ToImmutable(), generatorFactory); } public override bool Equals(object obj) @@ -204,12 +226,15 @@ public override bool Equals(object obj) public bool Equals(DllImportStubContext other) { + // We don't check if the generator factories are equal since + // the generator factory is deterministically created based on the ElementTypeInformation and Options. return other is not null && StubTypeNamespace == other.StubTypeNamespace && ElementTypeInformation.SequenceEqual(other.ElementTypeInformation) && StubContainingTypes.SequenceEqual(other.StubContainingTypes, (IEqualityComparer)new SyntaxEquivalentComparer()) && StubReturnType.IsEquivalentTo(other.StubReturnType) - && AdditionalAttributes.SequenceEqual(other.AdditionalAttributes, (IEqualityComparer)new SyntaxEquivalentComparer()); + && AdditionalAttributes.SequenceEqual(other.AdditionalAttributes, (IEqualityComparer)new SyntaxEquivalentComparer()) + && Options.Equals(other.Options); } public override int GetHashCode() diff --git a/DllImportGenerator/DllImportGenerator/ForwarderMarshallingGeneratorFactory.cs b/DllImportGenerator/DllImportGenerator/ForwarderMarshallingGeneratorFactory.cs new file mode 100644 index 000000000000..60c85b4baba0 --- /dev/null +++ b/DllImportGenerator/DllImportGenerator/ForwarderMarshallingGeneratorFactory.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.Interop +{ + class ForwarderMarshallingGeneratorFactory : IMarshallingGeneratorFactory + { + private static readonly Forwarder Forwarder = new Forwarder(); + + public IMarshallingGenerator Create(TypePositionInfo info, StubCodeContext context) => Forwarder; + } +} diff --git a/DllImportGenerator/DllImportGenerator/GeneratorDiagnostics.cs b/DllImportGenerator/DllImportGenerator/GeneratorDiagnostics.cs index 74abfb3c7149..77b018f4b880 100644 --- a/DllImportGenerator/DllImportGenerator/GeneratorDiagnostics.cs +++ b/DllImportGenerator/DllImportGenerator/GeneratorDiagnostics.cs @@ -1,69 +1,18 @@ -using System; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; -using System.Linq; - -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp.Syntax; +using System.Text; namespace Microsoft.Interop { - internal static class DiagnosticExtensions - { - public static Diagnostic CreateDiagnostic( - this ISymbol symbol, - DiagnosticDescriptor descriptor, - params object[] args) - { - return symbol.Locations.CreateDiagnostic(descriptor, args); - } - - public static Diagnostic CreateDiagnostic( - this AttributeData attributeData, - DiagnosticDescriptor descriptor, - params object[] args) - { - SyntaxReference? syntaxReference = attributeData.ApplicationSyntaxReference; - Location location = syntaxReference is not null - ? syntaxReference.GetSyntax().GetLocation() - : Location.None; - - return location.CreateDiagnostic(descriptor, args); - } - - public static Diagnostic CreateDiagnostic( - this ImmutableArray locations, - DiagnosticDescriptor descriptor, - params object[] args) - { - IEnumerable locationsInSource = locations.Where(l => l.IsInSource); - if (!locationsInSource.Any()) - return Diagnostic.Create(descriptor, Location.None, args); - - return Diagnostic.Create( - descriptor, - location: locationsInSource.First(), - additionalLocations: locationsInSource.Skip(1), - messageArgs: args); - } - - public static Diagnostic CreateDiagnostic( - this Location location, - DiagnosticDescriptor descriptor, - params object[] args) - { - return Diagnostic.Create( - descriptor, - location: location.IsInSource ? location : Location.None, - messageArgs: args); - } - } /// /// Class for reporting diagnostics in the DLL import generator /// - public class GeneratorDiagnostics + public class GeneratorDiagnostics : IGeneratorDiagnostics { public class Ids { @@ -213,7 +162,7 @@ public void ReportConfigurationNotSupported( /// Method with the parameter/return /// Type info for the parameter/return /// [Optional] Specific reason for lack of support - internal void ReportMarshallingNotSupported( + public void ReportMarshallingNotSupported( MethodDeclarationSyntax method, TypePositionInfo info, string? notSupportedDetails) @@ -298,7 +247,7 @@ internal void ReportMarshallingNotSupported( } } - internal void ReportInvalidMarshallingAttributeInfo( + public void ReportInvalidMarshallingAttributeInfo( AttributeData attributeData, string reasonResourceName, params string[] reasonArgs) diff --git a/DllImportGenerator/DllImportGenerator/Marshalling/MarshallingGenerator.cs b/DllImportGenerator/DllImportGenerator/Marshalling/MarshallingGenerator.cs deleted file mode 100644 index 24fb56ad90bc..000000000000 --- a/DllImportGenerator/DllImportGenerator/Marshalling/MarshallingGenerator.cs +++ /dev/null @@ -1,628 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Runtime.InteropServices; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Diagnostics; -using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; - -namespace Microsoft.Interop -{ - /// - /// Interface for generation of marshalling code for P/Invoke stubs - /// - internal interface IMarshallingGenerator - { - /// - /// Get the native type syntax for - /// - /// Object to marshal - /// Type syntax for the native type representing - TypeSyntax AsNativeType(TypePositionInfo info); - - /// - /// Get the as a parameter of the P/Invoke declaration - /// - /// Object to marshal - /// Parameter syntax for - ParameterSyntax AsParameter(TypePositionInfo info); - - /// - /// Get the as an argument to be passed to the P/Invoke - /// - /// Object to marshal - /// Code generation context - /// Argument syntax for - ArgumentSyntax AsArgument(TypePositionInfo info, StubCodeContext context); - - /// - /// Generate code for marshalling - /// - /// Object to marshal - /// Code generation context - /// List of statements to be added to the P/Invoke stub - /// - /// The generator should return the appropriate statements based on the - /// of . - /// For , any statements not of type - /// will be ignored. - /// - IEnumerable Generate(TypePositionInfo info, StubCodeContext context); - - /// - /// Returns whether or not this marshaller uses an identifier for the native value in addition - /// to an identifer for the managed value. - /// - /// Object to marshal - /// Code generation context - /// If the marshaller uses an identifier for the native value, true; otherwise, false. - /// - /// of may not be valid. - /// - bool UsesNativeIdentifier(TypePositionInfo info, StubCodeContext context); - - /// - /// Returns if the given ByValueContentsMarshalKind is supported in the current marshalling context. - /// A supported marshal kind has a different behavior than the default behavior. - /// - /// The marshal kind. - /// The marshalling context. - /// - bool SupportsByValueMarshalKind(ByValueContentsMarshalKind marshalKind, StubCodeContext context); - } - - /// - /// Interface for generating attributes for native return types. - /// - internal interface IAttributedReturnTypeMarshallingGenerator : IMarshallingGenerator - { - /// - /// Gets any attributes that should be applied to the return type for this . - /// - /// Object to marshal - /// Attributes for the return type for this , or null if no attributes should be added. - AttributeListSyntax? GenerateAttributesForReturnType(TypePositionInfo info); - } - - /// - /// Exception used to indicate marshalling isn't supported. - /// - internal class MarshallingNotSupportedException : Exception - { - /// - /// Construct a new instance. - /// - /// instance - /// instance - public MarshallingNotSupportedException(TypePositionInfo info, StubCodeContext context) - { - this.TypePositionInfo = info; - this.StubCodeContext = context; - } - - /// - /// Type that is being marshalled. - /// - public TypePositionInfo TypePositionInfo { get; private init; } - - /// - /// Context in which the marshalling is taking place. - /// - public StubCodeContext StubCodeContext { get; private init; } - - /// - /// [Optional] Specific reason marshalling of the supplied type isn't supported. - /// - public string? NotSupportedDetails { get; init; } - } - - internal class MarshallingGenerators - { - public static readonly ByteBoolMarshaller ByteBool = new ByteBoolMarshaller(); - public static readonly WinBoolMarshaller WinBool = new WinBoolMarshaller(); - public static readonly VariantBoolMarshaller VariantBool = new VariantBoolMarshaller(); - - public static readonly Utf16CharMarshaller Utf16Char = new Utf16CharMarshaller(); - public static readonly Utf16StringMarshaller Utf16String = new Utf16StringMarshaller(); - public static readonly Utf8StringMarshaller Utf8String = new Utf8StringMarshaller(); - public static readonly AnsiStringMarshaller AnsiString = new AnsiStringMarshaller(Utf8String); - public static readonly PlatformDefinedStringMarshaller PlatformDefinedString = new PlatformDefinedStringMarshaller(Utf16String, Utf8String); - - public static readonly Forwarder Forwarder = new Forwarder(); - public static readonly BlittableMarshaller Blittable = new BlittableMarshaller(); - public static readonly DelegateMarshaller Delegate = new DelegateMarshaller(); - public static readonly HResultExceptionMarshaller HResultException = new HResultExceptionMarshaller(); - - /// - /// Create an instance for marshalling the supplied type in the given position. - /// - /// Type details - /// Metadata about the stub the type is associated with - /// A instance. - public static IMarshallingGenerator Create( - TypePositionInfo info, - StubCodeContext context, - AnalyzerConfigOptions options) - { - return ValidateByValueMarshalKind(context, info, CreateCore(info, context, options)); - } - - private static IMarshallingGenerator ValidateByValueMarshalKind(StubCodeContext context, TypePositionInfo info, IMarshallingGenerator generator) - { - if (info.IsByRef && info.ByValueContentsMarshalKind != ByValueContentsMarshalKind.Default) - { - throw new MarshallingNotSupportedException(info, context) - { - NotSupportedDetails = Resources.InOutAttributeByRefNotSupported - }; - } - else if (info.ByValueContentsMarshalKind == ByValueContentsMarshalKind.In) - { - throw new MarshallingNotSupportedException(info, context) - { - NotSupportedDetails = Resources.InAttributeNotSupportedWithoutOut - }; - } - else if (info.ByValueContentsMarshalKind != ByValueContentsMarshalKind.Default - && !generator.SupportsByValueMarshalKind(info.ByValueContentsMarshalKind, context)) - { - throw new MarshallingNotSupportedException(info, context) - { - NotSupportedDetails = Resources.InOutAttributeMarshalerNotSupported - }; - } - return generator; - } - - /// - /// Create an instance to marshalling the supplied type. - /// - /// Type details - /// Metadata about the stub the type is associated with - /// A instance. - private static IMarshallingGenerator CreateCore( - TypePositionInfo info, - StubCodeContext context, - AnalyzerConfigOptions options) - { - if (options.GenerateForwarders()) - { - return MarshallingGenerators.Forwarder; - } - - if (info.IsNativeReturnPosition && !info.IsManagedReturnPosition) - { - // Use marshaller for native HRESULT return / exception throwing - System.Diagnostics.Debug.Assert(info.ManagedType is SpecialTypeInfo { SpecialType: SpecialType.System_Int32 }); - return HResultException; - } - - switch (info) - { - // Blittable primitives with no marshalling info or with a compatible [MarshalAs] attribute. - case { ManagedType: SpecialTypeInfo { SpecialType: SpecialType.System_SByte }, MarshallingAttributeInfo: NoMarshallingInfo or MarshalAsInfo(UnmanagedType.I1, _) } - or { ManagedType: SpecialTypeInfo { SpecialType: SpecialType.System_Byte }, MarshallingAttributeInfo: NoMarshallingInfo or MarshalAsInfo(UnmanagedType.U1, _) } - or { ManagedType: SpecialTypeInfo { SpecialType: SpecialType.System_Int16 }, MarshallingAttributeInfo: NoMarshallingInfo or MarshalAsInfo(UnmanagedType.I2, _) } - or { ManagedType: SpecialTypeInfo { SpecialType: SpecialType.System_UInt16 }, MarshallingAttributeInfo: NoMarshallingInfo or MarshalAsInfo(UnmanagedType.U2, _) } - or { ManagedType: SpecialTypeInfo { SpecialType: SpecialType.System_Int32 }, MarshallingAttributeInfo: NoMarshallingInfo or MarshalAsInfo(UnmanagedType.I4, _) } - or { ManagedType: SpecialTypeInfo { SpecialType: SpecialType.System_UInt32 }, MarshallingAttributeInfo: NoMarshallingInfo or MarshalAsInfo(UnmanagedType.U4, _) } - or { ManagedType: SpecialTypeInfo { SpecialType: SpecialType.System_Int64 }, MarshallingAttributeInfo: NoMarshallingInfo or MarshalAsInfo(UnmanagedType.I8, _) } - or { ManagedType: SpecialTypeInfo { SpecialType: SpecialType.System_UInt64 }, MarshallingAttributeInfo: NoMarshallingInfo or MarshalAsInfo(UnmanagedType.U8, _) } - or { ManagedType: SpecialTypeInfo { SpecialType: SpecialType.System_IntPtr }, MarshallingAttributeInfo: NoMarshallingInfo or MarshalAsInfo(UnmanagedType.SysInt, _) } - or { ManagedType: SpecialTypeInfo { SpecialType: SpecialType.System_UIntPtr }, MarshallingAttributeInfo: NoMarshallingInfo or MarshalAsInfo(UnmanagedType.SysUInt, _) } - or { ManagedType: SpecialTypeInfo { SpecialType: SpecialType.System_Single }, MarshallingAttributeInfo: NoMarshallingInfo or MarshalAsInfo(UnmanagedType.R4, _) } - or { ManagedType: SpecialTypeInfo { SpecialType: SpecialType.System_Double }, MarshallingAttributeInfo: NoMarshallingInfo or MarshalAsInfo(UnmanagedType.R8, _) }: - return Blittable; - - // Enum with no marshalling info - case { ManagedType: EnumTypeInfo enumType, MarshallingAttributeInfo: NoMarshallingInfo }: - // Check that the underlying type is not bool or char. C# does not allow this, but ECMA-335 does. - var underlyingSpecialType = enumType.UnderlyingType; - if (underlyingSpecialType == SpecialType.System_Boolean || underlyingSpecialType == SpecialType.System_Char) - { - throw new MarshallingNotSupportedException(info, context); - } - return Blittable; - - // Pointer with no marshalling info - case { ManagedType: PointerTypeInfo(_, _, IsFunctionPointer:false), MarshallingAttributeInfo: NoMarshallingInfo }: - return Blittable; - - // Function pointer with no marshalling info - case { ManagedType: PointerTypeInfo(_, _, IsFunctionPointer: true), MarshallingAttributeInfo: NoMarshallingInfo or MarshalAsInfo(UnmanagedType.FunctionPtr, _) }: - return Blittable; - - case { ManagedType: SpecialTypeInfo { SpecialType: SpecialType.System_Boolean }, MarshallingAttributeInfo: NoMarshallingInfo }: - return WinBool; // [Compat] Matching the default for the built-in runtime marshallers. - case { ManagedType: SpecialTypeInfo { SpecialType: SpecialType.System_Boolean }, MarshallingAttributeInfo: MarshalAsInfo(UnmanagedType.I1 or UnmanagedType.U1, _) }: - return ByteBool; - case { ManagedType: SpecialTypeInfo { SpecialType: SpecialType.System_Boolean }, MarshallingAttributeInfo: MarshalAsInfo(UnmanagedType.I4 or UnmanagedType.U4 or UnmanagedType.Bool, _) }: - return WinBool; - case { ManagedType: SpecialTypeInfo { SpecialType: SpecialType.System_Boolean }, MarshallingAttributeInfo: MarshalAsInfo(UnmanagedType.VariantBool, _) }: - return VariantBool; - - case { ManagedType: DelegateTypeInfo, MarshallingAttributeInfo: NoMarshallingInfo or MarshalAsInfo(UnmanagedType.FunctionPtr, _) }: - return Delegate; - - case { MarshallingAttributeInfo: SafeHandleMarshallingInfo(_, bool isAbstract) }: - if (!context.AdditionalTemporaryStateLivesAcrossStages) - { - throw new MarshallingNotSupportedException(info, context); - } - if (info.IsByRef && isAbstract) - { - throw new MarshallingNotSupportedException(info, context) - { - NotSupportedDetails = Resources.SafeHandleByRefMustBeConcrete - }; - } - return new SafeHandleMarshaller(options); - - // Marshalling in new model. - // Must go before the cases that do not explicitly check for marshalling info to support - // the user overridding the default marshalling rules with a MarshalUsing attribute. - case { MarshallingAttributeInfo: NativeMarshallingAttributeInfo marshalInfo }: - return CreateCustomNativeTypeMarshaller(info, context, marshalInfo, options); - - case { MarshallingAttributeInfo: BlittableTypeAttributeInfo }: - return Blittable; - - // Simple generated marshalling with new attribute model, only have type name. - case { MarshallingAttributeInfo: GeneratedNativeMarshallingAttributeInfo(string nativeTypeName) }: - return Forwarder; - - // Cases that just match on type must come after the checks that match only on marshalling attribute info. - // The checks below do not account for generic marshalling overrides like [MarshalUsing], so those checks must come first. - case { ManagedType: SpecialTypeInfo { SpecialType: SpecialType.System_Char } }: - return CreateCharMarshaller(info, context); - - case { ManagedType: SpecialTypeInfo { SpecialType: SpecialType.System_String } }: - return CreateStringMarshaller(info, context); - - case { ManagedType: SpecialTypeInfo { SpecialType: SpecialType.System_Void } }: - return Forwarder; - - default: - throw new MarshallingNotSupportedException(info, context); - } - } - - private static IMarshallingGenerator CreateCharMarshaller(TypePositionInfo info, StubCodeContext context) - { - MarshallingInfo marshalInfo = info.MarshallingAttributeInfo; - if (marshalInfo is NoMarshallingInfo) - { - // [Compat] Require explicit marshalling information. - throw new MarshallingNotSupportedException(info, context) - { - NotSupportedDetails = Resources.MarshallingStringOrCharAsUndefinedNotSupported - }; - } - - // Explicit MarshalAs takes precedence over string encoding info - if (marshalInfo is MarshalAsInfo marshalAsInfo) - { - switch (marshalAsInfo.UnmanagedType) - { - case UnmanagedType.I2: - case UnmanagedType.U2: - return Utf16Char; - } - } - else if (marshalInfo is MarshallingInfoStringSupport marshalStringInfo) - { - switch (marshalStringInfo.CharEncoding) - { - case CharEncoding.Utf16: - return Utf16Char; - case CharEncoding.Ansi: - throw new MarshallingNotSupportedException(info, context) // [Compat] ANSI is not supported for char - { - NotSupportedDetails = string.Format(Resources.MarshallingCharAsSpecifiedCharSetNotSupported, CharSet.Ansi) - }; - case CharEncoding.PlatformDefined: - throw new MarshallingNotSupportedException(info, context) // [Compat] See conversion of CharSet.Auto. - { - NotSupportedDetails = string.Format(Resources.MarshallingCharAsSpecifiedCharSetNotSupported, CharSet.Auto) - }; - } - } - - throw new MarshallingNotSupportedException(info, context); - } - - private static IMarshallingGenerator CreateStringMarshaller(TypePositionInfo info, StubCodeContext context) - { - MarshallingInfo marshalInfo = info.MarshallingAttributeInfo; - if (marshalInfo is NoMarshallingInfo) - { - // [Compat] Require explicit marshalling information. - throw new MarshallingNotSupportedException(info, context) - { - NotSupportedDetails = Resources.MarshallingStringOrCharAsUndefinedNotSupported - }; - } - - // Explicit MarshalAs takes precedence over string encoding info - if (marshalInfo is MarshalAsInfo marshalAsInfo) - { - switch (marshalAsInfo.UnmanagedType) - { - case UnmanagedType.LPStr: - return AnsiString; - case UnmanagedType.LPTStr: - case UnmanagedType.LPWStr: - return Utf16String; - case (UnmanagedType)0x30:// UnmanagedType.LPUTF8Str - return Utf8String; - } - } - else if (marshalInfo is MarshallingInfoStringSupport marshalStringInfo) - { - switch (marshalStringInfo.CharEncoding) - { - case CharEncoding.Ansi: - return AnsiString; - case CharEncoding.Utf16: - return Utf16String; - case CharEncoding.Utf8: - return Utf8String; - case CharEncoding.PlatformDefined: - return PlatformDefinedString; - } - } - - throw new MarshallingNotSupportedException(info, context); - } - - private static ExpressionSyntax GetNumElementsExpressionFromMarshallingInfo(TypePositionInfo info, CountInfo count, StubCodeContext context) - { - return count switch - { - SizeAndParamIndexInfo(int size, SizeAndParamIndexInfo.UnspecifiedParam) => GetConstSizeExpression(size), - ConstSizeCountInfo(int size) => GetConstSizeExpression(size), - SizeAndParamIndexInfo(SizeAndParamIndexInfo.UnspecifiedConstSize, TypePositionInfo param) => CheckedExpression(SyntaxKind.CheckedExpression, GetExpressionForParam(param)), - SizeAndParamIndexInfo(int size, TypePositionInfo param) => CheckedExpression(SyntaxKind.CheckedExpression, BinaryExpression(SyntaxKind.AddExpression, GetConstSizeExpression(size), GetExpressionForParam(param))), - CountElementCountInfo(TypePositionInfo elementInfo) => CheckedExpression(SyntaxKind.CheckedExpression, GetExpressionForParam(elementInfo)), - _ => throw new MarshallingNotSupportedException(info, context) - { - NotSupportedDetails = Resources.ArraySizeMustBeSpecified - }, - }; - - static LiteralExpressionSyntax GetConstSizeExpression(int size) - { - return LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(size)); - } - - ExpressionSyntax GetExpressionForParam(TypePositionInfo paramInfo) - { - ExpressionSyntax numElementsExpression = GetIndexedNumElementsExpression( - context, - paramInfo, - out int numIndirectionLevels); - - ManagedTypeInfo type = paramInfo.ManagedType; - MarshallingInfo marshallingInfo = paramInfo.MarshallingAttributeInfo; - - for (int i = 0; i < numIndirectionLevels; i++) - { - if (marshallingInfo is NativeContiguousCollectionMarshallingInfo collectionInfo) - { - type = collectionInfo.ElementType; - marshallingInfo = collectionInfo.ElementMarshallingInfo; - } - else - { - throw new MarshallingNotSupportedException(info, context) - { - NotSupportedDetails = Resources.CollectionSizeParamTypeMustBeIntegral - }; - } - } - - if (type is not SpecialTypeInfo specialType || !specialType.SpecialType.IsIntegralType()) - { - throw new MarshallingNotSupportedException(info, context) - { - NotSupportedDetails = Resources.CollectionSizeParamTypeMustBeIntegral - }; - } - - return CastExpression( - PredefinedType(Token(SyntaxKind.IntKeyword)), - ParenthesizedExpression(numElementsExpression)); - } - - static ExpressionSyntax GetIndexedNumElementsExpression(StubCodeContext context, TypePositionInfo numElementsInfo, out int numIndirectionLevels) - { - Stack indexerStack = new(); - - StubCodeContext? currentContext = context; - StubCodeContext lastContext = null!; - - while (currentContext is not null) - { - if (currentContext is ContiguousCollectionElementMarshallingCodeContext collectionContext) - { - indexerStack.Push(collectionContext.IndexerIdentifier); - } - lastContext = currentContext; - currentContext = currentContext.ParentContext; - } - - numIndirectionLevels = indexerStack.Count; - - ExpressionSyntax indexedNumElements = IdentifierName(lastContext.GetIdentifiers(numElementsInfo).managed); - while (indexerStack.Count > 0) - { - NameSyntax indexer = IdentifierName(indexerStack.Pop()); - indexedNumElements = ElementAccessExpression(indexedNumElements) - .AddArgumentListArguments(Argument(indexer)); - } - - return indexedNumElements; - } - } - - private static IMarshallingGenerator CreateCustomNativeTypeMarshaller(TypePositionInfo info, StubCodeContext context, NativeMarshallingAttributeInfo marshalInfo, AnalyzerConfigOptions options) - { - ValidateCustomNativeTypeMarshallingSupported(info, context, marshalInfo); - - ICustomNativeTypeMarshallingStrategy marshallingStrategy = new SimpleCustomNativeTypeMarshalling(marshalInfo.NativeMarshallingType.Syntax); - - if ((marshalInfo.MarshallingFeatures & CustomMarshallingFeatures.ManagedToNativeStackalloc) != 0) - { - marshallingStrategy = new StackallocOptimizationMarshalling(marshallingStrategy); - } - - if ((marshalInfo.MarshallingFeatures & CustomMarshallingFeatures.FreeNativeResources) != 0) - { - marshallingStrategy = new FreeNativeCleanupStrategy(marshallingStrategy); - } - - // Collections have extra configuration, so handle them here. - if (marshalInfo is NativeContiguousCollectionMarshallingInfo collectionMarshallingInfo) - { - return CreateNativeCollectionMarshaller(info, context, collectionMarshallingInfo, options, marshallingStrategy); - } - - if (marshalInfo.ValuePropertyType is not null) - { - marshallingStrategy = DecorateWithValuePropertyStrategy(marshalInfo, marshallingStrategy); - } - - IMarshallingGenerator marshallingGenerator = new CustomNativeTypeMarshallingGenerator(marshallingStrategy, enableByValueContentsMarshalling: false); - - if ((marshalInfo.MarshallingFeatures & CustomMarshallingFeatures.ManagedTypePinning) != 0) - { - return new PinnableManagedValueMarshaller(marshallingGenerator); - } - - return marshallingGenerator; - } - - private static void ValidateCustomNativeTypeMarshallingSupported(TypePositionInfo info, StubCodeContext context, NativeMarshallingAttributeInfo marshalInfo) - { - // 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) - { - throw new MarshallingNotSupportedException(info, context) - { - NotSupportedDetails = string.Format(Resources.CustomTypeMarshallingNativeToManagedUnsupported, marshalInfo.NativeMarshallingType.FullTypeName) - }; - } - // 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)) - { - throw new MarshallingNotSupportedException(info, context) - { - NotSupportedDetails = string.Format(Resources.CustomTypeMarshallingManagedToNativeUnsupported, marshalInfo.NativeMarshallingType.FullTypeName) - }; - } - // 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. - // "in" byref supports stack marshalling. - else if (info.RefKind == RefKind.In - && (marshalInfo.MarshallingFeatures & CustomMarshallingFeatures.ManagedToNative) == 0 - && !(context.SingleFrameSpansNativeContext && (marshalInfo.MarshallingFeatures & CustomMarshallingFeatures.ManagedToNativeStackalloc) != 0)) - { - throw new MarshallingNotSupportedException(info, context) - { - NotSupportedDetails = string.Format(Resources.CustomTypeMarshallingManagedToNativeUnsupported, marshalInfo.NativeMarshallingType.FullTypeName) - }; - } - // 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 - else if (info.RefKind == RefKind.Ref - && (marshalInfo.MarshallingFeatures & CustomMarshallingFeatures.ManagedToNative) == 0) - { - throw new MarshallingNotSupportedException(info, context) - { - NotSupportedDetails = string.Format(Resources.CustomTypeMarshallingManagedToNativeUnsupported, marshalInfo.NativeMarshallingType.FullTypeName) - }; - } - } - - private static ICustomNativeTypeMarshallingStrategy DecorateWithValuePropertyStrategy(NativeMarshallingAttributeInfo marshalInfo, ICustomNativeTypeMarshallingStrategy nativeTypeMarshaller) - { - TypeSyntax valuePropertyTypeSyntax = marshalInfo.ValuePropertyType!.Syntax; - - if ((marshalInfo.MarshallingFeatures & CustomMarshallingFeatures.NativeTypePinning) != 0) - { - return new PinnableMarshallerTypeMarshalling(nativeTypeMarshaller, valuePropertyTypeSyntax); - } - - return new CustomNativeTypeWithValuePropertyMarshalling(nativeTypeMarshaller, valuePropertyTypeSyntax); - } - - private static IMarshallingGenerator CreateNativeCollectionMarshaller( - TypePositionInfo info, - StubCodeContext context, - NativeContiguousCollectionMarshallingInfo collectionInfo, - AnalyzerConfigOptions options, - ICustomNativeTypeMarshallingStrategy marshallingStrategy) - { - var elementInfo = new TypePositionInfo(collectionInfo.ElementType, collectionInfo.ElementMarshallingInfo) { ManagedIndex = info.ManagedIndex }; - var elementMarshaller = Create( - elementInfo, - new ContiguousCollectionElementMarshallingCodeContext(StubCodeContext.Stage.Setup, string.Empty, context), - options); - var elementType = elementMarshaller.AsNativeType(elementInfo); - - bool isBlittable = elementMarshaller == Blittable; - - if (isBlittable) - { - marshallingStrategy = new ContiguousBlittableElementCollectionMarshalling(marshallingStrategy, collectionInfo.ElementType.Syntax); - } - else - { - marshallingStrategy = new ContiguousNonBlittableElementCollectionMarshalling(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. - if (collectionInfo.ValuePropertyType 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, - numElementsExpression, - SizeOfExpression(elementType)); - - if (collectionInfo.UseDefaultMarshalling && info.ManagedType is SzArrayType) - { - return new ArrayMarshaller( - new CustomNativeTypeMarshallingGenerator(marshallingStrategy, enableByValueContentsMarshalling: true), - elementType, - isBlittable, - options); - } - - IMarshallingGenerator marshallingGenerator = new CustomNativeTypeMarshallingGenerator(marshallingStrategy, enableByValueContentsMarshalling: false); - - if ((collectionInfo.MarshallingFeatures & CustomMarshallingFeatures.ManagedTypePinning) != 0) - { - return new PinnableManagedValueMarshaller(marshallingGenerator); - } - - return marshallingGenerator; - } - } -} diff --git a/DllImportGenerator/DllImportGenerator/NoPreserveSigMarshallingGeneratorFactory.cs b/DllImportGenerator/DllImportGenerator/NoPreserveSigMarshallingGeneratorFactory.cs new file mode 100644 index 000000000000..1e8ae0159e45 --- /dev/null +++ b/DllImportGenerator/DllImportGenerator/NoPreserveSigMarshallingGeneratorFactory.cs @@ -0,0 +1,30 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.Interop +{ + class NoPreserveSigMarshallingGeneratorFactory : IMarshallingGeneratorFactory + { + private static readonly HResultExceptionMarshaller HResultException = new HResultExceptionMarshaller(); + private readonly IMarshallingGeneratorFactory inner; + + public NoPreserveSigMarshallingGeneratorFactory(IMarshallingGeneratorFactory inner) + { + this.inner = inner; + } + + public IMarshallingGenerator Create(TypePositionInfo info, StubCodeContext context) + { + if (info.IsNativeReturnPosition && !info.IsManagedReturnPosition) + { + // Use marshaller for native HRESULT return / exception throwing + System.Diagnostics.Debug.Assert(info.ManagedType.Equals(SpecialTypeInfo.Int32)); + return HResultException; + } + return inner.Create(info, context); + } + } +} diff --git a/DllImportGenerator/DllImportGenerator/StubCodeGenerator.cs b/DllImportGenerator/DllImportGenerator/PInvokeStubCodeGenerator.cs similarity index 71% rename from DllImportGenerator/DllImportGenerator/StubCodeGenerator.cs rename to DllImportGenerator/DllImportGenerator/PInvokeStubCodeGenerator.cs index 2732414adc42..a7ecd31f8e45 100644 --- a/DllImportGenerator/DllImportGenerator/StubCodeGenerator.cs +++ b/DllImportGenerator/DllImportGenerator/PInvokeStubCodeGenerator.cs @@ -7,13 +7,26 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Diagnostics; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; using static Microsoft.Interop.StubCodeContext; namespace Microsoft.Interop { - internal sealed class StubCodeGenerator : StubCodeContext + /// + /// Base code generator for generating the body of a source-generated P/Invoke and providing customization for how to invoke/define the native method. + /// + /// + /// This type enables multiple code generators for P/Invoke-style marshalling + /// to reuse the same basic method body, but with different designs of how to emit the target native method. + /// This enables users to write code generators that work with slightly different semantics. + /// For example, the source generator for [GeneratedDllImport] emits the target P/Invoke as + /// a local function inside the generated stub body. + /// However, other managed-to-native code generators using a P/Invoke style might want to define + /// the target DllImport outside of the stub as a static non-local function or as a function pointer field. + /// This refactoring allows the code generator to have control over where the target method is declared + /// and how it is declared. + /// + internal sealed class PInvokeStubCodeGenerator : StubCodeContext { private record struct BoundGenerator(TypePositionInfo TypeInfo, IMarshallingGenerator Generator); @@ -39,21 +52,19 @@ private record struct BoundGenerator(TypePositionInfo TypeInfo, IMarshallingGene // Error code representing success. This maps to S_OK for Windows HRESULT semantics and 0 for POSIX errno semantics. private const int SuccessErrorCode = 0; - private readonly AnalyzerConfigOptions options; - private readonly GeneratedDllImportData dllImportData; + private readonly bool setLastError; private readonly List paramMarshallers; private readonly BoundGenerator retMarshaller; private readonly List sortedMarshallers; private readonly bool stubReturnsVoid; - public StubCodeGenerator( - GeneratedDllImportData dllImportData, + public PInvokeStubCodeGenerator( IEnumerable argTypes, - AnalyzerConfigOptions options, - Action marshallingNotSupportedCallback) + bool setLastError, + Action marshallingNotSupportedCallback, + IMarshallingGeneratorFactory generatorFactory) { - this.dllImportData = dllImportData; - this.options = options; + this.setLastError = setLastError; List allMarshallers = new(); List paramMarshallers = new(); @@ -156,12 +167,12 @@ BoundGenerator CreateGenerator(TypePositionInfo p) { try { - return new BoundGenerator(p, MarshallingGenerators.Create(p, this, options)); + return new BoundGenerator(p, generatorFactory.Create(p, this)); } catch (MarshallingNotSupportedException e) { marshallingNotSupportedCallback(p, e); - return new BoundGenerator(p, MarshallingGenerators.Forwarder); + return new BoundGenerator(p, new Forwarder()); } } } @@ -196,9 +207,8 @@ public override (string managed, string native) GetIdentifiers(TypePositionInfo } } - public BlockSyntax GenerateBody(string methodName, AttributeListSyntax? forwardedAttributes) + public BlockSyntax GeneratePInvokeBody(string dllImportName) { - string dllImportName = methodName + "__PInvoke__"; var setupStatements = new List(); foreach (var marshaller in paramMarshallers) @@ -228,9 +238,6 @@ public BlockSyntax GenerateBody(string methodName, AttributeListSyntax? forwarde // Stub return is not the same as invoke return if (!stubReturnsVoid && !retMarshaller.TypeInfo.IsManagedReturnPosition) { - // Should only happen when PreserveSig=false - Debug.Assert(!dllImportData.PreserveSig, "Expected PreserveSig=false when invoke return is not the stub return"); - // Stub return should be the last parameter for the invoke Debug.Assert(paramMarshallers.Any() && paramMarshallers.Last().TypeInfo.IsManagedReturnPosition, "Expected stub return to be the last parameter for the invoke"); @@ -248,7 +255,7 @@ public BlockSyntax GenerateBody(string methodName, AttributeListSyntax? forwarde // Do not manually handle SetLastError when generating forwarders. // We want the runtime to handle everything. - if (this.dllImportData.SetLastError && !options.GenerateForwarders()) + if (this.setLastError) { // Declare variable for last error setupStatements.Add(MarshallerHelpers.DeclareWithDefault( @@ -294,7 +301,7 @@ public BlockSyntax GenerateBody(string methodName, AttributeListSyntax? forwarde allStatements.AddRange(tryStatements); } - if (this.dllImportData.SetLastError && !options.GenerateForwarders()) + if (this.setLastError) { // Marshal.SetLastPInvokeError(); allStatements.Add(ExpressionStatement( @@ -312,40 +319,7 @@ public BlockSyntax GenerateBody(string methodName, AttributeListSyntax? forwarde allStatements.Add(ReturnStatement(IdentifierName(ReturnIdentifier))); // Wrap all statements in an unsafe block - var codeBlock = Block(UnsafeStatement(Block(allStatements))); - - // Define P/Invoke declaration - var dllImport = LocalFunctionStatement(retMarshaller.Generator.AsNativeType(retMarshaller.TypeInfo), dllImportName) - .AddModifiers( - Token(SyntaxKind.ExternKeyword), - Token(SyntaxKind.StaticKeyword), - Token(SyntaxKind.UnsafeKeyword)) - .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)) - .WithAttributeLists( - SingletonList(AttributeList( - SingletonSeparatedList(CreateDllImportAttributeForTarget(GetTargetDllImportDataFromStubData(methodName)))))); - - if (retMarshaller.Generator is IAttributedReturnTypeMarshallingGenerator retGenerator) - { - AttributeListSyntax? returnAttribute = retGenerator.GenerateAttributesForReturnType(retMarshaller.TypeInfo); - if (returnAttribute is not null) - { - dllImport = dllImport.AddAttributeLists(returnAttribute.WithTarget(AttributeTargetSpecifier(Identifier("return")))); - } - } - - if (forwardedAttributes is not null) - { - dllImport = dllImport.AddAttributeLists(forwardedAttributes); - } - - foreach (var marshaller in paramMarshallers) - { - ParameterSyntax paramSyntax = marshaller.Generator.AsParameter(marshaller.TypeInfo); - dllImport = dllImport.AddParameterListParameters(paramSyntax); - } - - return codeBlock.AddStatements(dllImport); + return Block(UnsafeStatement(Block(allStatements))); void GenerateStatementsForStage(Stage stage, List statementsToUpdate) { @@ -435,7 +409,7 @@ void GenerateStatementsForInvoke(List statementsToUpdate, Invoc // Do not manually handle SetLastError when generating forwarders. // We want the runtime to handle everything. - if (this.dllImportData.SetLastError && !options.GenerateForwarders()) + if (setLastError) { // Marshal.SetLastSystemError(0); var clearLastError = ExpressionStatement( @@ -482,6 +456,19 @@ void GenerateStatementsForInvoke(List statementsToUpdate, Invoc } } + public (ParameterListSyntax ParameterList, TypeSyntax ReturnType, AttributeListSyntax? ReturnTypeAttributes) GenerateTargetMethodSignatureData() + { + return ( + ParameterList( + SeparatedList( + paramMarshallers.Select(marshaler => marshaler.Generator.AsParameter(marshaler.TypeInfo)))), + retMarshaller.Generator.AsNativeType(retMarshaller.TypeInfo), + retMarshaller.Generator is IAttributedReturnTypeMarshallingGenerator attributedReturn + ? attributedReturn.GenerateAttributesForReturnType(retMarshaller.TypeInfo) + : null + ); + } + private void AppendVariableDeclations(List statementsToUpdate, TypePositionInfo info, IMarshallingGenerator generator) { var (managed, native) = this.GetIdentifiers(info); @@ -502,123 +489,5 @@ private void AppendVariableDeclations(List statementsToUpdate, native)); } } - - private static AttributeSyntax CreateDllImportAttributeForTarget(GeneratedDllImportData targetDllImportData) - { - Debug.Assert(targetDllImportData.EntryPoint is not null); - var newAttributeArgs = new List - { - AttributeArgument(LiteralExpression( - SyntaxKind.StringLiteralExpression, - Literal(targetDllImportData.ModuleName))), - AttributeArgument( - NameEquals(nameof(DllImportAttribute.EntryPoint)), - null, - CreateStringExpressionSyntax(targetDllImportData.EntryPoint!)) - }; - - if (targetDllImportData.IsUserDefined.HasFlag(DllImportMember.BestFitMapping)) - { - var name = NameEquals(nameof(DllImportAttribute.BestFitMapping)); - var value = CreateBoolExpressionSyntax(targetDllImportData.BestFitMapping); - newAttributeArgs.Add(AttributeArgument(name, null, value)); - } - if (targetDllImportData.IsUserDefined.HasFlag(DllImportMember.CallingConvention)) - { - var name = NameEquals(nameof(DllImportAttribute.CallingConvention)); - var value = CreateEnumExpressionSyntax(targetDllImportData.CallingConvention); - newAttributeArgs.Add(AttributeArgument(name, null, value)); - } - if (targetDllImportData.IsUserDefined.HasFlag(DllImportMember.CharSet)) - { - var name = NameEquals(nameof(DllImportAttribute.CharSet)); - var value = CreateEnumExpressionSyntax(targetDllImportData.CharSet); - newAttributeArgs.Add(AttributeArgument(name, null, value)); - } - if (targetDllImportData.IsUserDefined.HasFlag(DllImportMember.ExactSpelling)) - { - var name = NameEquals(nameof(DllImportAttribute.ExactSpelling)); - var value = CreateBoolExpressionSyntax(targetDllImportData.ExactSpelling); - newAttributeArgs.Add(AttributeArgument(name, null, value)); - } - if (targetDllImportData.IsUserDefined.HasFlag(DllImportMember.PreserveSig)) - { - var name = NameEquals(nameof(DllImportAttribute.PreserveSig)); - var value = CreateBoolExpressionSyntax(targetDllImportData.PreserveSig); - newAttributeArgs.Add(AttributeArgument(name, null, value)); - } - if (targetDllImportData.IsUserDefined.HasFlag(DllImportMember.SetLastError)) - { - var name = NameEquals(nameof(DllImportAttribute.SetLastError)); - var value = CreateBoolExpressionSyntax(targetDllImportData.SetLastError); - newAttributeArgs.Add(AttributeArgument(name, null, value)); - } - if (targetDllImportData.IsUserDefined.HasFlag(DllImportMember.ThrowOnUnmappableChar)) - { - var name = NameEquals(nameof(DllImportAttribute.ThrowOnUnmappableChar)); - var value = CreateBoolExpressionSyntax(targetDllImportData.ThrowOnUnmappableChar); - newAttributeArgs.Add(AttributeArgument(name, null, value)); - } - - // Create new attribute - return Attribute( - ParseName(typeof(DllImportAttribute).FullName), - AttributeArgumentList(SeparatedList(newAttributeArgs))); - - static ExpressionSyntax CreateBoolExpressionSyntax(bool trueOrFalse) - { - return LiteralExpression( - trueOrFalse - ? SyntaxKind.TrueLiteralExpression - : SyntaxKind.FalseLiteralExpression); - } - - static ExpressionSyntax CreateStringExpressionSyntax(string str) - { - return LiteralExpression( - SyntaxKind.StringLiteralExpression, - Literal(str)); - } - - static ExpressionSyntax CreateEnumExpressionSyntax(T value) where T : Enum - { - return MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - IdentifierName(typeof(T).FullName), - IdentifierName(value.ToString())); - } - } - - GeneratedDllImportData GetTargetDllImportDataFromStubData(string methodName) - { - DllImportMember membersToForward = DllImportMember.All - // https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.dllimportattribute.preservesig - // If PreserveSig=false (default is true), the P/Invoke stub checks/converts a returned HRESULT to an exception. - & ~DllImportMember.PreserveSig - // https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.dllimportattribute.setlasterror - // If SetLastError=true (default is false), the P/Invoke stub gets/caches the last error after invoking the native function. - & ~DllImportMember.SetLastError; - if (options.GenerateForwarders()) - { - membersToForward = DllImportMember.All; - } - - var targetDllImportData = dllImportData with - { - IsUserDefined = dllImportData.IsUserDefined & membersToForward - }; - - // If the EntryPoint property is not set, we will compute and - // add it based on existing semantics (i.e. method name). - // - // N.B. The export discovery logic is identical regardless of where - // the name is defined (i.e. method name vs EntryPoint property). - if (!targetDllImportData.IsUserDefined.HasFlag(DllImportMember.EntryPoint)) - { - targetDllImportData = targetDllImportData with { EntryPoint = methodName }; - } - - return targetDllImportData; - } } } diff --git a/DllImportGenerator/DllImportGenerator/Resources.Designer.cs b/DllImportGenerator/DllImportGenerator/Resources.Designer.cs index d43d6c82b3bd..eb6bfa6e52e0 100644 --- a/DllImportGenerator/DllImportGenerator/Resources.Designer.cs +++ b/DllImportGenerator/DllImportGenerator/Resources.Designer.cs @@ -19,7 +19,7 @@ namespace Microsoft.Interop { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resources { @@ -60,15 +60,6 @@ internal Resources() { } } - /// - /// Looks up a localized string similar to Marshalling an array from unmanaged to managed requires either the 'SizeParamIndex' or 'SizeConst' fields to be set on a 'MarshalAsAttribute' or the 'ConstantElementCount' or 'CountElementName' properties to be set on a 'MarshalUsingAttribute'.. - /// - internal static string ArraySizeMustBeSpecified { - get { - return ResourceManager.GetString("ArraySizeMustBeSpecified", resourceCulture); - } - } - /// /// Looks up a localized string similar to A type marked with 'BlittableTypeAttribute' must be blittable.. /// @@ -123,15 +114,6 @@ internal static string CollectionNativeTypeMustHaveRequiredShapeMessage { } } - /// - /// Looks up a localized string similar to The specified collection size parameter for an collection must be an integer type. If the size information is applied to a nested collection, the size parameter must be a collection of one less level of nesting with an integral element.. - /// - internal static string CollectionSizeParamTypeMustBeIntegral { - get { - return ResourceManager.GetString("CollectionSizeParamTypeMustBeIntegral", resourceCulture); - } - } - /// /// Looks up a localized string similar to Source-generated P/Invokes will ignore any configuration that is not supported.. /// @@ -151,7 +133,7 @@ internal static string ConfigurationNotSupportedMessage { } /// - /// Looks up a localized string similar to The specified marshalling configuration is not supported by source-generated P/Invokes. {0}. + /// Looks up a localized string similar to The specified marshalling configuration is not supported by source-generated P/Invokes. {0}.. /// internal static string ConfigurationNotSupportedMessageMarshallingInfo { get { @@ -276,42 +258,6 @@ internal static string CustomTypeMarshallingNativeToManagedUnsupported { } } - /// - /// Looks up a localized string similar to This element cannot depend on '{0}' for collection size information without creating a dependency cycle. - /// - internal static string CyclicalCountInfo { - get { - return ResourceManager.GetString("CyclicalCountInfo", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Count information for a given element at a given indirection level can only be specified once. - /// - internal static string DuplicateCountInfo { - get { - return ResourceManager.GetString("DuplicateCountInfo", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Multiple marshalling attributes per element per indirection level is unsupported, but duplicate information was provided for indirection level {0}. - /// - internal static string DuplicateMarshallingInfo { - get { - return ResourceManager.GetString("DuplicateMarshallingInfo", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Marshalling info was specified for 'ElementIndirectionLevel' {0}, but marshalling info was only needed for {1} levels of indirection. - /// - internal static string ExtraneousMarshallingInfo { - get { - return ResourceManager.GetString("ExtraneousMarshallingInfo", resourceCulture); - } - } - /// /// Looks up a localized string similar to Types that contain methods marked with 'GeneratedDllImportAttribute' must be 'partial'. P/Invoke source generation will ignore methods contained within non-partial types.. /// @@ -403,56 +349,11 @@ internal static string GetPinnableReferenceShouldSupportAllocatingMarshallingFal } /// - /// Looks up a localized string similar to The provided graph has cycles and cannot be topologically sorted.. - /// - internal static string GraphHasCycles { - get { - return ResourceManager.GetString("GraphHasCycles", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The '[In]' attribute is not supported unless the '[Out]' attribute is also used. The behavior of the '[In]' attribute without the '[Out]' attribute is the same as the default behavior.. - /// - internal static string InAttributeNotSupportedWithoutOut { - get { - return ResourceManager.GetString("InAttributeNotSupportedWithoutOut", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The '[In]' and '[Out]' attributes are unsupported on parameters passed by reference. Use the 'in', 'ref', or 'out' keywords instead.. - /// - internal static string InOutAttributeByRefNotSupported { - get { - return ResourceManager.GetString("InOutAttributeByRefNotSupported", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The provided '[In]' and '[Out]' attributes on this parameter are unsupported on this parameter.. + /// 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 InOutAttributeMarshalerNotSupported { + internal static string NativeGenericTypeMustBeClosedDescription { get { - return ResourceManager.GetString("InOutAttributeMarshalerNotSupported", 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.. - /// - internal static string MarshallingCharAsSpecifiedCharSetNotSupported { - get { - return ResourceManager.GetString("MarshallingCharAsSpecifiedCharSetNotSupported", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Marshalling string or char without explicit marshalling information is not supported. Specify either 'GeneratedDllImportAttribute.CharSet' or 'MarshalAsAttribute'.. - /// - internal static string MarshallingStringOrCharAsUndefinedNotSupported { - get { - return ResourceManager.GetString("MarshallingStringOrCharAsUndefinedNotSupported", resourceCulture); + return ResourceManager.GetString("NativeGenericTypeMustBeClosedDescription", resourceCulture); } } @@ -546,24 +447,6 @@ internal static string NativeTypeMustHaveRequiredShapeMessage { } } - /// - /// Looks up a localized string similar to The '[Out]' attribute is only supported on array parameters.. - /// - internal static string OutByValueNotSupportedDescription { - get { - return ResourceManager.GetString("OutByValueNotSupportedDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The '[Out]' attribute is not supported on the '{0}' parameter.. - /// - internal static string OutByValueNotSupportedMessage { - get { - return ResourceManager.GetString("OutByValueNotSupportedMessage", resourceCulture); - } - } - /// /// Looks up a localized string similar to The 'Value' property must not be a 'ref' or 'readonly ref' property.. /// diff --git a/DllImportGenerator/DllImportGenerator/Resources.resx b/DllImportGenerator/DllImportGenerator/Resources.resx index d2d2adeffc2c..3124872bc317 100644 --- a/DllImportGenerator/DllImportGenerator/Resources.resx +++ b/DllImportGenerator/DllImportGenerator/Resources.resx @@ -117,9 +117,6 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - Marshalling an array from unmanaged to managed requires either the 'SizeParamIndex' or 'SizeConst' fields to be set on a 'MarshalAsAttribute' or the 'ConstantElementCount' or 'CountElementName' properties to be set on a 'MarshalUsingAttribute'. - A type marked with 'BlittableTypeAttribute' must be blittable. @@ -132,8 +129,11 @@ Type '{0}' is marked with 'BlittableTypeAttribute' and 'NativeMarshallingAttribute'. A type can only have one of these two attributes. - - The specified collection size parameter for an collection must be an integer type. If the size information is applied to a nested collection, the size parameter must be a collection of one less level of nesting with an integral element. + + 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. + + + 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. @@ -184,18 +184,6 @@ The specified parameter needs to be marshalled from native to managed, but the native type '{0}' does not support it. - - This element cannot depend on '{0}' for collection size information without creating a dependency cycle - - - Count information for a given element at a given indirection level can only be specified once - - - 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 - Types that contain methods marked with 'GeneratedDllImportAttribute' must be 'partial'. P/Invoke source generation will ignore methods contained within non-partial types. @@ -226,23 +214,8 @@ Type '{0}' has a 'GetPinnableReference' method but its native type does not support marshalling in scenarios where pinning is impossible - - The provided graph has cycles and cannot be topologically sorted. - - - The '[In]' attribute is not supported unless the '[Out]' attribute is also used. The behavior of the '[In]' attribute without the '[Out]' attribute is the same as the default behavior. - - - The '[In]' and '[Out]' attributes are unsupported on parameters passed by reference. Use the 'in', 'ref', or 'out' keywords instead. - - - The provided '[In]' and '[Out]' attributes on this parameter are unsupported on this parameter. - - - 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. - - - Marshalling string or char without explicit marshalling information is not supported. Specify either 'GeneratedDllImportAttribute.CharSet' or 'MarshalAsAttribute'. + + 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. @@ -274,18 +247,6 @@ 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. - - - 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>' - - - The '[Out]' attribute is only supported on array parameters. - - - The '[Out]' attribute is not supported on the '{0}' parameter. - The 'Value' property must not be a 'ref' or 'readonly ref' property. @@ -352,4 +313,4 @@ The 'Value' property on the native type '{0}' must have a setter - + \ No newline at end of file diff --git a/DllImportGenerator/DllImportGenerator/TypeNames.cs b/DllImportGenerator/DllImportGenerator/TypeNames.cs index 678040e4e6b7..32dc72b3bce3 100644 --- a/DllImportGenerator/DllImportGenerator/TypeNames.cs +++ b/DllImportGenerator/DllImportGenerator/TypeNames.cs @@ -2,11 +2,14 @@ using System.Collections.Generic; using System.Text; using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.Interop; namespace Microsoft.Interop { static class TypeNames { + public const string DllImportAttribute = "System.Runtime.InteropServices.DllImportAttribute"; + public const string GeneratedDllImportAttribute = "System.Runtime.InteropServices.GeneratedDllImportAttribute"; public const string GeneratedMarshallingAttribute = "System.Runtime.InteropServices.GeneratedMarshallingAttribute"; @@ -28,8 +31,6 @@ static class TypeNames public const string System_Span_Metadata = "System.Span`1"; public const string System_Span = "System.Span"; - public const string System_Activator = "System.Activator"; - public const string System_Runtime_InteropServices_StructLayoutAttribute = "System.Runtime.InteropServices.StructLayoutAttribute"; public const string System_Runtime_InteropServices_MarshalAsAttribute = "System.Runtime.InteropServices.MarshalAsAttribute"; @@ -38,25 +39,10 @@ static class TypeNames public const string System_Runtime_InteropServices_Marshal = "System.Runtime.InteropServices.Marshal"; - private const string System_Runtime_InteropServices_MarshalEx = "System.Runtime.InteropServices.MarshalEx"; - - public static string MarshalEx(AnalyzerConfigOptions options) - { - return options.UseMarshalType() ? System_Runtime_InteropServices_Marshal : System_Runtime_InteropServices_MarshalEx; - } - public const string System_Runtime_InteropServices_GeneratedMarshalling_ArrayMarshaller_Metadata = "System.Runtime.InteropServices.GeneratedMarshalling.ArrayMarshaller`1"; public const string System_Runtime_InteropServices_GeneratedMarshalling_PtrArrayMarshaller_Metadata = "System.Runtime.InteropServices.GeneratedMarshalling.PtrArrayMarshaller`1"; - public const string System_Runtime_InteropServices_MemoryMarshal = "System.Runtime.InteropServices.MemoryMarshal"; - - public const string System_Runtime_InteropServices_SafeHandle = "System.Runtime.InteropServices.SafeHandle"; - - public const string System_Runtime_InteropServices_OutAttribute = "System.Runtime.InteropServices.OutAttribute"; - - public const string System_Runtime_InteropServices_InAttribute = "System.Runtime.InteropServices.InAttribute"; - public const string System_Runtime_CompilerServices_SkipLocalsInitAttribute = "System.Runtime.CompilerServices.SkipLocalsInitAttribute"; private const string System_Runtime_CompilerServices_Unsafe = "System.Runtime.CompilerServices.Unsafe"; diff --git a/DllImportGenerator/DllImportGenerator/ContiguousCollectionElementMarshallingCodeContext.cs b/DllImportGenerator/Microsoft.Interop.SourceGeneration/ContiguousCollectionElementMarshallingCodeContext.cs similarity index 100% rename from DllImportGenerator/DllImportGenerator/ContiguousCollectionElementMarshallingCodeContext.cs rename to DllImportGenerator/Microsoft.Interop.SourceGeneration/ContiguousCollectionElementMarshallingCodeContext.cs diff --git a/DllImportGenerator/Microsoft.Interop.SourceGeneration/IGeneratorDiagnostics.cs b/DllImportGenerator/Microsoft.Interop.SourceGeneration/IGeneratorDiagnostics.cs new file mode 100644 index 000000000000..f02f7c28957e --- /dev/null +++ b/DllImportGenerator/Microsoft.Interop.SourceGeneration/IGeneratorDiagnostics.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Linq; + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Microsoft.Interop +{ + public static class DiagnosticExtensions + { + public static Diagnostic CreateDiagnostic( + this ISymbol symbol, + DiagnosticDescriptor descriptor, + params object[] args) + { + return symbol.Locations.CreateDiagnostic(descriptor, args); + } + + public static Diagnostic CreateDiagnostic( + this AttributeData attributeData, + DiagnosticDescriptor descriptor, + params object[] args) + { + SyntaxReference? syntaxReference = attributeData.ApplicationSyntaxReference; + Location location = syntaxReference is not null + ? syntaxReference.GetSyntax().GetLocation() + : Location.None; + + return location.CreateDiagnostic(descriptor, args); + } + + public static Diagnostic CreateDiagnostic( + this ImmutableArray locations, + DiagnosticDescriptor descriptor, + params object[] args) + { + IEnumerable locationsInSource = locations.Where(l => l.IsInSource); + if (!locationsInSource.Any()) + return Diagnostic.Create(descriptor, Location.None, args); + + return Diagnostic.Create( + descriptor, + location: locationsInSource.First(), + additionalLocations: locationsInSource.Skip(1), + messageArgs: args); + } + + public static Diagnostic CreateDiagnostic( + this Location location, + DiagnosticDescriptor descriptor, + params object[] args) + { + return Diagnostic.Create( + descriptor, + location: location.IsInSource ? location : Location.None, + messageArgs: args); + } + } + + + public interface IGeneratorDiagnostics + { + /// + /// Report diagnostic for marshalling of a parameter/return that is not supported + /// + /// Method with the parameter/return + /// Type info for the parameter/return + /// [Optional] Specific reason for lack of support + void ReportMarshallingNotSupported( + MethodDeclarationSyntax method, + TypePositionInfo info, + string? notSupportedDetails); + + /// + /// Report diagnostic for configuration that is not supported by the DLL import source generator + /// + /// Attribute specifying the unsupported configuration + /// Name of the configuration + /// [Optiona] Unsupported configuration value + void ReportConfigurationNotSupported( + AttributeData attributeData, + string configurationName, + string? unsupportedValue); + + void ReportInvalidMarshallingAttributeInfo( + AttributeData attributeData, + string reasonResourceName, + params string[] reasonArgs); + } + + public static class IGeneratorDiagnosticsExtensions + { + public static void ReportConfigurationNotSupported(this IGeneratorDiagnostics diagnostics, AttributeData attributeData, string configurationName) + => diagnostics.ReportConfigurationNotSupported(attributeData, configurationName, null); + } +} diff --git a/DllImportGenerator/Microsoft.Interop.SourceGeneration/InteropGenerationOptions.cs b/DllImportGenerator/Microsoft.Interop.SourceGeneration/InteropGenerationOptions.cs new file mode 100644 index 000000000000..fa4e46a3f3d3 --- /dev/null +++ b/DllImportGenerator/Microsoft.Interop.SourceGeneration/InteropGenerationOptions.cs @@ -0,0 +1,8 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.Interop +{ + public record InteropGenerationOptions(bool UseMarshalType, bool UseInternalUnsafeType); +} diff --git a/DllImportGenerator/Microsoft.Interop.SourceGeneration/LanguageSupport.cs b/DllImportGenerator/Microsoft.Interop.SourceGeneration/LanguageSupport.cs new file mode 100644 index 000000000000..ddf2c4203111 --- /dev/null +++ b/DllImportGenerator/Microsoft.Interop.SourceGeneration/LanguageSupport.cs @@ -0,0 +1,10 @@ +// Types defined to enable language support of various features +// in the source generator. + + +namespace System.Runtime.CompilerServices +{ + // Define IsExternalInit type to support records. + internal class IsExternalInit + {} +} \ No newline at end of file diff --git a/DllImportGenerator/DllImportGenerator/ManagedTypeInfo.cs b/DllImportGenerator/Microsoft.Interop.SourceGeneration/ManagedTypeInfo.cs similarity index 68% rename from DllImportGenerator/DllImportGenerator/ManagedTypeInfo.cs rename to DllImportGenerator/Microsoft.Interop.SourceGeneration/ManagedTypeInfo.cs index 9e2bd6f70d8a..fe789e2e0886 100644 --- a/DllImportGenerator/DllImportGenerator/ManagedTypeInfo.cs +++ b/DllImportGenerator/Microsoft.Interop.SourceGeneration/ManagedTypeInfo.cs @@ -10,7 +10,7 @@ namespace Microsoft.Interop /// /// A discriminated union that contains enough info about a managed type to determine a marshalling generator and generate code. /// - internal abstract record ManagedTypeInfo(string FullTypeName, string DiagnosticFormattedName) + public abstract record ManagedTypeInfo(string FullTypeName, string DiagnosticFormattedName) { public TypeSyntax Syntax { get; } = SyntaxFactory.ParseTypeName(FullTypeName); @@ -46,7 +46,7 @@ public static ManagedTypeInfo CreateTypeInfoForTypeSymbol(ITypeSymbol type) } } - internal sealed record SpecialTypeInfo(string FullTypeName, string DiagnosticFormattedName, SpecialType SpecialType) : ManagedTypeInfo(FullTypeName, DiagnosticFormattedName) + public sealed record SpecialTypeInfo(string FullTypeName, string DiagnosticFormattedName, SpecialType SpecialType) : ManagedTypeInfo(FullTypeName, DiagnosticFormattedName) { public static readonly SpecialTypeInfo Int32 = new("int", "int", SpecialType.System_Int32); public static readonly SpecialTypeInfo Void = new("void", "void", SpecialType.System_Void); @@ -62,13 +62,13 @@ public override int GetHashCode() } } - internal sealed record EnumTypeInfo(string FullTypeName, string DiagnosticFormattedName, SpecialType UnderlyingType) : ManagedTypeInfo(FullTypeName, DiagnosticFormattedName); + public sealed record EnumTypeInfo(string FullTypeName, string DiagnosticFormattedName, SpecialType UnderlyingType) : ManagedTypeInfo(FullTypeName, DiagnosticFormattedName); - internal sealed record PointerTypeInfo(string FullTypeName, string DiagnosticFormattedName, bool IsFunctionPointer) : ManagedTypeInfo(FullTypeName, DiagnosticFormattedName); + public sealed record PointerTypeInfo(string FullTypeName, string DiagnosticFormattedName, bool IsFunctionPointer) : ManagedTypeInfo(FullTypeName, DiagnosticFormattedName); - internal sealed record SzArrayType(ManagedTypeInfo ElementTypeInfo) : ManagedTypeInfo($"{ElementTypeInfo.FullTypeName}[]", $"{ElementTypeInfo.DiagnosticFormattedName}[]"); + public sealed record SzArrayType(ManagedTypeInfo ElementTypeInfo) : ManagedTypeInfo($"{ElementTypeInfo.FullTypeName}[]", $"{ElementTypeInfo.DiagnosticFormattedName}[]"); - internal sealed record DelegateTypeInfo(string FullTypeName, string DiagnosticFormattedName) : ManagedTypeInfo(FullTypeName, DiagnosticFormattedName); + public sealed record DelegateTypeInfo(string FullTypeName, string DiagnosticFormattedName) : ManagedTypeInfo(FullTypeName, DiagnosticFormattedName); - internal sealed record SimpleManagedTypeInfo(string FullTypeName, string DiagnosticFormattedName) : ManagedTypeInfo(FullTypeName, DiagnosticFormattedName); + public sealed record SimpleManagedTypeInfo(string FullTypeName, string DiagnosticFormattedName) : ManagedTypeInfo(FullTypeName, DiagnosticFormattedName); } diff --git a/DllImportGenerator/DllImportGenerator/ManualTypeMarshallingHelper.cs b/DllImportGenerator/Microsoft.Interop.SourceGeneration/ManualTypeMarshallingHelper.cs similarity index 99% rename from DllImportGenerator/DllImportGenerator/ManualTypeMarshallingHelper.cs rename to DllImportGenerator/Microsoft.Interop.SourceGeneration/ManualTypeMarshallingHelper.cs index 1d073064a39d..137f45f1040d 100644 --- a/DllImportGenerator/DllImportGenerator/ManualTypeMarshallingHelper.cs +++ b/DllImportGenerator/Microsoft.Interop.SourceGeneration/ManualTypeMarshallingHelper.cs @@ -5,7 +5,7 @@ namespace Microsoft.Interop { - static class ManualTypeMarshallingHelper + public static class ManualTypeMarshallingHelper { public const string ValuePropertyName = "Value"; public const string GetPinnableReferenceName = "GetPinnableReference"; diff --git a/DllImportGenerator/DllImportGenerator/Marshalling/ArrayMarshaller.cs b/DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/ArrayMarshaller.cs similarity index 96% rename from DllImportGenerator/DllImportGenerator/Marshalling/ArrayMarshaller.cs rename to DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/ArrayMarshaller.cs index 46af295b524b..53fc3655af91 100644 --- a/DllImportGenerator/DllImportGenerator/Marshalling/ArrayMarshaller.cs +++ b/DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/ArrayMarshaller.cs @@ -6,14 +6,14 @@ namespace Microsoft.Interop { - internal sealed class ArrayMarshaller : IMarshallingGenerator + public sealed class ArrayMarshaller : IMarshallingGenerator { private readonly IMarshallingGenerator manualMarshallingGenerator; private readonly TypeSyntax elementType; private readonly bool enablePinning; - private readonly AnalyzerConfigOptions options; + private readonly InteropGenerationOptions options; - public ArrayMarshaller(IMarshallingGenerator manualMarshallingGenerator, TypeSyntax elementType, bool enablePinning, AnalyzerConfigOptions options) + public ArrayMarshaller(IMarshallingGenerator manualMarshallingGenerator, TypeSyntax elementType, bool enablePinning, InteropGenerationOptions options) { this.manualMarshallingGenerator = manualMarshallingGenerator; this.elementType = elementType; diff --git a/DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/AttributedMarshallingModelGeneratorFactory.cs b/DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/AttributedMarshallingModelGeneratorFactory.cs new file mode 100644 index 000000000000..1346356eee12 --- /dev/null +++ b/DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/AttributedMarshallingModelGeneratorFactory.cs @@ -0,0 +1,292 @@ +using Microsoft.CodeAnalysis.CSharp; +using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Microsoft.Interop +{ + public class AttributedMarshallingModelGeneratorFactory : IMarshallingGeneratorFactory + { + private static readonly BlittableMarshaller Blittable = new BlittableMarshaller(); + private static readonly Forwarder Forwarder = new Forwarder(); + + private readonly IMarshallingGeneratorFactory innerMarshallingGenerator; + + public AttributedMarshallingModelGeneratorFactory(IMarshallingGeneratorFactory innerMarshallingGenerator, InteropGenerationOptions options) + { + Options = options; + this.innerMarshallingGenerator = innerMarshallingGenerator; + ElementMarshallingGeneratorFactory = this; + } + + public InteropGenerationOptions Options { get; } + + /// + /// The to use for collection elements. + /// This property is settable to enable decorating factories to ensure that element marshalling also goes through the decorator support. + /// + public IMarshallingGeneratorFactory ElementMarshallingGeneratorFactory { get; set; } + + public IMarshallingGenerator Create(TypePositionInfo info, StubCodeContext context) + { + return info.MarshallingAttributeInfo switch + { + NativeMarshallingAttributeInfo marshalInfo => CreateCustomNativeTypeMarshaller(info, context, marshalInfo), + BlittableTypeAttributeInfo => Blittable, + GeneratedNativeMarshallingAttributeInfo => Forwarder, + _ => innerMarshallingGenerator.Create(info, context) + }; + } + + private static ExpressionSyntax GetNumElementsExpressionFromMarshallingInfo(TypePositionInfo info, CountInfo count, StubCodeContext context) + { + return count switch + { + SizeAndParamIndexInfo(int size, SizeAndParamIndexInfo.UnspecifiedParam) => GetConstSizeExpression(size), + ConstSizeCountInfo(int size) => GetConstSizeExpression(size), + SizeAndParamIndexInfo(SizeAndParamIndexInfo.UnspecifiedConstSize, TypePositionInfo param) => CheckedExpression(SyntaxKind.CheckedExpression, GetExpressionForParam(param)), + SizeAndParamIndexInfo(int size, TypePositionInfo param) => CheckedExpression(SyntaxKind.CheckedExpression, BinaryExpression(SyntaxKind.AddExpression, GetConstSizeExpression(size), GetExpressionForParam(param))), + CountElementCountInfo(TypePositionInfo elementInfo) => CheckedExpression(SyntaxKind.CheckedExpression, GetExpressionForParam(elementInfo)), + _ => throw new MarshallingNotSupportedException(info, context) + { + NotSupportedDetails = Resources.ArraySizeMustBeSpecified + }, + }; + + static LiteralExpressionSyntax GetConstSizeExpression(int size) + { + return LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(size)); + } + + ExpressionSyntax GetExpressionForParam(TypePositionInfo paramInfo) + { + ExpressionSyntax numElementsExpression = GetIndexedNumElementsExpression( + context, + paramInfo, + out int numIndirectionLevels); + + ManagedTypeInfo type = paramInfo.ManagedType; + MarshallingInfo marshallingInfo = paramInfo.MarshallingAttributeInfo; + + for (int i = 0; i < numIndirectionLevels; i++) + { + if (marshallingInfo is NativeContiguousCollectionMarshallingInfo collectionInfo) + { + type = collectionInfo.ElementType; + marshallingInfo = collectionInfo.ElementMarshallingInfo; + } + else + { + throw new MarshallingNotSupportedException(info, context) + { + NotSupportedDetails = Resources.CollectionSizeParamTypeMustBeIntegral + }; + } + } + + if (type is not SpecialTypeInfo specialType || !specialType.SpecialType.IsIntegralType()) + { + throw new MarshallingNotSupportedException(info, context) + { + NotSupportedDetails = Resources.CollectionSizeParamTypeMustBeIntegral + }; + } + + return CastExpression( + PredefinedType(Token(SyntaxKind.IntKeyword)), + ParenthesizedExpression(numElementsExpression)); + } + + static ExpressionSyntax GetIndexedNumElementsExpression(StubCodeContext context, TypePositionInfo numElementsInfo, out int numIndirectionLevels) + { + Stack indexerStack = new(); + + StubCodeContext? currentContext = context; + StubCodeContext lastContext = null!; + + while (currentContext is not null) + { + if (currentContext is ContiguousCollectionElementMarshallingCodeContext collectionContext) + { + indexerStack.Push(collectionContext.IndexerIdentifier); + } + lastContext = currentContext; + currentContext = currentContext.ParentContext; + } + + numIndirectionLevels = indexerStack.Count; + + ExpressionSyntax indexedNumElements = IdentifierName(lastContext.GetIdentifiers(numElementsInfo).managed); + while (indexerStack.Count > 0) + { + NameSyntax indexer = IdentifierName(indexerStack.Pop()); + indexedNumElements = ElementAccessExpression(indexedNumElements) + .AddArgumentListArguments(Argument(indexer)); + } + + return indexedNumElements; + } + } + + private IMarshallingGenerator CreateCustomNativeTypeMarshaller(TypePositionInfo info, StubCodeContext context, NativeMarshallingAttributeInfo marshalInfo) + { + ValidateCustomNativeTypeMarshallingSupported(info, context, marshalInfo); + + ICustomNativeTypeMarshallingStrategy marshallingStrategy = new SimpleCustomNativeTypeMarshalling(marshalInfo.NativeMarshallingType.Syntax); + + if ((marshalInfo.MarshallingFeatures & CustomMarshallingFeatures.ManagedToNativeStackalloc) != 0) + { + marshallingStrategy = new StackallocOptimizationMarshalling(marshallingStrategy); + } + + if ((marshalInfo.MarshallingFeatures & CustomMarshallingFeatures.FreeNativeResources) != 0) + { + marshallingStrategy = new FreeNativeCleanupStrategy(marshallingStrategy); + } + + // Collections have extra configuration, so handle them here. + if (marshalInfo is NativeContiguousCollectionMarshallingInfo collectionMarshallingInfo) + { + return CreateNativeCollectionMarshaller(info, context, collectionMarshallingInfo, marshallingStrategy); + } + + if (marshalInfo.ValuePropertyType is not null) + { + marshallingStrategy = DecorateWithValuePropertyStrategy(marshalInfo, marshallingStrategy); + } + + IMarshallingGenerator marshallingGenerator = new CustomNativeTypeMarshallingGenerator(marshallingStrategy, enableByValueContentsMarshalling: false); + + if ((marshalInfo.MarshallingFeatures & CustomMarshallingFeatures.ManagedTypePinning) != 0) + { + return new PinnableManagedValueMarshaller(marshallingGenerator); + } + + return marshallingGenerator; + } + + private void ValidateCustomNativeTypeMarshallingSupported(TypePositionInfo info, StubCodeContext context, NativeMarshallingAttributeInfo marshalInfo) + { + // 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) + { + throw new MarshallingNotSupportedException(info, context) + { + NotSupportedDetails = string.Format(Resources.CustomTypeMarshallingNativeToManagedUnsupported, marshalInfo.NativeMarshallingType.FullTypeName) + }; + } + // 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)) + { + throw new MarshallingNotSupportedException(info, context) + { + NotSupportedDetails = string.Format(Resources.CustomTypeMarshallingManagedToNativeUnsupported, marshalInfo.NativeMarshallingType.FullTypeName) + }; + } + // 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. + // "in" byref supports stack marshalling. + else if (info.RefKind == RefKind.In + && (marshalInfo.MarshallingFeatures & CustomMarshallingFeatures.ManagedToNative) == 0 + && !(context.SingleFrameSpansNativeContext && (marshalInfo.MarshallingFeatures & CustomMarshallingFeatures.ManagedToNativeStackalloc) != 0)) + { + throw new MarshallingNotSupportedException(info, context) + { + NotSupportedDetails = string.Format(Resources.CustomTypeMarshallingManagedToNativeUnsupported, marshalInfo.NativeMarshallingType.FullTypeName) + }; + } + // 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 + else if (info.RefKind == RefKind.Ref + && (marshalInfo.MarshallingFeatures & CustomMarshallingFeatures.ManagedToNative) == 0) + { + throw new MarshallingNotSupportedException(info, context) + { + NotSupportedDetails = string.Format(Resources.CustomTypeMarshallingManagedToNativeUnsupported, marshalInfo.NativeMarshallingType.FullTypeName) + }; + } + } + + private ICustomNativeTypeMarshallingStrategy DecorateWithValuePropertyStrategy(NativeMarshallingAttributeInfo marshalInfo, ICustomNativeTypeMarshallingStrategy nativeTypeMarshaller) + { + TypeSyntax valuePropertyTypeSyntax = marshalInfo.ValuePropertyType!.Syntax; + + if ((marshalInfo.MarshallingFeatures & CustomMarshallingFeatures.NativeTypePinning) != 0) + { + return new PinnableMarshallerTypeMarshalling(nativeTypeMarshaller, valuePropertyTypeSyntax); + } + + return new CustomNativeTypeWithValuePropertyMarshalling(nativeTypeMarshaller, valuePropertyTypeSyntax); + } + + private IMarshallingGenerator CreateNativeCollectionMarshaller( + TypePositionInfo info, + StubCodeContext context, + NativeContiguousCollectionMarshallingInfo collectionInfo, + ICustomNativeTypeMarshallingStrategy marshallingStrategy) + { + var elementInfo = new TypePositionInfo(collectionInfo.ElementType, collectionInfo.ElementMarshallingInfo) { ManagedIndex = info.ManagedIndex }; + var elementMarshaller = Create( + elementInfo, + new ContiguousCollectionElementMarshallingCodeContext(StubCodeContext.Stage.Setup, string.Empty, context)); + var elementType = elementMarshaller.AsNativeType(elementInfo); + + bool isBlittable = elementMarshaller is BlittableMarshaller; + + if (isBlittable) + { + marshallingStrategy = new ContiguousBlittableElementCollectionMarshalling(marshallingStrategy, collectionInfo.ElementType.Syntax); + } + else + { + marshallingStrategy = new ContiguousNonBlittableElementCollectionMarshalling(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. + if (collectionInfo.ValuePropertyType 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, + numElementsExpression, + SizeOfExpression(elementType)); + + if (collectionInfo.UseDefaultMarshalling && info.ManagedType is SzArrayType) + { + return new ArrayMarshaller( + new CustomNativeTypeMarshallingGenerator(marshallingStrategy, enableByValueContentsMarshalling: true), + elementType, + isBlittable, + Options); + } + + IMarshallingGenerator marshallingGenerator = new CustomNativeTypeMarshallingGenerator(marshallingStrategy, enableByValueContentsMarshalling: false); + + if ((collectionInfo.MarshallingFeatures & CustomMarshallingFeatures.ManagedTypePinning) != 0) + { + return new PinnableManagedValueMarshaller(marshallingGenerator); + } + + return marshallingGenerator; + } + } +} diff --git a/DllImportGenerator/DllImportGenerator/Marshalling/BlittableMarshaller.cs b/DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/BlittableMarshaller.cs similarity index 98% rename from DllImportGenerator/DllImportGenerator/Marshalling/BlittableMarshaller.cs rename to DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/BlittableMarshaller.cs index 15fa7774a996..8925ffc62555 100644 --- a/DllImportGenerator/DllImportGenerator/Marshalling/BlittableMarshaller.cs +++ b/DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/BlittableMarshaller.cs @@ -7,7 +7,7 @@ namespace Microsoft.Interop { - internal class BlittableMarshaller : IMarshallingGenerator + public sealed class BlittableMarshaller : IMarshallingGenerator { public TypeSyntax AsNativeType(TypePositionInfo info) { diff --git a/DllImportGenerator/DllImportGenerator/Marshalling/BoolMarshaller.cs b/DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/BoolMarshaller.cs similarity index 95% rename from DllImportGenerator/DllImportGenerator/Marshalling/BoolMarshaller.cs rename to DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/BoolMarshaller.cs index 2b5af8d2a5cd..7ceab5830c0d 100644 --- a/DllImportGenerator/DllImportGenerator/Marshalling/BoolMarshaller.cs +++ b/DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/BoolMarshaller.cs @@ -8,7 +8,7 @@ namespace Microsoft.Interop { - internal abstract class BoolMarshallerBase : IMarshallingGenerator + public abstract class BoolMarshallerBase : IMarshallingGenerator { private readonly PredefinedTypeSyntax _nativeType; private readonly int _trueValue; @@ -114,7 +114,7 @@ public IEnumerable Generate(TypePositionInfo info, StubCodeCont /// and C++, but those is implementation defined. /// Consult your compiler specification. /// - internal class ByteBoolMarshaller : BoolMarshallerBase + public sealed class ByteBoolMarshaller : BoolMarshallerBase { public ByteBoolMarshaller() : base(PredefinedType(Token(SyntaxKind.ByteKeyword)), trueValue: 1, falseValue: 0, compareToTrue: false) @@ -128,7 +128,7 @@ public ByteBoolMarshaller() /// /// Corresponds to the definition of BOOL. /// - internal class WinBoolMarshaller : BoolMarshallerBase + public sealed class WinBoolMarshaller : BoolMarshallerBase { public WinBoolMarshaller() : base(PredefinedType(Token(SyntaxKind.IntKeyword)), trueValue: 1, falseValue: 0, compareToTrue: false) @@ -139,7 +139,7 @@ public WinBoolMarshaller() /// /// Marshal a boolean value as a VARIANT_BOOL (Windows OLE/Automation type). /// - internal class VariantBoolMarshaller : BoolMarshallerBase + public sealed class VariantBoolMarshaller : BoolMarshallerBase { private const short VARIANT_TRUE = -1; private const short VARIANT_FALSE = 0; diff --git a/DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/ByValueContentsMarshalKindValidator.cs b/DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/ByValueContentsMarshalKindValidator.cs new file mode 100644 index 000000000000..dc9f64fde908 --- /dev/null +++ b/DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/ByValueContentsMarshalKindValidator.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.Interop +{ + /// + /// An implementation that wraps an inner instance and validates that the on the provided is valid in the current marshalling scenario. + /// + public class ByValueContentsMarshalKindValidator : IMarshallingGeneratorFactory + { + private readonly IMarshallingGeneratorFactory inner; + + public ByValueContentsMarshalKindValidator(IMarshallingGeneratorFactory inner) + { + this.inner = inner; + } + + public IMarshallingGenerator Create(TypePositionInfo info, StubCodeContext context) + { + return ValidateByValueMarshalKind(info, context, inner.Create(info, context)); + } + + private static IMarshallingGenerator ValidateByValueMarshalKind(TypePositionInfo info, StubCodeContext context, IMarshallingGenerator generator) + { + if (info.IsByRef && info.ByValueContentsMarshalKind != ByValueContentsMarshalKind.Default) + { + throw new MarshallingNotSupportedException(info, context) + { + NotSupportedDetails = Resources.InOutAttributeByRefNotSupported + }; + } + else if (info.ByValueContentsMarshalKind == ByValueContentsMarshalKind.In) + { + throw new MarshallingNotSupportedException(info, context) + { + NotSupportedDetails = Resources.InAttributeNotSupportedWithoutOut + }; + } + else if (info.ByValueContentsMarshalKind != ByValueContentsMarshalKind.Default + && !generator.SupportsByValueMarshalKind(info.ByValueContentsMarshalKind, context)) + { + throw new MarshallingNotSupportedException(info, context) + { + NotSupportedDetails = Resources.InOutAttributeMarshalerNotSupported + }; + } + return generator; + } + } +} diff --git a/DllImportGenerator/DllImportGenerator/Marshalling/CharMarshaller.cs b/DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/CharMarshaller.cs similarity index 97% rename from DllImportGenerator/DllImportGenerator/Marshalling/CharMarshaller.cs rename to DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/CharMarshaller.cs index d04a78c2460a..ce3c97230ada 100644 --- a/DllImportGenerator/DllImportGenerator/Marshalling/CharMarshaller.cs +++ b/DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/CharMarshaller.cs @@ -9,7 +9,7 @@ namespace Microsoft.Interop { - internal class Utf16CharMarshaller : IMarshallingGenerator + public sealed class Utf16CharMarshaller : IMarshallingGenerator { private static readonly PredefinedTypeSyntax NativeType = PredefinedType(Token(SyntaxKind.UShortKeyword)); diff --git a/DllImportGenerator/DllImportGenerator/Marshalling/ConditionalStackallocMarshallingGenerator.cs b/DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/ConditionalStackallocMarshallingGenerator.cs similarity index 99% rename from DllImportGenerator/DllImportGenerator/Marshalling/ConditionalStackallocMarshallingGenerator.cs rename to DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/ConditionalStackallocMarshallingGenerator.cs index 2994ec8df75c..c2c0788b196f 100644 --- a/DllImportGenerator/DllImportGenerator/Marshalling/ConditionalStackallocMarshallingGenerator.cs +++ b/DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/ConditionalStackallocMarshallingGenerator.cs @@ -6,7 +6,7 @@ namespace Microsoft.Interop { - internal abstract class ConditionalStackallocMarshallingGenerator : IMarshallingGenerator + public abstract class ConditionalStackallocMarshallingGenerator : IMarshallingGenerator { protected static string GetAllocationMarkerIdentifier(TypePositionInfo info, StubCodeContext context) => context.GetAdditionalIdentifier(info, "allocated"); diff --git a/DllImportGenerator/DllImportGenerator/Marshalling/CustomNativeTypeMarshallingGenerator.cs b/DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/CustomNativeTypeMarshallingGenerator.cs similarity index 100% rename from DllImportGenerator/DllImportGenerator/Marshalling/CustomNativeTypeMarshallingGenerator.cs rename to DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/CustomNativeTypeMarshallingGenerator.cs diff --git a/DllImportGenerator/DllImportGenerator/Marshalling/DelegateMarshaller.cs b/DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/DelegateMarshaller.cs similarity index 98% rename from DllImportGenerator/DllImportGenerator/Marshalling/DelegateMarshaller.cs rename to DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/DelegateMarshaller.cs index b5aca0f44207..c8854abb54bd 100644 --- a/DllImportGenerator/DllImportGenerator/Marshalling/DelegateMarshaller.cs +++ b/DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/DelegateMarshaller.cs @@ -6,7 +6,7 @@ namespace Microsoft.Interop { - internal class DelegateMarshaller : IMarshallingGenerator + public sealed class DelegateMarshaller : IMarshallingGenerator { public TypeSyntax AsNativeType(TypePositionInfo info) { diff --git a/DllImportGenerator/DllImportGenerator/Marshalling/Forwarder.cs b/DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/Forwarder.cs similarity index 98% rename from DllImportGenerator/DllImportGenerator/Marshalling/Forwarder.cs rename to DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/Forwarder.cs index e86c422ec482..1f8f1c1756f2 100644 --- a/DllImportGenerator/DllImportGenerator/Marshalling/Forwarder.cs +++ b/DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/Forwarder.cs @@ -8,7 +8,7 @@ namespace Microsoft.Interop { - internal class Forwarder : IMarshallingGenerator, IAttributedReturnTypeMarshallingGenerator + public sealed class Forwarder : IMarshallingGenerator, IAttributedReturnTypeMarshallingGenerator { public TypeSyntax AsNativeType(TypePositionInfo info) { diff --git a/DllImportGenerator/DllImportGenerator/Marshalling/HResultExceptionMarshaller.cs b/DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/HResultExceptionMarshaller.cs similarity index 96% rename from DllImportGenerator/DllImportGenerator/Marshalling/HResultExceptionMarshaller.cs rename to DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/HResultExceptionMarshaller.cs index 09ce81431e6a..349a651a9d5b 100644 --- a/DllImportGenerator/DllImportGenerator/Marshalling/HResultExceptionMarshaller.cs +++ b/DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/HResultExceptionMarshaller.cs @@ -9,7 +9,7 @@ namespace Microsoft.Interop { - internal sealed class HResultExceptionMarshaller : IMarshallingGenerator + public sealed class HResultExceptionMarshaller : IMarshallingGenerator { private static readonly TypeSyntax NativeType = PredefinedType(Token(SyntaxKind.IntKeyword)); diff --git a/DllImportGenerator/DllImportGenerator/Marshalling/ICustomNativeTypeMarshallingStrategy.cs b/DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/ICustomNativeTypeMarshallingStrategy.cs similarity index 100% rename from DllImportGenerator/DllImportGenerator/Marshalling/ICustomNativeTypeMarshallingStrategy.cs rename to DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/ICustomNativeTypeMarshallingStrategy.cs diff --git a/DllImportGenerator/DllImportGenerator/Marshalling/MarshallerHelpers.cs b/DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs similarity index 99% rename from DllImportGenerator/DllImportGenerator/Marshalling/MarshallerHelpers.cs rename to DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs index 256fc939ec7b..f3ee0df3ea33 100644 --- a/DllImportGenerator/DllImportGenerator/Marshalling/MarshallerHelpers.cs +++ b/DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs @@ -7,7 +7,7 @@ namespace Microsoft.Interop { - internal static class MarshallerHelpers + public static class MarshallerHelpers { public static readonly ExpressionSyntax IsWindows = InvocationExpression( MemberAccessExpression( diff --git a/DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/MarshallingGenerator.cs b/DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/MarshallingGenerator.cs new file mode 100644 index 000000000000..e97191550f18 --- /dev/null +++ b/DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/MarshallingGenerator.cs @@ -0,0 +1,116 @@ +using System; +using System.Collections.Generic; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Microsoft.Interop +{ + /// + /// Interface for generation of marshalling code for P/Invoke stubs + /// + public interface IMarshallingGenerator + { + /// + /// Get the native type syntax for + /// + /// Object to marshal + /// Type syntax for the native type representing + TypeSyntax AsNativeType(TypePositionInfo info); + + /// + /// Get the as a parameter of the P/Invoke declaration + /// + /// Object to marshal + /// Parameter syntax for + ParameterSyntax AsParameter(TypePositionInfo info); + + /// + /// Get the as an argument to be passed to the P/Invoke + /// + /// Object to marshal + /// Code generation context + /// Argument syntax for + ArgumentSyntax AsArgument(TypePositionInfo info, StubCodeContext context); + + /// + /// Generate code for marshalling + /// + /// Object to marshal + /// Code generation context + /// List of statements to be added to the P/Invoke stub + /// + /// The generator should return the appropriate statements based on the + /// of . + /// For , any statements not of type + /// will be ignored. + /// + IEnumerable Generate(TypePositionInfo info, StubCodeContext context); + + /// + /// Returns whether or not this marshaller uses an identifier for the native value in addition + /// to an identifer for the managed value. + /// + /// Object to marshal + /// Code generation context + /// If the marshaller uses an identifier for the native value, true; otherwise, false. + /// + /// of may not be valid. + /// + bool UsesNativeIdentifier(TypePositionInfo info, StubCodeContext context); + + /// + /// Returns if the given ByValueContentsMarshalKind is supported in the current marshalling context. + /// A supported marshal kind has a different behavior than the default behavior. + /// + /// The marshal kind. + /// The marshalling context. + /// + bool SupportsByValueMarshalKind(ByValueContentsMarshalKind marshalKind, StubCodeContext context); + } + + /// + /// Interface for generating attributes for native return types. + /// + public interface IAttributedReturnTypeMarshallingGenerator : IMarshallingGenerator + { + /// + /// Gets any attributes that should be applied to the return type for this . + /// + /// Object to marshal + /// Attributes for the return type for this , or null if no attributes should be added. + AttributeListSyntax? GenerateAttributesForReturnType(TypePositionInfo info); + } + + + /// + /// Exception used to indicate marshalling isn't supported. + /// + public sealed class MarshallingNotSupportedException : Exception + { + /// + /// Construct a new instance. + /// + /// instance + /// instance + public MarshallingNotSupportedException(TypePositionInfo info, StubCodeContext context) + { + this.TypePositionInfo = info; + this.StubCodeContext = context; + } + + /// + /// Type that is being marshalled. + /// + public TypePositionInfo TypePositionInfo { get; private init; } + + /// + /// Context in which the marshalling is taking place. + /// + public StubCodeContext StubCodeContext { get; private init; } + + /// + /// [Optional] Specific reason marshalling of the supplied type isn't supported. + /// + public string? NotSupportedDetails { get; init; } + } +} diff --git a/DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/MarshallingGeneratorFactory.cs b/DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/MarshallingGeneratorFactory.cs new file mode 100644 index 000000000000..1efa6e495e57 --- /dev/null +++ b/DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/MarshallingGeneratorFactory.cs @@ -0,0 +1,221 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; + +namespace Microsoft.Interop +{ + public interface IMarshallingGeneratorFactory + { + /// + /// Create an instance for marshalling the supplied type in the given position. + /// + /// Type details + /// Metadata about the stub the type is associated with + /// A instance. + public IMarshallingGenerator Create( + TypePositionInfo info, + StubCodeContext context); + } + + public sealed class DefaultMarshallingGeneratorFactory : IMarshallingGeneratorFactory + { + private static readonly ByteBoolMarshaller ByteBool = new(); + private static readonly WinBoolMarshaller WinBool = new(); + private static readonly VariantBoolMarshaller VariantBool = new(); + + private static readonly Utf16CharMarshaller Utf16Char = new(); + private static readonly Utf16StringMarshaller Utf16String = new(); + private static readonly Utf8StringMarshaller Utf8String = new(); + private static readonly AnsiStringMarshaller AnsiString = new AnsiStringMarshaller(Utf8String); + private static readonly PlatformDefinedStringMarshaller PlatformDefinedString = new PlatformDefinedStringMarshaller(Utf16String, Utf8String); + + private static readonly Forwarder Forwarder = new(); + private static readonly BlittableMarshaller Blittable = new(); + private static readonly DelegateMarshaller Delegate = new(); + private static readonly SafeHandleMarshaller SafeHandle = new(); + private InteropGenerationOptions Options { get; } + + public DefaultMarshallingGeneratorFactory(InteropGenerationOptions options) + { + this.Options = options; + } + + /// + /// Create an instance for marshalling the supplied type in the given position. + /// + /// Type details + /// Metadata about the stub the type is associated with + /// A instance. + public IMarshallingGenerator Create( + TypePositionInfo info, + StubCodeContext context) + { + switch (info) + { + // Blittable primitives with no marshalling info or with a compatible [MarshalAs] attribute. + case { ManagedType: SpecialTypeInfo { SpecialType: SpecialType.System_SByte }, MarshallingAttributeInfo: NoMarshallingInfo or MarshalAsInfo(UnmanagedType.I1, _) } + or { ManagedType: SpecialTypeInfo { SpecialType: SpecialType.System_Byte }, MarshallingAttributeInfo: NoMarshallingInfo or MarshalAsInfo(UnmanagedType.U1, _) } + or { ManagedType: SpecialTypeInfo { SpecialType: SpecialType.System_Int16 }, MarshallingAttributeInfo: NoMarshallingInfo or MarshalAsInfo(UnmanagedType.I2, _) } + or { ManagedType: SpecialTypeInfo { SpecialType: SpecialType.System_UInt16 }, MarshallingAttributeInfo: NoMarshallingInfo or MarshalAsInfo(UnmanagedType.U2, _) } + or { ManagedType: SpecialTypeInfo { SpecialType: SpecialType.System_Int32 }, MarshallingAttributeInfo: NoMarshallingInfo or MarshalAsInfo(UnmanagedType.I4, _) } + or { ManagedType: SpecialTypeInfo { SpecialType: SpecialType.System_UInt32 }, MarshallingAttributeInfo: NoMarshallingInfo or MarshalAsInfo(UnmanagedType.U4, _) } + or { ManagedType: SpecialTypeInfo { SpecialType: SpecialType.System_Int64 }, MarshallingAttributeInfo: NoMarshallingInfo or MarshalAsInfo(UnmanagedType.I8, _) } + or { ManagedType: SpecialTypeInfo { SpecialType: SpecialType.System_UInt64 }, MarshallingAttributeInfo: NoMarshallingInfo or MarshalAsInfo(UnmanagedType.U8, _) } + or { ManagedType: SpecialTypeInfo { SpecialType: SpecialType.System_IntPtr }, MarshallingAttributeInfo: NoMarshallingInfo or MarshalAsInfo(UnmanagedType.SysInt, _) } + or { ManagedType: SpecialTypeInfo { SpecialType: SpecialType.System_UIntPtr }, MarshallingAttributeInfo: NoMarshallingInfo or MarshalAsInfo(UnmanagedType.SysUInt, _) } + or { ManagedType: SpecialTypeInfo { SpecialType: SpecialType.System_Single }, MarshallingAttributeInfo: NoMarshallingInfo or MarshalAsInfo(UnmanagedType.R4, _) } + or { ManagedType: SpecialTypeInfo { SpecialType: SpecialType.System_Double }, MarshallingAttributeInfo: NoMarshallingInfo or MarshalAsInfo(UnmanagedType.R8, _) }: + return Blittable; + + // Enum with no marshalling info + case { ManagedType: EnumTypeInfo enumType, MarshallingAttributeInfo: NoMarshallingInfo }: + // Check that the underlying type is not bool or char. C# does not allow this, but ECMA-335 does. + var underlyingSpecialType = enumType.UnderlyingType; + if (underlyingSpecialType == SpecialType.System_Boolean || underlyingSpecialType == SpecialType.System_Char) + { + throw new MarshallingNotSupportedException(info, context); + } + return Blittable; + + // Pointer with no marshalling info + case { ManagedType: PointerTypeInfo(_, _, IsFunctionPointer: false), MarshallingAttributeInfo: NoMarshallingInfo }: + return Blittable; + + // Function pointer with no marshalling info + case { ManagedType: PointerTypeInfo(_, _, IsFunctionPointer: true), MarshallingAttributeInfo: NoMarshallingInfo or MarshalAsInfo(UnmanagedType.FunctionPtr, _) }: + return Blittable; + + case { ManagedType: SpecialTypeInfo { SpecialType: SpecialType.System_Boolean }, MarshallingAttributeInfo: NoMarshallingInfo }: + return WinBool; // [Compat] Matching the default for the built-in runtime marshallers. + case { ManagedType: SpecialTypeInfo { SpecialType: SpecialType.System_Boolean }, MarshallingAttributeInfo: MarshalAsInfo(UnmanagedType.I1 or UnmanagedType.U1, _) }: + return ByteBool; + case { ManagedType: SpecialTypeInfo { SpecialType: SpecialType.System_Boolean }, MarshallingAttributeInfo: MarshalAsInfo(UnmanagedType.I4 or UnmanagedType.U4 or UnmanagedType.Bool, _) }: + return WinBool; + case { ManagedType: SpecialTypeInfo { SpecialType: SpecialType.System_Boolean }, MarshallingAttributeInfo: MarshalAsInfo(UnmanagedType.VariantBool, _) }: + return VariantBool; + + case { ManagedType: DelegateTypeInfo, MarshallingAttributeInfo: NoMarshallingInfo or MarshalAsInfo(UnmanagedType.FunctionPtr, _) }: + return Delegate; + + case { MarshallingAttributeInfo: SafeHandleMarshallingInfo(_, bool isAbstract) }: + if (!context.AdditionalTemporaryStateLivesAcrossStages) + { + throw new MarshallingNotSupportedException(info, context); + } + if (info.IsByRef && isAbstract) + { + throw new MarshallingNotSupportedException(info, context) + { + NotSupportedDetails = Resources.SafeHandleByRefMustBeConcrete + }; + } + return new SafeHandleMarshaller(); + + case { ManagedType: SpecialTypeInfo { SpecialType: SpecialType.System_Char } }: + return CreateCharMarshaller(info, context); + + case { ManagedType: SpecialTypeInfo { SpecialType: SpecialType.System_String } }: + return CreateStringMarshaller(info, context); + + case { ManagedType: SpecialTypeInfo { SpecialType: SpecialType.System_Void } }: + return Forwarder; + + default: + throw new MarshallingNotSupportedException(info, context); + } + } + + private IMarshallingGenerator CreateCharMarshaller(TypePositionInfo info, StubCodeContext context) + { + MarshallingInfo marshalInfo = info.MarshallingAttributeInfo; + if (marshalInfo is NoMarshallingInfo) + { + // [Compat] Require explicit marshalling information. + throw new MarshallingNotSupportedException(info, context) + { + NotSupportedDetails = Resources.MarshallingStringOrCharAsUndefinedNotSupported + }; + } + + // Explicit MarshalAs takes precedence over string encoding info + if (marshalInfo is MarshalAsInfo marshalAsInfo) + { + switch (marshalAsInfo.UnmanagedType) + { + case UnmanagedType.I2: + case UnmanagedType.U2: + return Utf16Char; + } + } + else if (marshalInfo is MarshallingInfoStringSupport marshalStringInfo) + { + switch (marshalStringInfo.CharEncoding) + { + case CharEncoding.Utf16: + return Utf16Char; + case CharEncoding.Ansi: + throw new MarshallingNotSupportedException(info, context) // [Compat] ANSI is not supported for char + { + NotSupportedDetails = string.Format(Resources.MarshallingCharAsSpecifiedCharSetNotSupported, CharSet.Ansi) + }; + case CharEncoding.PlatformDefined: + throw new MarshallingNotSupportedException(info, context) // [Compat] See conversion of CharSet.Auto. + { + NotSupportedDetails = string.Format(Resources.MarshallingCharAsSpecifiedCharSetNotSupported, CharSet.Auto) + }; + } + } + + throw new MarshallingNotSupportedException(info, context); + } + + private IMarshallingGenerator CreateStringMarshaller(TypePositionInfo info, StubCodeContext context) + { + MarshallingInfo marshalInfo = info.MarshallingAttributeInfo; + if (marshalInfo is NoMarshallingInfo) + { + // [Compat] Require explicit marshalling information. + throw new MarshallingNotSupportedException(info, context) + { + NotSupportedDetails = Resources.MarshallingStringOrCharAsUndefinedNotSupported + }; + } + + // Explicit MarshalAs takes precedence over string encoding info + if (marshalInfo is MarshalAsInfo marshalAsInfo) + { + switch (marshalAsInfo.UnmanagedType) + { + case UnmanagedType.LPStr: + return AnsiString; + case UnmanagedType.LPTStr: + case UnmanagedType.LPWStr: + return Utf16String; + case (UnmanagedType)0x30:// UnmanagedType.LPUTF8Str + return Utf8String; + } + } + else if (marshalInfo is MarshallingInfoStringSupport marshalStringInfo) + { + switch (marshalStringInfo.CharEncoding) + { + case CharEncoding.Ansi: + return AnsiString; + case CharEncoding.Utf16: + return Utf16String; + case CharEncoding.Utf8: + return Utf8String; + case CharEncoding.PlatformDefined: + return PlatformDefinedString; + } + } + + throw new MarshallingNotSupportedException(info, context); + } + } +} diff --git a/DllImportGenerator/DllImportGenerator/Marshalling/PinnableManagedValueMarshaller.cs b/DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/PinnableManagedValueMarshaller.cs similarity index 97% rename from DllImportGenerator/DllImportGenerator/Marshalling/PinnableManagedValueMarshaller.cs rename to DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/PinnableManagedValueMarshaller.cs index e14cfbf25b27..d8e26f8d6334 100644 --- a/DllImportGenerator/DllImportGenerator/Marshalling/PinnableManagedValueMarshaller.cs +++ b/DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/PinnableManagedValueMarshaller.cs @@ -5,7 +5,7 @@ namespace Microsoft.Interop { - internal sealed class PinnableManagedValueMarshaller : IMarshallingGenerator + public sealed class PinnableManagedValueMarshaller : IMarshallingGenerator { private readonly IMarshallingGenerator manualMarshallingGenerator; diff --git a/DllImportGenerator/DllImportGenerator/Marshalling/SafeHandleMarshaller.cs b/DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/SafeHandleMarshaller.cs similarity index 98% rename from DllImportGenerator/DllImportGenerator/Marshalling/SafeHandleMarshaller.cs rename to DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/SafeHandleMarshaller.cs index e3b32f94a5c6..262de1be2d9b 100644 --- a/DllImportGenerator/DllImportGenerator/Marshalling/SafeHandleMarshaller.cs +++ b/DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/SafeHandleMarshaller.cs @@ -8,15 +8,8 @@ namespace Microsoft.Interop { - internal class SafeHandleMarshaller : IMarshallingGenerator + public sealed class SafeHandleMarshaller : IMarshallingGenerator { - private readonly AnalyzerConfigOptions options; - - public SafeHandleMarshaller(AnalyzerConfigOptions options) - { - this.options = options; - } - public TypeSyntax AsNativeType(TypePositionInfo info) { return MarshallerHelpers.SystemIntPtrType; diff --git a/DllImportGenerator/DllImportGenerator/Marshalling/StringMarshaller.Ansi.cs b/DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/StringMarshaller.Ansi.cs similarity index 98% rename from DllImportGenerator/DllImportGenerator/Marshalling/StringMarshaller.Ansi.cs rename to DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/StringMarshaller.Ansi.cs index a63ecd4ab7f8..97dd9328c875 100644 --- a/DllImportGenerator/DllImportGenerator/Marshalling/StringMarshaller.Ansi.cs +++ b/DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/StringMarshaller.Ansi.cs @@ -9,7 +9,7 @@ namespace Microsoft.Interop { - internal sealed class AnsiStringMarshaller : ConditionalStackallocMarshallingGenerator + public sealed class AnsiStringMarshaller : ConditionalStackallocMarshallingGenerator { private static readonly TypeSyntax NativeType = PointerType(PredefinedType(Token(SyntaxKind.ByteKeyword))); diff --git a/DllImportGenerator/DllImportGenerator/Marshalling/StringMarshaller.PlatformDefined.cs b/DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/StringMarshaller.PlatformDefined.cs similarity index 98% rename from DllImportGenerator/DllImportGenerator/Marshalling/StringMarshaller.PlatformDefined.cs rename to DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/StringMarshaller.PlatformDefined.cs index a9b665f88b09..3de4c48a724b 100644 --- a/DllImportGenerator/DllImportGenerator/Marshalling/StringMarshaller.PlatformDefined.cs +++ b/DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/StringMarshaller.PlatformDefined.cs @@ -10,7 +10,7 @@ namespace Microsoft.Interop { - internal sealed class PlatformDefinedStringMarshaller : ConditionalStackallocMarshallingGenerator + public sealed class PlatformDefinedStringMarshaller : ConditionalStackallocMarshallingGenerator { private static readonly TypeSyntax NativeType = PointerType(PredefinedType(Token(SyntaxKind.VoidKeyword))); diff --git a/DllImportGenerator/DllImportGenerator/Marshalling/StringMarshaller.Utf16.cs b/DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/StringMarshaller.Utf16.cs similarity index 99% rename from DllImportGenerator/DllImportGenerator/Marshalling/StringMarshaller.Utf16.cs rename to DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/StringMarshaller.Utf16.cs index 9ae9bb48c454..a1ceb12d0adf 100644 --- a/DllImportGenerator/DllImportGenerator/Marshalling/StringMarshaller.Utf16.cs +++ b/DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/StringMarshaller.Utf16.cs @@ -8,7 +8,7 @@ namespace Microsoft.Interop { - internal sealed class Utf16StringMarshaller : ConditionalStackallocMarshallingGenerator + public sealed class Utf16StringMarshaller : ConditionalStackallocMarshallingGenerator { // [Compat] Equivalent of MAX_PATH on Windows to match built-in system // The assumption is file paths are the most common case for marshalling strings, diff --git a/DllImportGenerator/DllImportGenerator/Marshalling/StringMarshaller.Utf8.cs b/DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/StringMarshaller.Utf8.cs similarity index 99% rename from DllImportGenerator/DllImportGenerator/Marshalling/StringMarshaller.Utf8.cs rename to DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/StringMarshaller.Utf8.cs index a4c52bffa9a2..2a4801e8d3c3 100644 --- a/DllImportGenerator/DllImportGenerator/Marshalling/StringMarshaller.Utf8.cs +++ b/DllImportGenerator/Microsoft.Interop.SourceGeneration/Marshalling/StringMarshaller.Utf8.cs @@ -8,7 +8,7 @@ namespace Microsoft.Interop { - internal sealed class Utf8StringMarshaller : ConditionalStackallocMarshallingGenerator + public sealed class Utf8StringMarshaller : ConditionalStackallocMarshallingGenerator { // [Compat] Equivalent of MAX_PATH on Windows to match built-in system // The assumption is file paths are the most common case for marshalling strings, diff --git a/DllImportGenerator/DllImportGenerator/MarshallingAttributeInfo.cs b/DllImportGenerator/Microsoft.Interop.SourceGeneration/MarshallingAttributeInfo.cs similarity index 96% rename from DllImportGenerator/DllImportGenerator/MarshallingAttributeInfo.cs rename to DllImportGenerator/Microsoft.Interop.SourceGeneration/MarshallingAttributeInfo.cs index 96988216005c..bbc2d7789145 100644 --- a/DllImportGenerator/DllImportGenerator/MarshallingAttributeInfo.cs +++ b/DllImportGenerator/Microsoft.Interop.SourceGeneration/MarshallingAttributeInfo.cs @@ -12,7 +12,7 @@ namespace Microsoft.Interop /// /// Type used to pass on default marshalling details. /// - internal sealed record DefaultMarshallingInfo( + public sealed record DefaultMarshallingInfo( CharEncoding CharEncoding ); @@ -20,11 +20,15 @@ CharEncoding CharEncoding // for C# vNext discriminated unions. Once discriminated unions are released, // these should be updated to be implemented as a discriminated union. - internal abstract record MarshallingInfo + public abstract record MarshallingInfo { + // Add a constructor that can only be called by derived types in the same assembly + // to enforce that this type cannot be extended by users of this library. + private protected MarshallingInfo() + {} } - internal sealed record NoMarshallingInfo : MarshallingInfo + public sealed record NoMarshallingInfo : MarshallingInfo { public static readonly MarshallingInfo Instance = new NoMarshallingInfo(); @@ -34,7 +38,7 @@ private NoMarshallingInfo() { } /// /// Character encoding enumeration. /// - internal enum CharEncoding + public enum CharEncoding { Undefined, Utf8, @@ -46,14 +50,14 @@ internal enum CharEncoding /// /// Details that are required when scenario supports strings. /// - internal record MarshallingInfoStringSupport( + public record MarshallingInfoStringSupport( CharEncoding CharEncoding ) : MarshallingInfo; /// /// Simple User-application of System.Runtime.InteropServices.MarshalAsAttribute /// - internal sealed record MarshalAsInfo( + public sealed record MarshalAsInfo( UnmanagedType UnmanagedType, CharEncoding CharEncoding) : MarshallingInfoStringSupport(CharEncoding) { @@ -64,10 +68,10 @@ internal sealed record MarshalAsInfo( /// or System.Runtime.InteropServices.GeneratedMarshallingAttribute on a blittable type /// in source in this compilation. /// - internal sealed record BlittableTypeAttributeInfo : MarshallingInfo; + public sealed record BlittableTypeAttributeInfo : MarshallingInfo; [Flags] - internal enum CustomMarshallingFeatures + public enum CustomMarshallingFeatures { None = 0, ManagedToNative = 0x1, @@ -78,23 +82,26 @@ internal enum CustomMarshallingFeatures FreeNativeResources = 0x20, } - internal abstract record CountInfo; + public abstract record CountInfo + { + private protected CountInfo() {} + } - internal sealed record NoCountInfo : CountInfo + public sealed record NoCountInfo : CountInfo { public static readonly NoCountInfo Instance = new NoCountInfo(); private NoCountInfo() { } } - internal sealed record ConstSizeCountInfo(int Size) : CountInfo; + public sealed record ConstSizeCountInfo(int Size) : CountInfo; - internal sealed record CountElementCountInfo(TypePositionInfo ElementInfo) : CountInfo + public sealed record CountElementCountInfo(TypePositionInfo ElementInfo) : CountInfo { public const string ReturnValueElementName = "return-value"; } - internal sealed record SizeAndParamIndexInfo(int ConstSize, TypePositionInfo? ParamAtIndex) : CountInfo + public sealed record SizeAndParamIndexInfo(int ConstSize, TypePositionInfo? ParamAtIndex) : CountInfo { public const int UnspecifiedConstSize = -1; @@ -106,7 +113,7 @@ internal sealed record SizeAndParamIndexInfo(int ConstSize, TypePositionInfo? Pa /// /// User-applied System.Runtime.InteropServices.NativeMarshallingAttribute /// - internal record NativeMarshallingAttributeInfo( + public record NativeMarshallingAttributeInfo( ManagedTypeInfo NativeMarshallingType, ManagedTypeInfo? ValuePropertyType, CustomMarshallingFeatures MarshallingFeatures, @@ -116,18 +123,18 @@ internal record NativeMarshallingAttributeInfo( /// User-applied System.Runtime.InteropServices.GeneratedMarshallingAttribute /// on a non-blittable type in source in this compilation. /// - internal sealed record GeneratedNativeMarshallingAttributeInfo( + public sealed record GeneratedNativeMarshallingAttributeInfo( string NativeMarshallingFullyQualifiedTypeName) : MarshallingInfo; /// /// The type of the element is a SafeHandle-derived type with no marshalling attributes. /// - internal sealed record SafeHandleMarshallingInfo(bool AccessibleDefaultConstructor, bool IsAbstract) : MarshallingInfo; + public sealed record SafeHandleMarshallingInfo(bool AccessibleDefaultConstructor, bool IsAbstract) : MarshallingInfo; /// /// User-applied System.Runtime.InteropServices.NativeMarshallingAttribute /// with a contiguous collection marshaller - internal sealed record NativeContiguousCollectionMarshallingInfo( + public sealed record NativeContiguousCollectionMarshallingInfo( ManagedTypeInfo NativeMarshallingType, ManagedTypeInfo? ValuePropertyType, CustomMarshallingFeatures MarshallingFeatures, @@ -141,10 +148,10 @@ internal sealed record NativeContiguousCollectionMarshallingInfo( UseDefaultMarshalling ); - internal class MarshallingAttributeInfoParser + public sealed class MarshallingAttributeInfoParser { private readonly Compilation _compilation; - private readonly GeneratorDiagnostics _diagnostics; + private readonly IGeneratorDiagnostics _diagnostics; private readonly DefaultMarshallingInfo _defaultInfo; private readonly ISymbol _contextSymbol; private readonly ITypeSymbol _marshalAsAttribute; @@ -152,7 +159,7 @@ internal class MarshallingAttributeInfoParser public MarshallingAttributeInfoParser( Compilation compilation, - GeneratorDiagnostics diagnostics, + IGeneratorDiagnostics diagnostics, DefaultMarshallingInfo defaultInfo, ISymbol contextSymbol) { diff --git a/DllImportGenerator/Microsoft.Interop.SourceGeneration/Microsoft.Interop.SourceGeneration.csproj b/DllImportGenerator/Microsoft.Interop.SourceGeneration/Microsoft.Interop.SourceGeneration.csproj new file mode 100644 index 000000000000..8317b0a45f8e --- /dev/null +++ b/DllImportGenerator/Microsoft.Interop.SourceGeneration/Microsoft.Interop.SourceGeneration.csproj @@ -0,0 +1,30 @@ + + + + netstandard2.0 + false + enable + Microsoft.Interop + + + + + + + + + + Resources.resx + True + True + + + + + + Designer + Resources.Designer.cs + ResXFileCodeGenerator + + + diff --git a/DllImportGenerator/Microsoft.Interop.SourceGeneration/Resources.Designer.cs b/DllImportGenerator/Microsoft.Interop.SourceGeneration/Resources.Designer.cs new file mode 100644 index 000000000000..1d254e29a5c2 --- /dev/null +++ b/DllImportGenerator/Microsoft.Interop.SourceGeneration/Resources.Designer.cs @@ -0,0 +1,369 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Microsoft.Interop { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [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. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.Interop.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Marshalling an array from unmanaged to managed requires either the 'SizeParamIndex' or 'SizeConst' fields to be set on a 'MarshalAsAttribute' or the 'ConstantElementCount' or 'CountElementName' properties to be set on a 'MarshalUsingAttribute'.. + /// + internal static string ArraySizeMustBeSpecified { + get { + return ResourceManager.GetString("ArraySizeMustBeSpecified", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The 'SizeParamIndex' value in the 'MarshalAsAttribute' is out of range.. + /// + internal static string ArraySizeParamIndexOutOfRange { + get { + return ResourceManager.GetString("ArraySizeParamIndexOutOfRange", 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 The specified collection size parameter for an collection must be an integer type. If the size information is applied to a nested collection, the size parameter must be a collection of one less level of nesting with an integral element.. + /// + internal static string CollectionSizeParamTypeMustBeIntegral { + get { + return ResourceManager.GetString("CollectionSizeParamTypeMustBeIntegral", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Only one of 'ConstantElementCount' or 'ElementCountInfo' may be used in a 'MarshalUsingAttribute' for a given 'ElementIndirectionLevel'. + /// + internal static string ConstantAndElementCountInfoDisallowed { + get { + return ResourceManager.GetString("ConstantAndElementCountInfoDisallowed", 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.. + /// + internal static string CustomTypeMarshallingManagedToNativeUnsupported { + get { + 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.. + /// + internal static string CustomTypeMarshallingNativeToManagedUnsupported { + get { + return ResourceManager.GetString("CustomTypeMarshallingNativeToManagedUnsupported", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to This element cannot depend on '{0}' for collection size information without creating a dependency cycle. + /// + internal static string CyclicalCountInfo { + get { + return ResourceManager.GetString("CyclicalCountInfo", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Count information for a given element at a given indirection level can only be specified once. + /// + internal static string DuplicateCountInfo { + get { + return ResourceManager.GetString("DuplicateCountInfo", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Multiple marshalling attributes per element per indirection level is unsupported, but duplicate information was provided for indirection level {0}. + /// + internal static string DuplicateMarshallingInfo { + get { + return ResourceManager.GetString("DuplicateMarshallingInfo", resourceCulture); + } + } + + /// + /// 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. + /// + internal static string ExtraneousMarshallingInfo { + get { + return ResourceManager.GetString("ExtraneousMarshallingInfo", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The provided graph has cycles and cannot be topologically sorted.. + /// + internal static string GraphHasCycles { + get { + return ResourceManager.GetString("GraphHasCycles", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The '[In]' attribute is not supported unless the '[Out]' attribute is also used. The behavior of the '[In]' attribute without the '[Out]' attribute is the same as the default behavior.. + /// + internal static string InAttributeNotSupportedWithoutOut { + get { + return ResourceManager.GetString("InAttributeNotSupportedWithoutOut", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The '[In]' and '[Out]' attributes are unsupported on parameters passed by reference. Use the 'in', 'ref', or 'out' keywords instead.. + /// + internal static string InOutAttributeByRefNotSupported { + get { + return ResourceManager.GetString("InOutAttributeByRefNotSupported", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The provided '[In]' and '[Out]' attributes on this parameter are unsupported on this parameter.. + /// + internal static string InOutAttributeMarshalerNotSupported { + get { + return ResourceManager.GetString("InOutAttributeMarshalerNotSupported", 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.. + /// + internal static string MarshallingCharAsSpecifiedCharSetNotSupported { + get { + return ResourceManager.GetString("MarshallingCharAsSpecifiedCharSetNotSupported", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Marshalling string or char without explicit marshalling information is not supported. Specify either 'GeneratedDllImportAttribute.CharSet' or 'MarshalAsAttribute'.. + /// + internal static string MarshallingStringOrCharAsUndefinedNotSupported { + get { + return ResourceManager.GetString("MarshallingStringOrCharAsUndefinedNotSupported", 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.. + /// + internal static string NativeGenericTypeMustBeClosedOrMatchArityMessage { + get { + return ResourceManager.GetString("NativeGenericTypeMustBeClosedOrMatchArityMessage", 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.. + /// + 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.. + /// + internal static string OutByValueNotSupportedDescription { + get { + return ResourceManager.GetString("OutByValueNotSupportedDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The '[Out]' attribute is not supported on the '{0}' parameter.. + /// + internal static string OutByValueNotSupportedMessage { + get { + return ResourceManager.GetString("OutByValueNotSupportedMessage", resourceCulture); + } + } + + /// + /// 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 An abstract type derived from 'SafeHandle' cannot be marshalled by reference. The provided type must be concrete.. + /// + internal static string SafeHandleByRefMustBeConcrete { + get { + return ResourceManager.GetString("SafeHandleByRefMustBeConcrete", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Specified type is not supported by source-generated P/Invokes. + /// + internal static string TypeNotSupportedTitle { + get { + 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/DllImportGenerator/Microsoft.Interop.SourceGeneration/Resources.resx b/DllImportGenerator/Microsoft.Interop.SourceGeneration/Resources.resx new file mode 100644 index 000000000000..544a1ea46a21 --- /dev/null +++ b/DllImportGenerator/Microsoft.Interop.SourceGeneration/Resources.resx @@ -0,0 +1,222 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Marshalling an array from unmanaged to managed requires either the 'SizeParamIndex' or 'SizeConst' fields to be set on a 'MarshalAsAttribute' or the 'ConstantElementCount' or 'CountElementName' properties to be set on a 'MarshalUsingAttribute'. + + + The 'SizeParamIndex' value in the 'MarshalAsAttribute' is out of range. + + + 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. + + + The specified collection size parameter for an collection must be an integer type. If the size information is applied to a nested collection, the size parameter must be a collection of one less level of nesting with an integral element. + + + The specified parameter needs to be marshalled from managed to native, but the native type '{0}' does not support it. + + + The specified parameter needs to be marshalled from native to managed, but the native type '{0}' does not support it. + + + The provided graph has cycles and cannot be topologically sorted. + + + The '[In]' attribute is not supported unless the '[Out]' attribute is also used. The behavior of the '[In]' attribute without the '[Out]' attribute is the same as the default behavior. + + + The '[In]' and '[Out]' attributes are unsupported on parameters passed by reference. Use the 'in', 'ref', or 'out' keywords instead. + + + The provided '[In]' and '[Out]' attributes on this parameter are unsupported on this parameter. + + + 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. + + + Marshalling string or char without explicit marshalling information is not supported. Specify either 'GeneratedDllImportAttribute.CharSet' or 'MarshalAsAttribute'. + + + The '[Out]' attribute is only supported on array parameters. + + + 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 + + + Count information for a given element at a given indirection level can only be specified once + + + 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 + + + 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. + + + 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' + + \ No newline at end of file diff --git a/DllImportGenerator/DllImportGenerator/StubCodeContext.cs b/DllImportGenerator/Microsoft.Interop.SourceGeneration/StubCodeContext.cs similarity index 98% rename from DllImportGenerator/DllImportGenerator/StubCodeContext.cs rename to DllImportGenerator/Microsoft.Interop.SourceGeneration/StubCodeContext.cs index 1fcacdb07a72..ab414c504269 100644 --- a/DllImportGenerator/DllImportGenerator/StubCodeContext.cs +++ b/DllImportGenerator/Microsoft.Interop.SourceGeneration/StubCodeContext.cs @@ -3,7 +3,7 @@ namespace Microsoft.Interop { - internal abstract class StubCodeContext + public abstract class StubCodeContext { /// /// Code generation stage diff --git a/DllImportGenerator/Microsoft.Interop.SourceGeneration/TypeNames.cs b/DllImportGenerator/Microsoft.Interop.SourceGeneration/TypeNames.cs new file mode 100644 index 000000000000..a0bb19753fa4 --- /dev/null +++ b/DllImportGenerator/Microsoft.Interop.SourceGeneration/TypeNames.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.Interop; + +namespace Microsoft.Interop +{ + static class TypeNames + { + public const string GeneratedDllImportAttribute = "System.Runtime.InteropServices.GeneratedDllImportAttribute"; + + public const string GeneratedMarshallingAttribute = "System.Runtime.InteropServices.GeneratedMarshallingAttribute"; + + public const string BlittableTypeAttribute = "System.Runtime.InteropServices.BlittableTypeAttribute"; + + public const string NativeMarshallingAttribute = "System.Runtime.InteropServices.NativeMarshallingAttribute"; + + public const string MarshalUsingAttribute = "System.Runtime.InteropServices.MarshalUsingAttribute"; + + public const string GenericContiguousCollectionMarshallerAttribute = "System.Runtime.InteropServices.GenericContiguousCollectionMarshallerAttribute"; + + public const string LCIDConversionAttribute = "System.Runtime.InteropServices.LCIDConversionAttribute"; + + public const string System_Span_Metadata = "System.Span`1"; + public const string System_Span = "System.Span"; + + public const string System_Activator = "System.Activator"; + + public const string System_Runtime_InteropServices_StructLayoutAttribute = "System.Runtime.InteropServices.StructLayoutAttribute"; + + public const string System_Runtime_InteropServices_MarshalAsAttribute = "System.Runtime.InteropServices.MarshalAsAttribute"; + + public const string System_Runtime_InteropServices_Marshal = "System.Runtime.InteropServices.Marshal"; + + private const string System_Runtime_InteropServices_MarshalEx = "System.Runtime.InteropServices.MarshalEx"; + + public static string MarshalEx(InteropGenerationOptions options) + { + return options.UseMarshalType ? System_Runtime_InteropServices_Marshal : System_Runtime_InteropServices_MarshalEx; + } + + public const string System_Runtime_InteropServices_UnmanagedType = "System.Runtime.InteropServices.UnmanagedType"; + + public const string System_Runtime_InteropServices_MemoryMarshal = "System.Runtime.InteropServices.MemoryMarshal"; + + public const string System_Runtime_InteropServices_GeneratedMarshalling_ArrayMarshaller_Metadata = "System.Runtime.InteropServices.GeneratedMarshalling.ArrayMarshaller`1"; + + public const string System_Runtime_InteropServices_GeneratedMarshalling_PtrArrayMarshaller_Metadata = "System.Runtime.InteropServices.GeneratedMarshalling.PtrArrayMarshaller`1"; + + public const string System_Runtime_InteropServices_SafeHandle = "System.Runtime.InteropServices.SafeHandle"; + + public const string System_Runtime_InteropServices_OutAttribute = "System.Runtime.InteropServices.OutAttribute"; + + public const string System_Runtime_InteropServices_InAttribute = "System.Runtime.InteropServices.InAttribute"; + + public const string System_Runtime_CompilerServices_SkipLocalsInitAttribute = "System.Runtime.CompilerServices.SkipLocalsInitAttribute"; + + private const string System_Runtime_CompilerServices_Unsafe = "System.Runtime.CompilerServices.Unsafe"; + + private const string Internal_Runtime_CompilerServices_Unsafe = "Internal.Runtime.CompilerServices.Unsafe"; + + public static string Unsafe(InteropGenerationOptions options) + { + return options.UseInternalUnsafeType ? Internal_Runtime_CompilerServices_Unsafe : System_Runtime_CompilerServices_Unsafe; + } + } +} diff --git a/DllImportGenerator/DllImportGenerator/TypePositionInfo.cs b/DllImportGenerator/Microsoft.Interop.SourceGeneration/TypePositionInfo.cs similarity index 96% rename from DllImportGenerator/DllImportGenerator/TypePositionInfo.cs rename to DllImportGenerator/Microsoft.Interop.SourceGeneration/TypePositionInfo.cs index 2cbbcb87becc..a7d730d08b08 100644 --- a/DllImportGenerator/DllImportGenerator/TypePositionInfo.cs +++ b/DllImportGenerator/Microsoft.Interop.SourceGeneration/TypePositionInfo.cs @@ -15,7 +15,7 @@ namespace Microsoft.Interop /// contents of the managed array. /// [Flags] - internal enum ByValueContentsMarshalKind + public enum ByValueContentsMarshalKind { /// /// Marshal contents from managed to native only. @@ -40,7 +40,7 @@ internal enum ByValueContentsMarshalKind /// /// Positional type information involved in unmanaged/managed scenarios. /// - internal sealed record TypePositionInfo(ManagedTypeInfo ManagedType, MarshallingInfo MarshallingAttributeInfo) + public sealed record TypePositionInfo(ManagedTypeInfo ManagedType, MarshallingInfo MarshallingAttributeInfo) { public const int UnsetIndex = int.MinValue; public const int ReturnIndex = UnsetIndex + 1; diff --git a/DllImportGenerator/DllImportGenerator/TypeSymbolExtensions.cs b/DllImportGenerator/Microsoft.Interop.SourceGeneration/TypeSymbolExtensions.cs similarity index 99% rename from DllImportGenerator/DllImportGenerator/TypeSymbolExtensions.cs rename to DllImportGenerator/Microsoft.Interop.SourceGeneration/TypeSymbolExtensions.cs index e9b3b8261a95..8587c0780acc 100644 --- a/DllImportGenerator/DllImportGenerator/TypeSymbolExtensions.cs +++ b/DllImportGenerator/Microsoft.Interop.SourceGeneration/TypeSymbolExtensions.cs @@ -11,7 +11,7 @@ namespace Microsoft.Interop { - static class TypeSymbolExtensions + public static class TypeSymbolExtensions { public static bool HasOnlyBlittableFields(this ITypeSymbol type) => HasOnlyBlittableFields(type, ImmutableHashSet.Create(SymbolEqualityComparer.Default));