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

Fix SafeHandle benchmarks and add string benchmarks #1238

Merged
419 changes: 419 additions & 0 deletions DllImportGenerator/Benchmarks/Strings.cs

Large diffs are not rendered by default.

96 changes: 93 additions & 3 deletions DllImportGenerator/DllImportGenerator/Marshalling/Forwarder.cs
Original file line number Diff line number Diff line change
@@ -1,23 +1,104 @@
ο»Ώ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
{
internal class Forwarder : IMarshallingGenerator
internal class Forwarder : IMarshallingGenerator, IAttributedReturnTypeMarshallingGenerator
{
public TypeSyntax AsNativeType(TypePositionInfo info)
{
return info.ManagedType.AsTypeSyntax();
}

private bool TryRehydrateMarshalAsAttribute(TypePositionInfo info, out AttributeSyntax marshalAsAttribute)
{
marshalAsAttribute = null!;
elinor-fung marked this conversation as resolved.
Show resolved Hide resolved
// If the parameter has [MarshalAs] marshalling, we resurface that
// in the forwarding target since the built-in system understands it.
// ICustomMarshaller marshalling requires additional information that we throw away earlier since it's unsupported,
// so explicitly do not resurface a [MarshalAs(UnmanagdType.CustomMarshaller)] attribute.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// so explicitly do not resurface a [MarshalAs(UnmanagdType.CustomMarshaller)] attribute.
// so explicitly do not resurface a [MarshalAs(UnmanagedType.CustomMarshaler)] attribute.

if (info.MarshallingAttributeInfo is MarshalAsInfo { UnmanagedType: not UnmanagedType.CustomMarshaler } marshalAs)
{
marshalAsAttribute = Attribute(ParseName(TypeNames.System_Runtime_InteropServices_MarshalAsAttribute))
.WithArgumentList(AttributeArgumentList(SingletonSeparatedList(AttributeArgument(
CastExpression(ParseTypeName(TypeNames.System_Runtime_InteropServices_UnmanagedType),
LiteralExpression(SyntaxKind.NumericLiteralExpression,
Literal((int)marshalAs.UnmanagedType)))))));
return true;
}

if (info.MarshallingAttributeInfo is NativeContiguousCollectionMarshallingInfo collectionMarshalling
&& collectionMarshalling.UseDefaultMarshalling
&& collectionMarshalling.ElementCountInfo is NoCountInfo or SizeAndParamIndexInfo
&& collectionMarshalling.ElementMarshallingInfo is NoMarshallingInfo or MarshalAsInfo { UnmanagedType: not UnmanagedType.CustomMarshaler }
&& info.ManagedType is IArrayTypeSymbol)
{
List<AttributeArgumentSyntax> marshalAsArguments = new List<AttributeArgumentSyntax>();
marshalAsArguments.Add(
AttributeArgument(
CastExpression(ParseTypeName(TypeNames.System_Runtime_InteropServices_UnmanagedType),
LiteralExpression(SyntaxKind.NumericLiteralExpression,
Literal((int)UnmanagedType.LPArray))))
);

if (collectionMarshalling.ElementCountInfo is SizeAndParamIndexInfo countInfo)
{
if (countInfo.ConstSize != SizeAndParamIndexInfo.UnspecifiedData)
{
marshalAsArguments.Add(
AttributeArgument(NameEquals("SizeConst"), null,
LiteralExpression(SyntaxKind.NumericLiteralExpression,
Literal(countInfo.ConstSize)))
);
}
if (countInfo.ParamIndex != SizeAndParamIndexInfo.UnspecifiedData)
{
marshalAsArguments.Add(
AttributeArgument(NameEquals("SizeParamIndex"), null,
LiteralExpression(SyntaxKind.NumericLiteralExpression,
Literal(countInfo.ParamIndex)))
);
}
}

if (collectionMarshalling.ElementMarshallingInfo is MarshalAsInfo elementMarshalAs)
{
marshalAsArguments.Add(
AttributeArgument(NameEquals("ArraySubType"), null,
CastExpression(ParseTypeName(TypeNames.System_Runtime_InteropServices_UnmanagedType),
LiteralExpression(SyntaxKind.NumericLiteralExpression,
Literal((int)elementMarshalAs.UnmanagedType))))
);
}
marshalAsAttribute = Attribute(ParseName(TypeNames.System_Runtime_InteropServices_MarshalAsAttribute))
.WithArgumentList(AttributeArgumentList(SeparatedList(marshalAsArguments)));
return true;
}

return false;
}

public ParameterSyntax AsParameter(TypePositionInfo info)
{
return Parameter(Identifier(info.InstanceIdentifier))
ParameterSyntax param = Parameter(Identifier(info.InstanceIdentifier))
.WithModifiers(TokenList(Token(info.RefKindSyntax)))
.WithType(info.ManagedType.AsTypeSyntax());

// If the parameter has [MarshalAs] marshalling, we resurface that
// in the forwarding target since the built-in system understands it.
// ICustomMarshaller marshalling requires additional information that we throw away earlier since it's unsupported,
// so explicitly do not resurface a [MarshalAs(UnmanagdType.CustomMarshaller)] attribute.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The repeated comment seams unnecessary, since it is right at the top of TryRehydrateMarshalAsAttribute?

if (TryRehydrateMarshalAsAttribute(info, out AttributeSyntax marshalAsAttribute))
{
param = param.AddAttributeLists(AttributeList(SingletonSeparatedList(marshalAsAttribute)));
}

return param;
}

public ArgumentSyntax AsArgument(TypePositionInfo info, StubCodeContext context)
Expand All @@ -34,5 +115,14 @@ public IEnumerable<StatementSyntax> Generate(TypePositionInfo info, StubCodeCont
public bool UsesNativeIdentifier(TypePositionInfo info, StubCodeContext context) => false;

public bool SupportsByValueMarshalKind(ByValueContentsMarshalKind marshalKind, StubCodeContext context) => true;

public AttributeListSyntax? GenerateAttributesForReturnType(TypePositionInfo info)
{
if (!TryRehydrateMarshalAsAttribute(info, out AttributeSyntax marshalAsAttribute))
{
return null;
}
return AttributeList(SingletonSeparatedList(marshalAsAttribute));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,19 @@ internal interface IMarshallingGenerator
bool SupportsByValueMarshalKind(ByValueContentsMarshalKind marshalKind, StubCodeContext context);
}

/// <summary>
/// Interface for generating attributes for native return types.
/// </summary>
internal interface IAttributedReturnTypeMarshallingGenerator : IMarshallingGenerator
{
/// <summary>
/// Gets any attributes that should be applied to the return type for this <paramref name="info"/>.
/// </summary>
/// <param name="info">Object to marshal</param>
/// <returns>Attributes for the return type for this <paramref name="info"/>, or <c>null</c> if no attributes should be added.</returns>
AttributeListSyntax? GenerateAttributesForReturnType(TypePositionInfo info);
}

/// <summary>
/// Exception used to indicate marshalling isn't supported.
/// </summary>
Expand Down
10 changes: 10 additions & 0 deletions DllImportGenerator/DllImportGenerator/StubCodeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,16 @@ public BlockSyntax GenerateSyntax()
.WithAttributeLists(
SingletonList(AttributeList(
SingletonSeparatedList(CreateDllImportAttributeForTarget(GetTargetDllImportDataFromStubData())))));

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"))));
}
}

foreach (var marshaller in paramMarshallers)
{
ParameterSyntax paramSyntax = marshaller.Generator.AsParameter(marshaller.TypeInfo);
Expand Down
2 changes: 2 additions & 0 deletions DllImportGenerator/DllImportGenerator/TypeNames.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ static class TypeNames

public const string System_Runtime_InteropServices_MarshalAsAttribute = "System.Runtime.InteropServices.MarshalAsAttribute";

public const string System_Runtime_InteropServices_UnmanagedType = "System.Runtime.InteropServices.UnmanagedType";

public const string System_Runtime_InteropServices_Marshal = "System.Runtime.InteropServices.Marshal";

private const string System_Runtime_InteropServices_MarshalEx = "System.Runtime.InteropServices.MarshalEx";
Expand Down
35 changes: 28 additions & 7 deletions DllImportGenerator/TestAssets/NativeExports/Handles.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,13 @@ public static void AllocateHandleOut(nint* handle)
[UnmanagedCallersOnly(EntryPoint = "release_handle")]
public static byte ReleaseHandle(nint handle)
{
return (byte)(ActiveHandles.Remove(handle) ? 1 : 0);
return (byte)(ReleaseHandleCore(handle) ? 1 : 0);
}

[UnmanagedCallersOnly(EntryPoint = "is_handle_alive")]
public static byte IsHandleAlive(nint handle)
{
return (byte)(ActiveHandles.Contains(handle) ? 1 : 0);
return (byte)(IsHandleAliveCore(handle) ? 1 : 0);
}

[UnmanagedCallersOnly(EntryPoint = "modify_handle")]
Expand All @@ -47,16 +47,37 @@ public static void ModifyHandle(nint* handle, byte newHandle)
}
}

