Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor DllImportGenerator project for easier extensibility #1119

Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
ffe2f22
Move marshalling generators and the marshalling generator infra into …
jkoritzinsky May 12, 2021
aa619bf
Fix build.
jkoritzinsky May 12, 2021
363cb2e
Use decorator pattern instead of inheritance to handle customizations…
jkoritzinsky May 13, 2021
3d2b9dc
Remove default parameter value.
jkoritzinsky May 13, 2021
458ccab
Convert Microsoft.Interop.SourceGeneration to a source package.
jkoritzinsky May 17, 2021
a31ac5f
Fix visibility and resource weirdness.
jkoritzinsky May 18, 2021
22a372e
Add a non-boilerplate description for Ancillary.Interop to satisfy pa…
jkoritzinsky May 18, 2021
e18a5e0
Merge branch 'feature/DllImportGenerator' of github.com:dotnet/runtim…
jkoritzinsky May 18, 2021
d2050fd
Revert "Fix visibility and resource weirdness."
jkoritzinsky May 21, 2021
0a3354b
Revert "Convert Microsoft.Interop.SourceGeneration to a source package."
jkoritzinsky May 21, 2021
7df4da2
Update CodeAnalysisAnalyzers version.
jkoritzinsky May 24, 2021
2af6835
Reference CodeAnalysis.Analyzers from the shared code package.
jkoritzinsky May 24, 2021
6211e32
Merge branch 'feature/DllImportGenerator' of github.com:dotnet/runtim…
jkoritzinsky Jun 11, 2021
28615eb
Merge branch 'feature/DllImportGenerator' of github.com:dotnet/runtim…
jkoritzinsky Jun 16, 2021
a11f0f7
Refactor out the new marshalling model into its own IMarshallingGener…
jkoritzinsky Jun 17, 2021
60f298c
Merge branch 'feature/DllImportGenerator' of github.com:dotnet/runtim…
jkoritzinsky Sep 8, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions DllImportGenerator/Benchmarks/Benchmarks.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
<ItemGroup>
<ProjectReference Include="..\Ancillary.Interop\Ancillary.Interop.csproj" />
<ProjectReference Include="..\DllImportGenerator\DllImportGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
<ProjectReference Include="..\Microsoft.Interop.SourceGeneration\Microsoft.Interop.SourceGeneration.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
<ProjectReference Include="..\TestAssets\NativeExports\NativeExports.csproj" />
<ProjectReference Include="..\TestAssets\SharedTypes\SharedTypes.csproj" />
</ItemGroup>
Expand Down
1 change: 1 addition & 0 deletions DllImportGenerator/Demo/Demo.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
<ItemGroup>
<ProjectReference Include="..\Ancillary.Interop\Ancillary.Interop.csproj" />
<ProjectReference Include="..\DllImportGenerator\DllImportGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
<ProjectReference Include="..\Microsoft.Interop.SourceGeneration\Microsoft.Interop.SourceGeneration.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
<ProjectReference Include="..\TestAssets\NativeExports\NativeExports.csproj" />
</ItemGroup>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
<ItemGroup>
<ProjectReference Include="..\Ancillary.Interop\Ancillary.Interop.csproj" />
<ProjectReference Include="..\DllImportGenerator\DllImportGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
<ProjectReference Include="..\Microsoft.Interop.SourceGeneration\Microsoft.Interop.SourceGeneration.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
<ProjectReference Include="..\TestAssets\NativeExports\NativeExports.csproj" />
<ProjectReference Include="..\TestAssets\SharedTypes\SharedTypes.csproj" />
</ItemGroup>
Expand Down
8 changes: 7 additions & 1 deletion DllImportGenerator/DllImportGenerator.sln
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SharedTypes", "TestAssets\S
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TestAssets", "TestAssets", "{C7134A89-8852-4FBE-B426-EFE007C4A819}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Benchmarks", "Benchmarks\Benchmarks.csproj", "{0914590B-C47A-4754-A7BE-E4CD843A92E4}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Benchmarks", "Benchmarks\Benchmarks.csproj", "{0914590B-C47A-4754-A7BE-E4CD843A92E4}"
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
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public void Execute(GeneratorExecutionContext context)
generatorDiagnostics.ReportTargetFrameworkNotSupported(MinimumSupportedFrameworkVersion);
}

