-
Notifications
You must be signed in to change notification settings - Fork 4.7k
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
Generate an 'out' local variable for unmanaged to managed stubs when the value needs to be marshalled out to unmanaged #89139
Conversation
…lingInterfaceCleanup
…lingInterfaceCleanup
…lingInterfaceCleanup
…lingInterfaceCleanup
…lingInterfaceCleanup
…lingInterfaceCleanup
…ld 20230716.1 (dotnet#89077)" This reverts commit 09e724f.
…slyn build 20230716.1 (dotnet#89077)"" This reverts commit 0a532ad.
…lingInterfaceCleanup
Tagging subscribers to this area: @dotnet/interop-contrib Issue DetailsGenerates an 'out' local variable that will hold the marshalled value that will be passed back on a successful return. Right now it just generates the variable and discards the value immediately. The next step is to actually use that variable name in marshalling and copy the value to the parameter right before a successful return. Using the variable just requires changing the MarshalToLocalContext to use the commented version of GetIdentifiers. Copying from the local to the parameter is a little more complicated because of the ByValue [Out] case. First step of #88438
|
...time.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs
Outdated
Show resolved
Hide resolved
...System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/GeneratedStatements.cs
Outdated
Show resolved
Hide resolved
...ies/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IRefStrings.cs
Outdated
Show resolved
Hide resolved
...ystem.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/VariableDeclarations.cs
Show resolved
Hide resolved
...ystem.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/VariableDeclarations.cs
Show resolved
Hide resolved
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.
Is this needed for a specific scenario or are we trying to be precise? I want to make sure we are weighting priorities correctly here.
@@ -110,6 +109,9 @@ public BlockSyntax GenerateStubBody(ExpressionSyntax methodToInvoke) | |||
allStatements.AddRange(tryStatements); | |||
} | |||
|
|||
allStatements.AddRange(statements.AssignOut); | |||
allStatements.AddRange(statements.Cleanup); |
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.
This order concerns me. We added the finally staements above after we added the cleanup. Inverting this seems like a profound change.
@@ -22,6 +22,8 @@ public struct GeneratedStatements | |||
public ImmutableArray<StatementSyntax> NotifyForSuccessfulInvoke { get; init; } | |||
public ImmutableArray<StatementSyntax> GuaranteedUnmarshal { get; init; } | |||
public ImmutableArray<StatementSyntax> Cleanup { get; init; } | |||
//public ImmutableArray<StatementSyntax> CleanupNativeOut { get; init; } |
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.
What are the next steps for commented out code? At a minimum we need to have that captured here so we know how to uncomment or delete it.
@@ -39,7 +41,9 @@ public static GeneratedStatements Create(BoundGenerators marshallers, StubCodeCo | |||
NotifyForSuccessfulInvoke = GenerateStatementsForStubContext(marshallers, context with { CurrentStage = StubCodeContext.Stage.NotifyForSuccessfulInvoke }), | |||
GuaranteedUnmarshal = GenerateStatementsForStubContext(marshallers, context with { CurrentStage = StubCodeContext.Stage.GuaranteedUnmarshal }), | |||
Cleanup = GenerateStatementsForStubContext(marshallers, context with { CurrentStage = StubCodeContext.Stage.Cleanup }), | |||
ManagedExceptionCatchClauses = GenerateCatchClauseForManagedException(marshallers, context) | |||
//CleanupNativeOut = GenerateStatementsForStubContext(marshallers, new MarshalToLocalContext(context with { CurrentStage = StubCodeContext.Stage.Cleanup })), |
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.
Again with commented out code.
if (!MarshallerHelpers.MarshalsOutToLocal(marshaller.TypeInfo, context)) | ||
continue; | ||
else | ||
localContext = new AssignOutContext(context, marshaller.Generator.AsParameter(marshaller.TypeInfo, context).Identifier.ToString()); |
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.
if (!MarshallerHelpers.MarshalsOutToLocal(marshaller.TypeInfo, context)) | |
continue; | |
else | |
localContext = new AssignOutContext(context, marshaller.Generator.AsParameter(marshaller.TypeInfo, context).Identifier.ToString()); | |
if (!MarshallerHelpers.MarshalsOutToLocal(marshaller.TypeInfo, context)) | |
{ | |
continue; | |
} | |
localContext = new AssignOutContext(context, marshaller.Generator.AsParameter(marshaller.TypeInfo, context).Identifier.ToString()); |
//if (freeStrategy == FreeStrategy.FreeOriginal) | ||
//{ | ||
// marshallingStrategy = new UnmanagedToManagedOwnershipTrackingStrategy(marshallingStrategy); | ||
//} |
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.
I'll stop commenting, but undescribed commented out code is a red-flag in general.
if (context is MarshalToLocalContext | ||
&& (info.ByValueContentsMarshalKind.HasFlag(ByValueContentsMarshalKind.Out) | ||
|| info.RefKind is RefKind.Out)) | ||
break; | ||
else if (context.Direction is MarshalDirection.UnmanagedToManaged && info.RefKind is RefKind.Out) | ||
break; | ||
else | ||
return _nativeTypeMarshaller.GenerateCleanupStatements(info, context); |
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.
If/else chains require braces.
public override bool AdditionalTemporaryStateLivesAcrossStages => InnerContext.AdditionalTemporaryStateLivesAcrossStages; | ||
|
||
public override (string managed, string native) GetIdentifiers(TypePositionInfo info) | ||
//=> InnerContext.GetIdentifiers(info); |
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.
?
//if (freeStrategy == FreeStrategy.FreeOriginal) | ||
//{ | ||
// marshallingStrategy = new UnmanagedToManagedOwnershipTrackingStrategy(marshallingStrategy); | ||
//} |
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.
Please remove the commented-out code before merging
@@ -467,7 +467,8 @@ private enum FreeStrategy | |||
/// <summary> | |||
/// Do not free the unmanaged value, we don't own it. | |||
/// </summary> | |||
NoFree | |||
NoFree, | |||
FreeTemp |
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.
Can you add a comment here about what this strategy means?
// TODO: Correctly clean up the allocated contents that aren't transferred back to the caller | ||
// We don't correctly clean up the [out] parameters |
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.
Is this TODO still accurate?
statements.Add(numElementsAssignment); | ||
if (MarshallerHelpers.MarshalsOutToLocal(info, context)) | ||
{ | ||
// #pragma warning disable CS9081 |
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.
We shouldn't be suppressing this rule. This suppression likely covers up a use-out-of-scope bug. We should either declare native_out
as scoped
, or (if we can't), revisit how we do this. Can you share some example code that goes down this route?
if (MarshallerHelpers.MarshalsOutToLocal(info, context) && _isStateful) | ||
{ | ||
var elementType = _parameterPointedToType;//info.IsByRef ? PointerType(_unmanagedElementType) : _unmanagedElementType; | ||
// <value_native_out> = (<nativeType>*)Unsafe.AsPointer(ref <nativeSpan>.GetPinnableReference()); |
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.
Similar question here around using a "pinnable reference" without pinning. Are we sure that this works? If this is only in the "stateful element" case, we shouldn't add code that will do bad things and instead we should gracefully fail earlier.
// To simplify propogating back the value to the "byref" parameter, | ||
// we'll just declare the native identifier as a ref to its type. | ||
// The rest of the code we generate will work as expected, and we don't need | ||
// to manually propogate back the updated values after the call. |
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.
Looks like this comment is out of date now. We should remove it if it's no longer accurate.
// https://github.com/dotnet/runtime/issues/89265 | ||
//obj.Double(data, data.Length); |
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.
Can we split this case into a separate Xunit test and mark it with ActiveIssue instead of just commenting it out? Using ActiveIssue makes it easier to track/find the tests when we fix them.
@@ -281,6 +278,7 @@ public unsafe void ValidateArrayElementsByRefFreed_Stateful() | |||
} | |||
|
|||
[Fact] | |||
[ActiveIssue("Make issue: ByValueContents out do not get freed")] |
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.
Is this a new issue introduced by this PR or is this an existing issue?
// https://github.com/dotnet/runtime/issues/89265 | ||
//yield return new object[] { | ||
// ID(), | ||
// codeSnippets.ByValueMarshallingOfType(inAttribute + outAttribute + constElementCount, "int[]", paramNameWithLocation), | ||
// new DiagnosticResult[] { inOutAttributeIsDefaultDiagnostic } | ||
//}; |
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.
It looks like this test is passing in main. We're really late in the release cycle, so we shouldn't be disabling failing tests to get PRs in.
@@ -18,6 +18,13 @@ | |||
'$(TargetOS)' == 'tvossimulator'">true</_TargetsAppleOS> | |||
</PropertyGroup> | |||
|
|||
<Target Name="DisableComInterfaceGenerator" |
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.
Let's add a comment here about this target if we're going to merge it into main.
This is mainly to be precise and avoid memory leaks and potential double frees if marshalling fails. |
This PR requires a few test regressions that aren't palatable and isn't that much smaller than the full change, so I'll close this in favor of the complete change. |
Generates an 'out' local variable that will hold the marshalled value that will be passed back on a successful return. Right now it just generates the variable and discards the value immediately. The next step is to actually use that variable name in marshalling and copy the value to the parameter right before a successful return.
Using the variable just requires changing the MarshalToLocalContext to use the commented version of GetIdentifiers.
Copying from the local to the parameter is a little more complicated because of the ByValue [Out] case.
First step of #88438