-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Use the same method to generate DllImportAttribute in local methods and P/Invoke forwarders #103973
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
Changes from all commits
edcd284
1171df1
f04cb71
ca2d486
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
|
@@ -422,7 +422,7 @@ private static MemberDeclarationSyntax PrintForwarderStub(ContainingSyntax userD | |||
.AddAttributeLists( | ||||
AttributeList( | ||||
SingletonSeparatedList( | ||||
CreateForwarderDllImport(pinvokeData)))); | ||||
CreateDllImportAttribute(pinvokeData)))); | ||||
|
||||
MemberDeclarationSyntax toPrint = stub.ContainingSyntaxContext.WrapMemberInContainingSyntaxWithUnsafeModifier(stubMethod); | ||||
|
||||
|
@@ -438,37 +438,24 @@ private static LocalFunctionStatementSyntax CreateTargetDllImportAsLocalStatemen | |||
{ | ||||
Debug.Assert(!options.GenerateForwarders, "GenerateForwarders should have already been handled to use a forwarder stub"); | ||||
|
||||
// StringMarshalling.Utf16 is the only value that has a supported analogue in DllImportAttribute.CharSet. If it's not Utf16, consider StringMarshalling unset | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Shouldn't it have errored in this case? I know we have a test around down-level forwarding of Line 242 in 0015867
Or am I missing something about dotnet/runtime-only down-level versus down-level support? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It should error in down-level forwarding of UTF-8 and StringMarshalling.Custom, but this code path is taken in the normal path of LibraryImport in net7+, so we don't want to unconditionally warn here. Ideally this should warn when we hit this path for down-level support with partial marshalling, but we don't right now either. |
||||
var stringMarshallingMask = libraryImportData.StringMarshalling == StringMarshalling.Utf16 ? InteropAttributeMember.All : ~InteropAttributeMember.StringMarshalling; | ||||
var dllImportData = libraryImportData with | ||||
{ | ||||
// If setLastError was set in LibraryImport, we will call the Marshal API to handle that, the DllImport should not. | ||||
IsUserDefined = libraryImportData.IsUserDefined & ~InteropAttributeMember.SetLastError & stringMarshallingMask, | ||||
EntryPoint = libraryImportData.EntryPoint ?? stubMethodName | ||||
}; | ||||
|
||||
(ParameterListSyntax parameterList, TypeSyntax returnType, AttributeListSyntax returnTypeAttributes) = stubGenerator.GenerateTargetMethodSignatureData(); | ||||
LocalFunctionStatementSyntax localDllImport = LocalFunctionStatement(returnType, stubTargetName) | ||||
.AddModifiers( | ||||
Token(SyntaxKind.StaticKeyword), | ||||
Token(SyntaxKind.ExternKeyword), | ||||
Token(SyntaxKind.UnsafeKeyword)) | ||||
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken)) | ||||
.WithAttributeLists( | ||||
SingletonList(AttributeList( | ||||
SingletonSeparatedList( | ||||
Attribute( | ||||
NameSyntaxes.DllImportAttribute, | ||||
AttributeArgumentList( | ||||
SeparatedList( | ||||
new[] | ||||
{ | ||||
AttributeArgument(LiteralExpression( | ||||
SyntaxKind.StringLiteralExpression, | ||||
Literal(libraryImportData.ModuleName))), | ||||
AttributeArgument( | ||||
NameEquals(nameof(DllImportAttribute.EntryPoint)), | ||||
null, | ||||
LiteralExpression( | ||||
SyntaxKind.StringLiteralExpression, | ||||
Literal(libraryImportData.EntryPoint ?? stubMethodName))), | ||||
AttributeArgument( | ||||
NameEquals(nameof(DllImportAttribute.ExactSpelling)), | ||||
null, | ||||
LiteralExpression(SyntaxKind.TrueLiteralExpression)) | ||||
} | ||||
))))))) | ||||
.WithAttributeLists(SingletonList(AttributeList(SingletonSeparatedList( | ||||
CreateDllImportAttribute(dllImportData))))) | ||||
.WithParameterList(parameterList); | ||||
if (returnTypeAttributes is not null) | ||||
{ | ||||
|
@@ -477,7 +464,7 @@ private static LocalFunctionStatementSyntax CreateTargetDllImportAsLocalStatemen | |||
return localDllImport; | ||||
} | ||||
|
||||
private static AttributeSyntax CreateForwarderDllImport(LibraryImportData target) | ||||
private static AttributeSyntax CreateDllImportAttribute(LibraryImportData target) | ||||
{ | ||||
var newAttributeArgs = new List<AttributeArgumentSyntax> | ||||
{ | ||||
|
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
|
@@ -531,6 +531,48 @@ public static IEnumerable<object[]> CodeSnippetsToValidateFallbackForwarder() | |||
} | ||||
} | ||||
|
||||
public static IEnumerable<object[]> CodeSnippetsToValidateCharSetPresent() | ||||
{ | ||||
// Confirm that Charset=... is present | ||||
{ | ||||
string code = CodeSnippets.BasicParametersAndModifiersWithStringMarshalling( | ||||
"System.Text.StringBuilder", | ||||
StringMarshalling.Utf16, | ||||
CodeSnippets.LibraryImportAttributeDeclaration); | ||||
yield return new object[] { ID(), code, TestTargetFramework.Net6 }; | ||||
yield return new object[] { ID(), code, TestTargetFramework.Core }; | ||||
yield return new object[] { ID(), code, TestTargetFramework.Standard }; | ||||
yield return new object[] { ID(), code, TestTargetFramework.Framework }; | ||||
} | ||||
{ | ||||
string code = CodeSnippets.BasicParameterWithStringMarshalling( | ||||
"int", | ||||
"System.Text.StringBuilder", | ||||
"Microsoft.Win32.SafeHandles.SafeHandleZeroOrMinusOneIsInvalid", | ||||
StringMarshalling.Utf16, | ||||
CodeSnippets.LibraryImportAttributeDeclaration); | ||||
yield return new object[] { ID(), code, TestTargetFramework.Net6 }; | ||||
yield return new object[] { ID(), code, TestTargetFramework.Core }; | ||||
yield return new object[] { ID(), code, TestTargetFramework.Standard }; | ||||
yield return new object[] { ID(), code, TestTargetFramework.Framework }; | ||||
} | ||||
} | ||||
|
||||
[Theory] | ||||
[MemberData(nameof(CodeSnippetsToValidateCharSetPresent))] | ||||
[OuterLoop("Uses the network for downlevel ref packs")] | ||||
public async Task ValidateSnippetsCharSetPresent(string id, string source, TestTargetFramework targetFramework) | ||||
{ | ||||
TestUtils.Use(id); | ||||
var test = new CharSetIsForwardedTest(targetFramework) | ||||
{ | ||||
TestCode = source, | ||||
TestBehaviors = TestBehaviors.SkipGeneratedSourcesCheck | ||||
}; | ||||
|
||||
await test.RunAsync(); | ||||
} | ||||
|
||||
[Theory] | ||||
[MemberData(nameof(CodeSnippetsToValidateFallbackForwarder))] | ||||
[OuterLoop("Uses the network for downlevel ref packs")] | ||||
|
@@ -546,6 +588,25 @@ public async Task ValidateSnippetsFallbackForwarder(string id, string source, Te | |||
await test.RunAsync(); | ||||
} | ||||
|
||||
class CharSetIsForwardedTest : VerifyCS.Test | ||||
{ | ||||
public CharSetIsForwardedTest(TestTargetFramework targetFramework) | ||||
: base(targetFramework) | ||||
{ | ||||
} | ||||
|
||||
protected override void VerifyFinalCompilation(Compilation compilation) | ||||
{ | ||||
SyntaxTree generatedCode = compilation.SyntaxTrees.Last(); | ||||
SemanticModel model = compilation.GetSemanticModel(generatedCode); | ||||
var hasCharSet = generatedCode.GetRoot() | ||||
.DescendantNodes().OfType<AttributeSyntax>() | ||||
.Where(att => att.Name.ToString().EndsWith(nameof(DllImportAttribute))) | ||||
.All(att => att.ArgumentList?.Arguments.Any(a => a.NameEquals?.Name.ToString().StartsWith(nameof(DllImportAttribute.CharSet)) == true) == true); | ||||
Assert.True(hasCharSet); | ||||
} | ||||
} | ||||
|
||||
class FallbackForwarderTest : VerifyCS.Test | ||||
{ | ||||
private readonly bool _expectFallbackForwarder; | ||||
|
@@ -724,7 +785,7 @@ public class Basic { } | |||
await test.RunAsync(); | ||||
} | ||||
|
||||
|
||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||
[OuterLoop("Uses the network for downlevel ref packs")] | ||||
[InlineData(TestTargetFramework.Standard)] | ||||
[InlineData(TestTargetFramework.Framework)] | ||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unsure if this is worth quibbling about, but
CharSet.Ansi
on non-Windows is Utf8 if I recall correctly. Based on certain reg keys on Windows it can also be Utf8. I don't know if this makes a difference in this case, but it is something to note.