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

Update design and expose new attributes for the CustomTypeMarshaller support #71682

Merged
merged 9 commits into from
Jul 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 0 additions & 3 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -154,9 +154,6 @@ csharp_space_between_square_brackets = false
# License header
file_header_template = Licensed to the .NET Foundation under one or more agreements.\nThe .NET Foundation licenses this file to you under the MIT license.

# Marshaller type must have CustomTypeMarshallerAttribute attribute
dotnet_diagnostic.SYSLIB1056.severity = silent

# C++ Files
[*.{cpp,h,in}]
curly_bracket_next_line = true
Expand Down
234 changes: 112 additions & 122 deletions docs/design/libraries/LibraryImportGenerator/UserTypeMarshallingV2.md

Large diffs are not rendered by default.

2 changes: 0 additions & 2 deletions eng/generators.targets
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,6 @@
</PropertyGroup>

<ItemGroup Condition="'$(NetCoreAppCurrentTargetFrameworkMoniker)' == '$(TargetFrameworkMoniker)' and '$(IncludeLibraryImportGeneratorSources)' != 'false'">
<Compile Include="$(LibrariesProjectRoot)System.Runtime.InteropServices/tests/Ancillary.Interop/CustomMarshallerAttribute.cs" />
<Compile Include="$(LibrariesProjectRoot)System.Runtime.InteropServices/tests/Ancillary.Interop/Scenario.cs" />
</ItemGroup>
</Target>

Expand Down
2 changes: 1 addition & 1 deletion src/libraries/Common/src/Interop/Interop.Ldap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ internal struct SEC_WINNT_AUTH_IDENTITY_EX
public int packageListLength;

