forked from dotnet/runtime
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
SafeHandle marshalling (dotnet/runtimelab#133)
Co-authored-by: Jan Kotas <jkotas@microsoft.com> Commit migrated from dotnet/runtimelab@2cde5aa
- Loading branch information
1 parent
ff96442
commit ea4e979
Showing
18 changed files
with
460 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
230 changes: 230 additions & 0 deletions
230
...System.Runtime.InteropServices/gen/DllImportGenerator/Marshalling/SafeHandleMarshaller.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,230 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Runtime.InteropServices; | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.CSharp; | ||
using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; | ||
|
||
namespace Microsoft.Interop | ||
{ | ||
class SafeHandleMarshaller : IMarshallingGenerator | ||
{ | ||
public TypeSyntax AsNativeType(TypePositionInfo info) | ||
{ | ||
return ParseTypeName("global::System.IntPtr"); | ||
} | ||
|
||
public ParameterSyntax AsParameter(TypePositionInfo info) | ||
{ | ||
var type = info.IsByRef | ||
? PointerType(AsNativeType(info)) | ||
: AsNativeType(info); | ||
return Parameter(Identifier(info.InstanceIdentifier)) | ||
.WithType(type); | ||
} | ||
|
||
public ArgumentSyntax AsArgument(TypePositionInfo info, StubCodeContext context) | ||
{ | ||
string identifier = context.GetIdentifiers(info).native; | ||
if (info.IsByRef) | ||
{ | ||
return Argument( | ||
PrefixUnaryExpression( | ||
SyntaxKind.AddressOfExpression, | ||
IdentifierName(identifier))); | ||
} | ||
|
||
return Argument(IdentifierName(identifier)); | ||
} | ||
|
||
public IEnumerable<StatementSyntax> Generate(TypePositionInfo info, StubCodeContext context) | ||
{ | ||
// The high level logic (note that the parameter may be in, out or both): | ||
// 1) If this is an input parameter we need to AddRef the SafeHandle. | ||
// 2) If this is an output parameter we need to preallocate a SafeHandle to wrap the new native handle value. We | ||
// must allocate this before the native call to avoid a failure point when we already have a native resource | ||
// allocated. We must allocate a new SafeHandle even if we have one on input since both input and output native | ||
// handles need to be tracked and released by a SafeHandle. | ||
// 3) Initialize a local IntPtr that will be passed to the native call. If we have an input SafeHandle the value | ||
// comes from there otherwise we get it from the new SafeHandle (which is guaranteed to be initialized to an | ||
// invalid handle value). | ||
// 4) If this is a out parameter we also store the original handle value (that we just computed above) in a local | ||
// variable. | ||
// 5) If we successfully AddRef'd the incoming SafeHandle, we need to Release it before we return. | ||
// 6) After the native call, if this is an output parameter and the handle value we passed to native differs from | ||
// the local copy we made then the new handle value is written into the output SafeHandle and that SafeHandle | ||
// is propagated back to the caller. | ||
|
||
(string managedIdentifier, string nativeIdentifier) = context.GetIdentifiers(info); | ||
string addRefdIdentifier = $"{managedIdentifier}__addRefd"; | ||
string newHandleObjectIdentifier = info.IsManagedReturnPosition | ||
? managedIdentifier | ||
: $"{managedIdentifier}__newHandle"; | ||
string handleValueBackupIdentifier = $"{nativeIdentifier}__original"; | ||
switch (context.CurrentStage) | ||
{ | ||
case StubCodeContext.Stage.Setup: | ||
yield return LocalDeclarationStatement( | ||
VariableDeclaration( | ||
AsNativeType(info), | ||
SingletonSeparatedList( | ||
VariableDeclarator(nativeIdentifier)))); | ||
if (!info.IsManagedReturnPosition && info.RefKind != RefKind.Out) | ||
{ | ||
yield return LocalDeclarationStatement( | ||
VariableDeclaration( | ||
PredefinedType(Token(SyntaxKind.BoolKeyword)), | ||
SingletonSeparatedList( | ||
VariableDeclarator(addRefdIdentifier) | ||
.WithInitializer(EqualsValueClause(LiteralExpression(SyntaxKind.FalseLiteralExpression)))))); | ||
|
||
} | ||
if (info.IsByRef && info.RefKind != RefKind.In) | ||
{ | ||
// We create the new handle in the Setup phase | ||
// so we eliminate the possible failure points during unmarshalling, where we would | ||
// leak the handle if we failed to create the handle. | ||
yield return LocalDeclarationStatement( | ||
VariableDeclaration( | ||
info.ManagedType.AsTypeSyntax(), | ||
SingletonSeparatedList( | ||
VariableDeclarator(newHandleObjectIdentifier) | ||
.WithInitializer(EqualsValueClause( | ||
InvocationExpression( | ||
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, | ||
ParseName(TypeNames.System_Runtime_InteropServices_MarshalEx), | ||
GenericName(Identifier("CreateSafeHandle"), | ||
TypeArgumentList(SingletonSeparatedList(info.ManagedType.AsTypeSyntax())))), | ||
ArgumentList())))))); | ||
yield return LocalDeclarationStatement( | ||
VariableDeclaration( | ||
AsNativeType(info), | ||
SingletonSeparatedList( | ||
VariableDeclarator(handleValueBackupIdentifier) | ||
.WithInitializer(EqualsValueClause( | ||
InvocationExpression( | ||
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, | ||
IdentifierName(newHandleObjectIdentifier), | ||
IdentifierName(nameof(SafeHandle.DangerousGetHandle))), | ||
ArgumentList())))))); | ||
} | ||
else if (info.IsManagedReturnPosition) | ||
{ | ||
yield return ExpressionStatement( | ||
AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, | ||
IdentifierName(managedIdentifier), | ||
InvocationExpression( | ||
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, | ||
ParseName(TypeNames.System_Runtime_InteropServices_MarshalEx), | ||
GenericName(Identifier("CreateSafeHandle"), | ||
TypeArgumentList(SingletonSeparatedList(info.ManagedType.AsTypeSyntax())))), | ||
ArgumentList()))); | ||
} | ||
break; | ||
case StubCodeContext.Stage.Marshal: | ||
if (info.RefKind != RefKind.Out) | ||
{ | ||
// <managedIdentifier>.DangerousAddRef(ref <addRefdIdentifier>); | ||
yield return ExpressionStatement( | ||
InvocationExpression( | ||
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, | ||
IdentifierName(managedIdentifier), | ||
IdentifierName(nameof(SafeHandle.DangerousAddRef))), | ||
ArgumentList(SingletonSeparatedList( | ||
Argument(IdentifierName(addRefdIdentifier)) | ||
.WithRefKindKeyword(Token(SyntaxKind.RefKeyword)))))); | ||
|
||
|
||
ExpressionSyntax assignHandleToNativeExpression = | ||
AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, | ||
IdentifierName(nativeIdentifier), | ||
InvocationExpression( | ||
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, | ||
IdentifierName(managedIdentifier), | ||
IdentifierName(nameof(SafeHandle.DangerousGetHandle))), | ||
ArgumentList())); | ||
if (info.IsByRef && info.RefKind != RefKind.In) | ||
{ | ||
yield return ExpressionStatement( | ||
AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, | ||
IdentifierName(handleValueBackupIdentifier), | ||
assignHandleToNativeExpression)); | ||
} | ||
else | ||
{ | ||
yield return ExpressionStatement(assignHandleToNativeExpression); | ||
} | ||
} | ||
break; | ||
case StubCodeContext.Stage.GuaranteedUnmarshal: | ||
StatementSyntax unmarshalStatement = ExpressionStatement( | ||
InvocationExpression( | ||
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, | ||
ParseTypeName(TypeNames.System_Runtime_InteropServices_MarshalEx), | ||
IdentifierName("SetHandle")), | ||
ArgumentList(SeparatedList( | ||
new [] | ||
{ | ||
Argument(IdentifierName(newHandleObjectIdentifier)), | ||
Argument(IdentifierName(nativeIdentifier)) | ||
})))); | ||
|
||
if(info.IsManagedReturnPosition) | ||
{ | ||
yield return unmarshalStatement; | ||
} | ||
else if (info.RefKind == RefKind.Out) | ||
{ | ||
yield return unmarshalStatement; | ||
yield return ExpressionStatement( | ||
AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, | ||
IdentifierName(managedIdentifier), | ||
IdentifierName(newHandleObjectIdentifier))); | ||
} | ||
else if (info.RefKind == RefKind.Ref) | ||
{ | ||
// Decrement refcount on original SafeHandle if we addrefd | ||
yield return IfStatement( | ||
IdentifierName(addRefdIdentifier), | ||
ExpressionStatement( | ||
InvocationExpression( | ||
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, | ||
IdentifierName(managedIdentifier), | ||
IdentifierName(nameof(SafeHandle.DangerousRelease))), | ||
ArgumentList()))); | ||
|
||
// Do not unmarshal the handle if the value didn't change. | ||
yield return IfStatement( | ||
BinaryExpression(SyntaxKind.NotEqualsExpression, | ||
IdentifierName(handleValueBackupIdentifier), | ||
IdentifierName(nativeIdentifier)), | ||
Block( | ||
unmarshalStatement, | ||
ExpressionStatement( | ||
AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, | ||
IdentifierName(managedIdentifier), | ||
IdentifierName(newHandleObjectIdentifier))))); | ||
} | ||
break; | ||
case StubCodeContext.Stage.Cleanup: | ||
if (!info.IsByRef || info.RefKind == RefKind.In) | ||
{ | ||
yield return IfStatement( | ||
IdentifierName(addRefdIdentifier), | ||
ExpressionStatement( | ||
InvocationExpression( | ||
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, | ||
IdentifierName(managedIdentifier), | ||
IdentifierName(nameof(SafeHandle.DangerousRelease))), | ||
ArgumentList()))); | ||
} | ||
break; | ||
default: | ||
break; | ||
} | ||
} | ||
|
||
public bool UsesNativeIdentifier(TypePositionInfo info, StubCodeContext context) => true; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.