var env = new StubEnvironment(context.Compilation, isSupported, targetFrameworkVersion, context.AnalyzerConfigOptions.GlobalOptions);
var env = new StubEnvironment(context.Compilation, isSupported, targetFrameworkVersion, new DllImportGeneratorOptions(context.AnalyzerConfigOptions.GlobalOptions));
var generatedDllImports = new StringBuilder();
foreach (SyntaxReference synRef in synRec.Methods)
{
Expand Down
16 changes: 13 additions & 3 deletions DllImportGenerator/DllImportGenerator/DllImportGenerator.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,12 @@
</ItemGroup>

<ItemGroup>
<None Remove="bin\Debug\netstandard2.0\\DllImportGenerator.dll" />
<None Include="$(OutputPath)\$(AssemblyName).dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
<None Include="Microsoft.Interop.DllImportGenerator.props" Pack="true" PackagePath="build" />
</ItemGroup>

<ItemGroup>
<None Include="$(OutputPath)\$(AssemblyName).dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
<None Include="Microsoft.Interop.DllImportGenerator.props" Pack="true" PackagePath="build" />
<ProjectReference Include="..\Microsoft.Interop.SourceGeneration\Microsoft.Interop.SourceGeneration.csproj" />
</ItemGroup>

<ItemGroup>
Expand All @@ -57,4 +57,14 @@
</EmbeddedResource>
</ItemGroup>

<Target Name="IncludeProjectReferenceInPackage" BeforeTargets="_GetPackageFiles">
<MSBuild Projects="@(ProjectReference)" Targets="Build">
<Output TaskParameter="TargetOutputs" ItemName="ProjectReferenceOutput" />
</MSBuild>

<ItemGroup>
<None Include="@(ProjectReferenceOutput)" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
</ItemGroup>
</Target>

</Project>
Original file line number Diff line number Diff line change
@@ -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) : InteropGenerationOptions(UseMarshalType)
{
public DllImportGeneratorOptions(AnalyzerConfigOptions options)
: this(options.GenerateForwarders(), options.UseMarshalType())
{
}
}

public static class OptionsHelper
{
public const string UseMarshalTypeOption = "build_property.DllImportGenerator_UseMarshalType";
Expand Down
169 changes: 164 additions & 5 deletions DllImportGenerator/DllImportGenerator/DllImportStub.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ internal record StubEnvironment(
Compilation Compilation,
bool SupportedTargetFramework,
Version TargetFrameworkVersion,
AnalyzerConfigOptions Options);
DllImportGeneratorOptions Options);

