Skip to content

Commit

Permalink
Add StringMarshallingCustomType to GeneratedDllImportAttribute (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
elinor-fung authored Feb 25, 2022
1 parent 1857d04 commit c32b1f5
Show file tree
Hide file tree
Showing 18 changed files with 472 additions and 65 deletions.
4 changes: 2 additions & 2 deletions src/coreclr/nativeaot/Test.CoreLib/src/Test.CoreLib.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
<TargetFramework>netstandard2.0</TargetFramework>
<EnableDllImportGenerator>true</EnableDllImportGenerator>
<!--
DLLIMPORTGEN003: DllImportGenerator Target Framework Not Supported.
DLLIMPORTGEN004: DllImportGenerator Target Framework Not Supported.
-->
<NoWarn>$(NoWarn);DLLIMPORTGEN003</NoWarn>
<NoWarn>$(NoWarn);DLLIMPORTGEN004</NoWarn>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
<DefineConstants>FEATURE_GC_STRESS;$(DefineConstants)</DefineConstants>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ sealed class GeneratedDllImportAttribute : Attribute
public string? EntryPoint { get; set; }
public bool SetLastError { get; set; }
public StringMarshalling StringMarshalling { get; set; }
public Type? StringMarshallingCustomType { get; set; }

public GeneratedDllImportAttribute(string dllName)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,7 @@ private static TargetFramework DetermineTargetFramework(Compilation compilation,
bool setLastError = false;

StringMarshalling stringMarshalling = StringMarshalling.Custom;
INamedTypeSymbol? stringMarshallingCustomType = null;

// All other data on attribute is defined as NamedArguments.
foreach (KeyValuePair<string, TypedConstant> namedArg in attrData.NamedArguments)
Expand All @@ -369,16 +370,6 @@ private static TargetFramework DetermineTargetFramework(Compilation compilation,
// Return null here to indicate invalid attribute data.
Debug.WriteLine($"An unknown member '{namedArg.Key}' was found on {attrData.AttributeClass}");
return null;
case nameof(GeneratedDllImportData.StringMarshalling):
userDefinedValues |= DllImportMember.StringMarshalling;
// TypedConstant's Value property only contains primitive values.
if (namedArg.Value.Value is not int)
{
return null;
}
// A boxed primitive can be unboxed to an enum with the same underlying type.
stringMarshalling = (StringMarshalling)namedArg.Value.Value!;
break;
case nameof(GeneratedDllImportData.EntryPoint):
userDefinedValues |= DllImportMember.EntryPoint;
if (namedArg.Value.Value is not string)
Expand All @@ -395,6 +386,24 @@ private static TargetFramework DetermineTargetFramework(Compilation compilation,
}
setLastError = (bool)namedArg.Value.Value!;
break;
case nameof(GeneratedDllImportData.StringMarshalling):
userDefinedValues |= DllImportMember.StringMarshalling;
// TypedConstant's Value property only contains primitive values.
if (namedArg.Value.Value is not int)
{
return null;
}
// A boxed primitive can be unboxed to an enum with the same underlying type.
stringMarshalling = (StringMarshalling)namedArg.Value.Value!;
break;
case nameof(GeneratedDllImportData.StringMarshallingCustomType):
userDefinedValues |= DllImportMember.StringMarshallingCustomType;
if (namedArg.Value.Value is not INamedTypeSymbol)
{
return null;
}
stringMarshallingCustomType = (INamedTypeSymbol)namedArg.Value.Value;
break;
}
}

Expand All @@ -406,9 +415,10 @@ private static TargetFramework DetermineTargetFramework(Compilation compilation,
return new GeneratedDllImportData(attrData.ConstructorArguments[0].Value!.ToString())
{
IsUserDefined = userDefinedValues,
StringMarshalling = stringMarshalling,
EntryPoint = entryPoint,
SetLastError = setLastError,
StringMarshalling = stringMarshalling,
StringMarshallingCustomType = stringMarshallingCustomType,
};
}

Expand Down Expand Up @@ -462,6 +472,23 @@ private static IncrementalStubGenerationContext CalculateStubInformation(IMethod
stubDllImportData = new GeneratedDllImportData("INVALID_CSHARP_SYNTAX");
}

if (stubDllImportData.IsUserDefined.HasFlag(DllImportMember.StringMarshalling))
{
// User specified StringMarshalling.Custom without specifying StringMarshallingCustomType
if (stubDllImportData.StringMarshalling == StringMarshalling.Custom && stubDllImportData.StringMarshallingCustomType is null)
{
generatorDiagnostics.ReportInvalidStringMarshallingConfiguration(
generatedDllImportAttr, symbol.Name, Resources.InvalidStringMarshallingConfigurationMissingCustomType);
}

// User specified something other than StringMarshalling.Custom while specifying StringMarshallingCustomType
if (stubDllImportData.StringMarshalling != StringMarshalling.Custom && stubDllImportData.StringMarshallingCustomType is not null)
{
generatorDiagnostics.ReportInvalidStringMarshallingConfiguration(
generatedDllImportAttr, symbol.Name, Resources.InvalidStringMarshallingConfigurationNotCustom);
}
}

if (lcidConversionAttr is not null)
{
// Using LCIDConversion with GeneratedDllImport is not supported
Expand Down Expand Up @@ -543,13 +570,22 @@ private MemberDeclarationSyntax PrintForwarderStub(MethodDeclarationSyntax userD
&& targetDllImportData.StringMarshalling != StringMarshalling.Utf16)
{
diagnostics.ReportCannotForwardToDllImport(
userDeclaredMethod,
$"{nameof(TypeNames.GeneratedDllImportAttribute)}{Type.Delimiter}{nameof(StringMarshalling)}",
$"{nameof(StringMarshalling)}{Type.Delimiter}{targetDllImportData.StringMarshalling}",
userDeclaredMethod);
$"{nameof(StringMarshalling)}{Type.Delimiter}{targetDllImportData.StringMarshalling}");

targetDllImportData = targetDllImportData with { IsUserDefined = targetDllImportData.IsUserDefined & ~DllImportMember.StringMarshalling };
}

if (targetDllImportData.IsUserDefined.HasFlag(DllImportMember.StringMarshallingCustomType))
{
diagnostics.ReportCannotForwardToDllImport(
userDeclaredMethod,
$"{nameof(TypeNames.GeneratedDllImportAttribute)}{Type.Delimiter}{nameof(DllImportMember.StringMarshallingCustomType)}");

targetDllImportData = targetDllImportData with { IsUserDefined = targetDllImportData.IsUserDefined & ~DllImportMember.StringMarshallingCustomType };
}

SyntaxTokenList modifiers = StripTriviaFromModifiers(userDeclaredMethod.Modifiers);
modifiers = AddToModifiers(modifiers, SyntaxKind.ExternKeyword);
// Create stub function
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,12 @@ private static (ImmutableArray<TypePositionInfo>, IMarshallingGeneratorFactory)
_ => CharEncoding.Undefined, // [Compat] Do not assume a specific value
};
}
else if (dllImportData.IsUserDefined.HasFlag(DllImportMember.StringMarshallingCustomType))
{
defaultEncoding = CharEncoding.Custom;
}