private static object m_lock = new object();

private static nint AllocateHandleCore()
{
if (LastHandle == int.MaxValue)
lock (m_lock)
{
return InvalidHandle;
if (LastHandle == int.MaxValue)
{
return InvalidHandle;
}

nint newHandle = ++LastHandle;
ActiveHandles.Add(newHandle);
return newHandle;
}
}

nint newHandle = ++LastHandle;
ActiveHandles.Add(newHandle);
return newHandle;
private static bool IsHandleAliveCore(nint handle)
{
lock (m_lock)
{
return ActiveHandles.Contains(handle);
}
}

private static bool ReleaseHandleCore(nint handle)
{
lock (m_lock)
{
return ActiveHandles.Remove(handle);
}
}
}
}
2 changes: 1 addition & 1 deletion eng/Versions.props
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
<!-- Package dependencies -->
<SystemCommandLineVersion>2.0.0-beta1.21118.1</SystemCommandLineVersion>
<IcedVersion>1.8.0</IcedVersion>
<BenchmarkDotNetVersion>0.12.1.1528</BenchmarkDotNetVersion>
<BenchmarkDotNetVersion>0.13.0</BenchmarkDotNetVersion>
<CoverletVersion>1.3.0</CoverletVersion>
<DNNEVersion>1.0.16</DNNEVersion>
<!-- Set the custom NETCoreApp version based on the VS redist package version -->
Expand Down