internal class DllImportStub
{
Expand Down Expand Up @@ -181,7 +181,7 @@ public static DllImportStub Create(
var managedRetTypeInfo = retTypeInfo;
// Do not manually handle PreserveSig when generating forwarders.
// We want the runtime to handle everything.
if (!dllImportData.PreserveSig && !env.Options.GenerateForwarders())
if (!dllImportData.PreserveSig && !env.Options.GenerateForwarders)
{
// Create type info for native HRESULT return
retTypeInfo = TypePositionInfo.CreateForType(env.Compilation.GetSpecialType(SpecialType.System_Int32), NoMarshallingInfo.Instance);
Expand All @@ -196,7 +196,7 @@ public static DllImportStub Create(
// Transform the managed return type info into an out parameter and add it as the last param
TypePositionInfo nativeOutInfo = managedRetTypeInfo with
{
InstanceIdentifier = StubCodeGenerator.ReturnIdentifier,
InstanceIdentifier = PInvokeStubCodeGenerator.ReturnIdentifier,
RefKind = RefKind.Out,
RefKindSyntax = SyntaxKind.OutKeyword,
ManagedIndex = TypePositionInfo.ReturnIndex,
Expand All @@ -207,8 +207,16 @@ public static DllImportStub Create(
}

// Generate stub code
var stubGenerator = new StubCodeGenerator(method, dllImportData, paramsTypeInfo, retTypeInfo, diagnostics, env.Options);
var code = stubGenerator.GenerateSyntax();
var stubGenerator = new PInvokeStubCodeGenerator(
method,
paramsTypeInfo,
retTypeInfo,
diagnostics,
dllImportData.SetLastError && !env.Options.GenerateForwarders,
new GeneratedDllImportMarshallingGeneratorFactory(env.Options));
string stubTargetName = "__PInvoke__";
var code = stubGenerator.GeneratePInvokeBody(IdentifierName(stubTargetName));
code = code.AddStatements(CreateTargetFunctionAsLocalStatement(stubGenerator, env.Options, dllImportData, stubTargetName, method.Name));

var additionalAttrs = new List<AttributeListSyntax>();

Expand Down Expand Up @@ -236,5 +244,156 @@ public static DllImportStub Create(
AdditionalAttributes = additionalAttrs.ToArray(),
};
}

private static LocalFunctionStatementSyntax CreateTargetFunctionAsLocalStatement(
PInvokeStubCodeGenerator stubGenerator,
DllImportGeneratorOptions options,
GeneratedDllImportData dllImportData,
string stubTargetName,
string stubMethodName)
{
var (parameterList, returnType) = stubGenerator.GenerateTargetMethodSignatureData();
return 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);
}

private static AttributeSyntax CreateDllImportAttributeForTarget(GeneratedDllImportData targetDllImportData)
{
var newAttributeArgs = new List<AttributeArgumentSyntax>
{
AttributeArgument(LiteralExpression(
SyntaxKind.StringLiteralExpression,
Literal(targetDllImportData.ModuleName))),
AttributeArgument(
NameEquals(nameof(DllImportAttribute.EntryPoint)),
null,
CreateStringExpressionSyntax(targetDllImportData.EntryPoint))
};

if (targetDllImportData.IsUserDefined.HasFlag(DllImportStub.DllImportMember.BestFitMapping))
{
var name = NameEquals(nameof(DllImportAttribute.BestFitMapping));
var value = CreateBoolExpressionSyntax(targetDllImportData.BestFitMapping);
newAttributeArgs.Add(AttributeArgument(name, null, value));
}
if (targetDllImportData.IsUserDefined.HasFlag(DllImportStub.DllImportMember.CallingConvention))
{
var name = NameEquals(nameof(DllImportAttribute.CallingConvention));
var value = CreateEnumExpressionSyntax(targetDllImportData.CallingConvention);
newAttributeArgs.Add(AttributeArgument(name, null, value));
}
if (targetDllImportData.IsUserDefined.HasFlag(DllImportStub.DllImportMember.CharSet))
{
var name = NameEquals(nameof(DllImportAttribute.CharSet));
var value = CreateEnumExpressionSyntax(targetDllImportData.CharSet);
newAttributeArgs.Add(AttributeArgument(name, null, value));
}
if (targetDllImportData.IsUserDefined.HasFlag(DllImportStub.DllImportMember.ExactSpelling))
{
var name = NameEquals(nameof(DllImportAttribute.ExactSpelling));
var value = CreateBoolExpressionSyntax(targetDllImportData.ExactSpelling);
newAttributeArgs.Add(AttributeArgument(name, null, value));
}
if (targetDllImportData.IsUserDefined.HasFlag(DllImportStub.DllImportMember.PreserveSig))
{
var name = NameEquals(nameof(DllImportAttribute.PreserveSig));
var value = CreateBoolExpressionSyntax(targetDllImportData.PreserveSig);
newAttributeArgs.Add(AttributeArgument(name, null, value));
}
if (targetDllImportData.IsUserDefined.HasFlag(DllImportStub.DllImportMember.SetLastError))
{
var name = NameEquals(nameof(DllImportAttribute.SetLastError));
var value = CreateBoolExpressionSyntax(targetDllImportData.SetLastError);
newAttributeArgs.Add(AttributeArgument(name, null, value));
}
if (targetDllImportData.IsUserDefined.HasFlag(DllImportStub.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>(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 = DllImportStub.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.
& ~DllImportStub.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.
& ~DllImportStub.DllImportMember.SetLastError;
if (forwardAll)
{
membersToForward = DllImportStub.DllImportMember.All;
}

var targetDllImportData = new GeneratedDllImportData
{
CharSet = dllImportData.CharSet,
BestFitMapping = dllImportData.BestFitMapping,
CallingConvention = dllImportData.CallingConvention,
EntryPoint = dllImportData.EntryPoint,
ModuleName = dllImportData.ModuleName,
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(DllImportStub.DllImportMember.EntryPoint))
{
targetDllImportData.EntryPoint = originalMethodName;
}

return targetDllImportData;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using System;
using System.Collections.Generic;
using System.Text;

namespace Microsoft.Interop
{
class GeneratedDllImportMarshallingGeneratorFactory : DefaultMarshallingGeneratorFactory<DllImportGeneratorOptions>
{
public GeneratedDllImportMarshallingGeneratorFactory(DllImportGeneratorOptions options) : base(options) { }

protected override IMarshallingGenerator CreateCore(TypePositionInfo info, StubCodeContext context)
{
if (Options.GenerateForwarders)
{
return Forwarder;
}

if (info.IsNativeReturnPosition && !info.IsManagedReturnPosition)
{
// Use marshaller for native HRESULT return / exception throwing
System.Diagnostics.Debug.Assert(info.ManagedType.SpecialType == SpecialType.System_Int32);
return HResultException;
}

return base.CreateCore(info, context);
}
}
}
Loading