var defaultInfo = new DefaultMarshallingInfo(defaultEncoding);
var defaultInfo = new DefaultMarshallingInfo(defaultEncoding, dllImportData.StringMarshallingCustomType);

var marshallingAttributeParser = new MarshallingAttributeInfoParser(env.Compilation, diagnostics, defaultInfo, method);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.Runtime.InteropServices;
using Microsoft.CodeAnalysis;

namespace Microsoft.Interop
{
Expand All @@ -16,6 +17,7 @@ public enum DllImportMember
EntryPoint = 1 << 0,
SetLastError = 1 << 1,
StringMarshalling = 1 << 2,
StringMarshallingCustomType = 1 << 3,
All = ~None
}

Expand All @@ -32,8 +34,9 @@ internal sealed record GeneratedDllImportData(string ModuleName)
/// Value set by the user on the original declaration.
/// </summary>
public DllImportMember IsUserDefined { get; init; }
public StringMarshalling StringMarshalling { get; init; }
public string? EntryPoint { get; init; }
public bool SetLastError { get; init; }
public StringMarshalling StringMarshalling { get; init; }
public INamedTypeSymbol? StringMarshallingCustomType { get; init; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,25 @@ public class GeneratorDiagnostics : IGeneratorDiagnostics
public class Ids
{
public const string Prefix = "DLLIMPORTGEN";
public const string TypeNotSupported = Prefix + "001";
public const string ConfigurationNotSupported = Prefix + "002";
public const string TargetFrameworkNotSupported = Prefix + "003";
public const string CannotForwardToDllImport = Prefix + "004";
public const string InvalidGeneratedDllImportAttributeUsage = Prefix + "001";
public const string TypeNotSupported = Prefix + "002";
public const string ConfigurationNotSupported = Prefix + "003";
public const string TargetFrameworkNotSupported = Prefix + "004";
public const string CannotForwardToDllImport = Prefix + "005";
}

private const string Category = "SourceGeneration";

public static readonly DiagnosticDescriptor InvalidStringMarshallingConfiguration =
new DiagnosticDescriptor(
Ids.InvalidGeneratedDllImportAttributeUsage,
GetResourceString(nameof(Resources.InvalidLibraryImportAttributeUsageTitle)),
GetResourceString(nameof(Resources.InvalidStringMarshallingConfigurationMessage)),
Category,
DiagnosticSeverity.Error,
isEnabledByDefault: true,
description: GetResourceString(nameof(Resources.InvalidStringMarshallingConfigurationDescription)));

public static readonly DiagnosticDescriptor ParameterTypeNotSupported =
new DiagnosticDescriptor(
Ids.TypeNotSupported,
Expand Down Expand Up @@ -141,6 +152,24 @@ public class Ids

public IEnumerable<Diagnostic> Diagnostics => _diagnostics;

/// <summary>
/// Report diagnostic for invalid configuration for string marshalling.
/// </summary>
/// <param name="attributeData">Attribute specifying the invalid configuration</param>
/// <param name="methodName">Name of the method</param>
/// <param name="detailsMessage">Specific reason the configuration is invalid</param>
public void ReportInvalidStringMarshallingConfiguration(
AttributeData attributeData,
string methodName,
string detailsMessage)
{
_diagnostics.Add(
attributeData.CreateDiagnostic(
GeneratorDiagnostics.InvalidStringMarshallingConfiguration,
methodName,
detailsMessage));
}

/// <summary>
/// Report diagnostic for configuration that is not supported by the DLL import source generator
/// </summary>
Expand Down Expand Up @@ -287,17 +316,16 @@ public void ReportTargetFrameworkNotSupported(Version minimumSupportedVersion)
/// <summary>
/// Report diagnostic for configuration that cannot be forwarded to <see cref="DllImportAttribute" />
/// </summary>
/// <param name="method">Method with the configuration that cannot be forwarded</param>
/// <param name="name">Configuration name</param>
/// <param name="value">Configuration value</param>
/// <param name="method">Method with the arguments that cannot be forwarded</param>
public void ReportCannotForwardToDllImport(string name, string value, MethodDeclarationSyntax method)
public void ReportCannotForwardToDllImport(MethodDeclarationSyntax method, string name, string? value = null)
{
_diagnostics.Add(
Diagnostic.Create(
CannotForwardToDllImport,
Location.Create(method.SyntaxTree, method.Identifier.Span),
name,
value));
value is null ? name : $"{name}={value}"));
}