#if NET7_0_OR_GREATER
[CustomMarshaller(typeof(SEC_WINNT_AUTH_IDENTITY_EX), Scenario.ManagedToUnmanagedIn, typeof(Marshaller))]
[CustomMarshaller(typeof(SEC_WINNT_AUTH_IDENTITY_EX), MarshalMode.ManagedToUnmanagedIn, typeof(Marshaller))]
internal static class Marshaller
{
public static Native ConvertToUnmanaged(SEC_WINNT_AUTH_IDENTITY_EX managed)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ internal struct CRYPTUI_VIEWCERTIFICATE_STRUCTW
internal uint nStartPage;

#if NET7_0_OR_GREATER
[CustomMarshaller(typeof(CRYPTUI_VIEWCERTIFICATE_STRUCTW), Scenario.Default, typeof(Marshaller))]
[CustomMarshaller(typeof(CRYPTUI_VIEWCERTIFICATE_STRUCTW), MarshalMode.Default, typeof(Marshaller))]
public static class Marshaller
{
public static Native ConvertToUnmanaged(CRYPTUI_VIEWCERTIFICATE_STRUCTW managed) => new(managed);
Expand Down Expand Up @@ -152,7 +152,7 @@ internal struct CRYPTUI_SELECTCERTIFICATE_STRUCTW
internal IntPtr hSelectedCertStore;

#if NET7_0_OR_GREATER
[CustomMarshaller(typeof(CRYPTUI_SELECTCERTIFICATE_STRUCTW), Scenario.Default, typeof(Marshaller))]
[CustomMarshaller(typeof(CRYPTUI_SELECTCERTIFICATE_STRUCTW), MarshalMode.Default, typeof(Marshaller))]
public static class Marshaller
{
public static Native ConvertToUnmanaged(CRYPTUI_SELECTCERTIFICATE_STRUCTW managed) => new(managed);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public static partial bool WinHttpAddRequestHeaders(
uint modifiers);

#if NET7_0_OR_GREATER
[CustomMarshaller(typeof(StringBuilder), Scenario.ManagedToUnmanagedIn, typeof(SimpleStringBufferMarshaller))]
[CustomMarshaller(typeof(StringBuilder), MarshalMode.ManagedToUnmanagedIn, typeof(SimpleStringBufferMarshaller))]
private static unsafe class SimpleStringBufferMarshaller
{
public static void* ConvertToUnmanaged(StringBuilder builder)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ public struct WINHTTP_AUTOPROXY_OPTIONS
[MarshalAs(UnmanagedType.Bool)]
public bool AutoLoginIfChallenged;
#if NET7_0_OR_GREATER
[CustomMarshaller(typeof(WINHTTP_AUTOPROXY_OPTIONS), Scenario.Default, typeof(Marshaller))]
[CustomMarshaller(typeof(WINHTTP_AUTOPROXY_OPTIONS), MarshalMode.Default, typeof(Marshaller))]
public static class Marshaller
{
public static Native ConvertToUnmanaged(WINHTTP_AUTOPROXY_OPTIONS managed) => new(managed);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ internal struct WAVEOUTCAPS
private ushort wReserved1;
private ushort dwSupport;
#if NET7_0_OR_GREATER
[CustomMarshaller(typeof(WAVEOUTCAPS), Scenario.Default, typeof(Marshaller))]
[CustomMarshaller(typeof(WAVEOUTCAPS), MarshalMode.Default, typeof(Marshaller))]
public static class Marshaller
{
public static Native ConvertToUnmanaged(WAVEOUTCAPS managed) => new(managed);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ internal struct IPv6MulticastRequest
internal byte[] MulticastAddress; // IP address of group.
internal int InterfaceIndex; // Local interface index.

[CustomMarshaller(typeof(IPv6MulticastRequest), Scenario.Default, typeof(Marshaller))]
[CustomMarshaller(typeof(IPv6MulticastRequest), MarshalMode.Default, typeof(Marshaller))]
public static class Marshaller
{
public static Native ConvertToUnmanaged(IPv6MulticastRequest managed) => new(managed);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -703,7 +703,7 @@ internal struct EvtStringVariant
public uint Type;

#if NET7_0_OR_GREATER
[CustomMarshaller(typeof(EvtStringVariant), Scenario.Default, typeof(Marshaller))]
[CustomMarshaller(typeof(EvtStringVariant), MarshalMode.Default, typeof(Marshaller))]
public static class Marshaller
{
public static Native ConvertToUnmanaged(EvtStringVariant managed) => new(managed);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ internal sealed class DOCINFO
internal int fwType;

#if NET7_0_OR_GREATER
[CustomMarshaller(typeof(DOCINFO), Scenario.ManagedToUnmanagedIn, typeof(Marshaller))]
[CustomMarshaller(typeof(DOCINFO), MarshalMode.ManagedToUnmanagedIn, typeof(Marshaller))]
public static class Marshaller
{
public static Native ConvertToUnmanaged(DOCINFO managed) => new(managed);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -872,10 +872,12 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshalling\AnsiStringMarshaller.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshalling\ArrayMarshaller.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshalling\BStrStringMarshaller.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshalling\CustomMarshallerAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshalling\CustomTypeMarshallerAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshalling\CustomTypeMarshallerDirection.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshalling\CustomTypeMarshallerFeatures.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshalling\CustomTypeMarshallerKind.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshalling\MarshalMode.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshalling\MarshalUsingAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshalling\NativeMarshallingAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshalling\PointerArrayMarshaller.cs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#nullable enable

namespace System.Runtime.InteropServices.Marshalling
{
/// <summary>
/// Attribute to indicate an entry point type for defining a marshaller.
/// </summary>
[AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class, AllowMultiple = true)]
public sealed class CustomMarshallerAttribute : Attribute
{
/// <summary>
/// Create a <see cref="CustomMarshallerAttribute"/> instance.
/// </summary>
/// <param name="managedType">Managed type to marshal.</param>
/// <param name="marshalMode">The marshalling mode this attribute applies to.</param>
/// <param name="marshallerType">Type used for marshalling.</param>
public CustomMarshallerAttribute(Type managedType, MarshalMode marshalMode, Type marshallerType)
{
ManagedType = managedType;
MarshalMode = marshalMode;
MarshallerType = marshallerType;
}

/// <summary>
/// The managed type to marshal.
/// </summary>
public Type ManagedType { get; }
jkoritzinsky marked this conversation as resolved.
Show resolved Hide resolved

/// <summary>
/// The marshalling mode this attribute applies to.
/// </summary>
public MarshalMode MarshalMode { get; }

/// <summary>
/// Type used for marshalling.
/// </summary>
public Type MarshallerType { get; }

/// <summary>
/// Placeholder type for generic parameter
/// </summary>
public struct GenericPlaceholder
{
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,17 @@
#if MICROSOFT_INTEROP_SOURCEGENERATION
namespace Microsoft.Interop
#else
namespace System.Runtime.InteropServices
namespace System.Runtime.InteropServices.Marshalling
#endif
{
/// <summary>
/// An enumeration representing the different marshalling scenarios in our marshalling model.
/// An enumeration representing the different marshalling modes in our marshalling model.
/// </summary>
#if LIBRARYIMPORT_GENERATOR_TEST || MICROSOFT_INTEROP_SOURCEGENERATION
public
#else
internal
#endif
enum Scenario
public enum MarshalMode
{
/// <summary>
/// All scenarios. A marshaller specified with this scenario will be used if there is not a specific
/// marshaller specified for a given usage scenario.
/// All modes. A marshaller specified with this mode will be used if there is not a specific
/// marshaller specified for a given usage mode.
/// </summary>
Default,
/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,13 @@ private static void AnalyzeManagedTypeMarshallingInfo(

if (!hasCustomTypeMarshallerAttribute)
{
if (ManualTypeMarshallingHelper.HasEntryPointMarshallerAttribute(entryType))
{
// TEMPORARY: Don't analyze V2 analyzers in this method for now.
// We'll add support soon, but for now just don't emit warnings to use the V1 marshaller design
// when using the V2 design.
return;
}
context.ReportDiagnostic(
attributeData.CreateDiagnostic(
NativeTypeMustHaveCustomTypeMarshallerAttributeRule,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -358,13 +358,13 @@ private static IncrementalStubGenerationContext CalculateStubInformation(
// Since the char type in an array will not be part of the P/Invoke signature, we can
// use the regular blittable marshaller in all cases.
new CharMarshallingGeneratorFactory(generatorFactory, useBlittableMarshallerForUtf16: true),
new AttributedMarshallingModelOptions(runtimeMarshallingDisabled, Scenario.ElementIn, Scenario.ElementRef, Scenario.ElementOut));
new AttributedMarshallingModelOptions(runtimeMarshallingDisabled, MarshalMode.ElementIn, MarshalMode.ElementRef, MarshalMode.ElementOut));
// We don't need to include the later generator factories for collection elements
// as the later generator factories only apply to parameters.
generatorFactory = new AttributedMarshallingModelGeneratorFactory(
generatorFactory,
elementFactory,
new AttributedMarshallingModelOptions(runtimeMarshallingDisabled, Scenario.ManagedToUnmanagedIn, Scenario.ManagedToUnmanagedRef, Scenario.ManagedToUnmanagedOut));
new AttributedMarshallingModelOptions(runtimeMarshallingDisabled, MarshalMode.ManagedToUnmanagedIn, MarshalMode.ManagedToUnmanagedRef, MarshalMode.ManagedToUnmanagedOut));

generatorFactory = new ByValueContentsMarshalKindValidator(generatorFactory);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,32 +24,32 @@ public readonly record struct CustomTypeMarshallerData(
MarshallingInfo? CollectionElementMarshallingInfo);

public readonly record struct CustomTypeMarshallers(
ImmutableDictionary<Scenario, CustomTypeMarshallerData> Scenarios)
ImmutableDictionary<MarshalMode, CustomTypeMarshallerData> Modes)
{
public CustomTypeMarshallerData GetScenarioOrDefault(Scenario scenario)
public CustomTypeMarshallerData GetModeOrDefault(MarshalMode mode)
{
CustomTypeMarshallerData data;
if (Scenarios.TryGetValue(scenario, out data))
if (Modes.TryGetValue(mode, out data))
return data;

if (Scenarios.TryGetValue(Scenario.Default, out data))
if (Modes.TryGetValue(MarshalMode.Default, out data))
return data;

// TODO: Hard failure based on previous implementation
throw new InvalidOperationException();
}

public bool TryGetScenarioOrDefault(Scenario scenario, out CustomTypeMarshallerData data)
public bool TryGetModeOrDefault(MarshalMode mode, out CustomTypeMarshallerData data)
{
if (Scenarios.TryGetValue(scenario, out data))
if (Modes.TryGetValue(mode, out data))
return true;

return Scenarios.TryGetValue(Scenario.Default, out data);
return Modes.TryGetValue(MarshalMode.Default, out data);
}

public bool IsDefinedOrDefault(Scenario scenario)
public bool IsDefinedOrDefault(MarshalMode mode)
{
return Scenarios.ContainsKey(scenario) || Scenarios.ContainsKey(Scenario.Default);
return Modes.ContainsKey(mode) || Modes.ContainsKey(MarshalMode.Default);
}
}

Expand All @@ -73,7 +73,7 @@ private enum MarshallingDirection
public static bool IsLinearCollectionEntryPoint(INamedTypeSymbol entryPointType)
{
return entryPointType.IsGenericType
&& entryPointType.TypeParameters.Last().GetAttributes().Any(attr => attr.AttributeClass.ToDisplayString() == TypeNames.ElementUnmanagedTypeAttribute);
&& entryPointType.GetAttributes().Any(attr => attr.AttributeClass.ToDisplayString() == TypeNames.ContiguousCollectionMarshallerAttribute);
}

public static bool HasEntryPointMarshallerAttribute(ITypeSymbol entryPointType)
Expand Down Expand Up @@ -116,7 +116,7 @@ private static bool TryGetMarshallersFromEntryType(
// We expect a callback for getting the element marshalling info when handling linear collection marshalling
Debug.Assert(!isLinearCollectionMarshalling || getMarshallingInfoForElement is not null);

Dictionary<Scenario, CustomTypeMarshallerData> scenarios = new();
Dictionary<MarshalMode, CustomTypeMarshallerData> modes = new();
foreach (AttributeData attr in attrs)
{
Debug.Assert(attr.ConstructorArguments.Length == 3);
Expand All @@ -136,7 +136,7 @@ private static bool TryGetMarshallersFromEntryType(
&& !compilation.HasImplicitConversion(managedType, managedTypeInst))
return false;

var marshallerScenario = (Scenario)attr.ConstructorArguments[1].Value!;
var marshalMode = (MarshalMode)attr.ConstructorArguments[1].Value!;

ITypeSymbol? marshallerTypeOnAttr = attr.ConstructorArguments[2].Value as ITypeSymbol;
if (marshallerTypeOnAttr is null)
Expand Down Expand Up @@ -168,51 +168,51 @@ private static bool TryGetMarshallersFromEntryType(
}

// TODO: We can probably get rid of MarshallingDirection and just use Scenario instead
MarshallingDirection direction = marshallerScenario switch
MarshallingDirection direction = marshalMode switch
{
Scenario.Default
MarshalMode.Default
=> MarshallingDirection.Bidirectional,

Scenario.ManagedToUnmanagedIn
or Scenario.UnmanagedToManagedOut
MarshalMode.ManagedToUnmanagedIn
or MarshalMode.UnmanagedToManagedOut
=> MarshallingDirection.ManagedToUnmanaged,

Scenario.ManagedToUnmanagedOut
or Scenario.UnmanagedToManagedIn
MarshalMode.ManagedToUnmanagedOut
or MarshalMode.UnmanagedToManagedIn
=> MarshallingDirection.UnmanagedToManaged,

Scenario.ManagedToUnmanagedRef
or Scenario.UnmanagedToManagedRef
MarshalMode.ManagedToUnmanagedRef
or MarshalMode.UnmanagedToManagedRef
=> MarshallingDirection.Bidirectional,

Scenario.ElementIn
or Scenario.ElementRef
or Scenario.ElementOut
MarshalMode.ElementIn
or MarshalMode.ElementRef
or MarshalMode.ElementOut
=> MarshallingDirection.Bidirectional,

_ => throw new UnreachableException()
};

// TODO: Report invalid shape for scenario
// Skip checking for bidirectional support for Default scenario - always take / store marshaller data
// TODO: Report invalid shape for mode
// Skip checking for bidirectional support for Default mode - always take / store marshaller data
CustomTypeMarshallerData? data = GetMarshallerDataForType(marshallerType, direction, managedType, isLinearCollectionMarshalling, compilation, getMarshallingInfoForElement);

// TODO: Should we fire a diagnostic for duplicated scenarios or just take the last one?
// TODO: Should we fire a diagnostic for duplicated modes or just take the last one?
if (data is null
|| scenarios.ContainsKey(marshallerScenario))
|| modes.ContainsKey(marshalMode))
{
continue;
}

scenarios.Add(marshallerScenario, data.Value);
modes.Add(marshalMode, data.Value);
}

if (scenarios.Count == 0)
if (modes.Count == 0)
return false;

marshallers = new CustomTypeMarshallers()
{
Scenarios = scenarios.ToImmutableDictionary()
Modes = modes.ToImmutableDictionary()
};

return true;
Expand Down Expand Up @@ -392,10 +392,10 @@ public static (AttributeData? attribute, INamedTypeSymbol? entryType) GetDefault
}
else
{
// Native type is the first parameter of ConvertToManaged or ConvertToManagedGuaranteed
if (methods.ToManagedGuaranteed is not null)
// Native type is the first parameter of ConvertToManaged or ConvertToManagedFinally
if (methods.ToManagedFinally is not null)
{
nativeType = methods.ToManagedGuaranteed.Parameters[0].Type;
nativeType = methods.ToManagedFinally.Parameters[0].Type;
}
else if (methods.ToManaged is not null)
{
Expand Down
Loading