private static LocalizableResourceString GetResourceString(string resourceName)
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -136,12 +136,10 @@
<value>Native type '{0}' has a constructor taking a caller-allocated buffer, but does not support marshalling in scenarios where using a caller-allocated buffer is impossible</value>
</data>
<data name="CannotForwardToDllImportDescription" xml:space="preserve">
<value>The generated 'DllImportAttribute' will not have a value corresponding to '{0}={1}'.</value>
<comment>{0} and {1} are the name and value of an attribute argument</comment>
<value>The generated 'DllImportAttribute' will not have a value corresponding to '{0}'.</value>
</data>
<data name="CannotForwardToDllImportMessage" xml:space="preserve">
<value>'{0}={1}' has no equivalent in 'DllImportAtttribute' and will not be forwarded</value>
<comment>{0} and {1} are the name and value of an attribute argument</comment>
<value>'{0}' has no equivalent in 'DllImportAtttribute' and will not be forwarded</value>
</data>
<data name="CannotForwardToDllImportTitle" xml:space="preserve">
<value>Specified 'GeneratedDllImportAttribute' arguments cannot be forwarded to 'DllImportAttribute'</value>
Expand Down Expand Up @@ -240,6 +238,22 @@
<data name="GetPinnableReferenceShouldSupportAllocatingMarshallingFallbackMessage" xml:space="preserve">
<value>Type '{0}' has a 'GetPinnableReference' method but its native type does not support marshalling in scenarios where pinning is impossible</value>
</data>
<data name="InvalidLibraryImportAttributeUsageTitle" xml:space="preserve">
<value>Invalid 'LibraryImportAttribute' usage</value>
</data>
<data name="InvalidStringMarshallingConfigurationDescription" xml:space="preserve">
<value>The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' is invalid.</value>
</data>
<data name="InvalidStringMarshallingConfigurationMessage" xml:space="preserve">
<value>The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' on method '{0}' is invalid. {1}</value>
<comment>{1} is a message containing additional details about what is not valid</comment>
</data>
<data name="InvalidStringMarshallingConfigurationMissingCustomType" xml:space="preserve">
<value>'StringMarshallingCustomType' must be specified when 'StringMarshalling' is set to 'StringMarshalling.Custom'.</value>
</data>
<data name="InvalidStringMarshallingConfigurationNotCustom" xml:space="preserve">
<value>'StringMarshalling' should be set to 'StringMarshalling.Custom' when 'StringMarshallingCustomType' is specified.</value>
</data>
<data name="MarshallerGetPinnableReferenceRequiresValuePropertyDescription" xml:space="preserve">
<value>The use cases for 'GetPinnableReference' are not applicable in any scenarios where a 'Value' property is not also required.</value>
</data>
Expand Down
Loading

0 comments on commit c32b1f5

Please sign in to comment.