Skip to content

Commit

Permalink
Implement the non-blittable type marshalling proposal (dotnet/runtime…
Browse files Browse the repository at this point in the history
  • Loading branch information
jkoritzinsky authored Nov 19, 2020
1 parent a17d5d5 commit 80349fb
Show file tree
Hide file tree
Showing 20 changed files with 1,387 additions and 53 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ DLLIMPORTGENANALYZER010 | Usage | Warning | GetPinnableReferenceShou
DLLIMPORTGENANALYZER011 | Usage | Warning | StackallocMarshallingShouldSupportAllocatingMarshallingFallback
DLLIMPORTGENANALYZER012 | Usage | Error | StackallocConstructorMustHaveStackBufferSizeConstant
DLLIMPORTGENANALYZER013 | Usage | Warning | GeneratedDllImportMissingRequiredModifiers
DLLIMPORTGENANALYZER014 | Usage | Error | RefValuePropertyUnsupported
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public static class Ids
public const string GetPinnableReferenceShouldSupportAllocatingMarshallingFallback = Prefix + "010";
public const string StackallocMarshallingShouldSupportAllocatingMarshallingFallback = Prefix + "011";
public const string StackallocConstructorMustHaveStackBufferSizeConstant = Prefix + "012";
public const string RefValuePropertyUnsupported = Prefix + "014";

// GeneratedDllImport
public const string GeneratedDllImportMissingRequiredModifiers = Prefix + "013";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,16 @@ public class ManualTypeMarshallingAnalyzer : DiagnosticAnalyzer
isEnabledByDefault: true,
description: GetResourceString(nameof(Resources.StackallocConstructorMustHaveStackBufferSizeConstantDescription)));

public readonly static DiagnosticDescriptor RefValuePropertyUnsupportedRule =
new DiagnosticDescriptor(
Ids.RefValuePropertyUnsupported,
"RefValuePropertyUnsupported",
GetResourceString(nameof(Resources.RefValuePropertyUnsupportedMessage)),
Category,
DiagnosticSeverity.Error,
isEnabledByDefault: true,
description: GetResourceString(nameof(Resources.RefValuePropertyUnsupportedDescription)));

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
ImmutableArray.Create(
BlittableTypeMustBeBlittableRule,
Expand All @@ -147,7 +157,8 @@ public class ManualTypeMarshallingAnalyzer : DiagnosticAnalyzer
ValuePropertyMustHaveGetterRule,
GetPinnableReferenceShouldSupportAllocatingMarshallingFallbackRule,
StackallocMarshallingShouldSupportAllocatingMarshallingFallbackRule,
StackallocConstructorMustHaveStackBufferSizeConstantRule);
StackallocConstructorMustHaveStackBufferSizeConstantRule,
RefValuePropertyUnsupportedRule);

public override void Initialize(AnalysisContext context)
{
Expand Down Expand Up @@ -242,7 +253,7 @@ public void AnalyzeTypeDefinition(SymbolAnalysisContext context)
}
else if (nativeMarshallingAttributeData is not null)
{
AnalyzeNativeMarshalerType(context, type, nativeMarshallingAttributeData, validateGetPinnableReference: true, validateAllScenarioSupport: true);
AnalyzeNativeMarshalerType(context, type, nativeMarshallingAttributeData, validateManagedGetPinnableReference: true, validateAllScenarioSupport: true);
}
}

Expand Down Expand Up @@ -283,7 +294,7 @@ public void AnalyzeReturnType(SymbolAnalysisContext context)
}
}

private void AnalyzeNativeMarshalerType(SymbolAnalysisContext context, ITypeSymbol type, AttributeData nativeMarshalerAttributeData, bool validateGetPinnableReference, bool validateAllScenarioSupport)
private void AnalyzeNativeMarshalerType(SymbolAnalysisContext context, ITypeSymbol type, AttributeData nativeMarshalerAttributeData, bool validateManagedGetPinnableReference, bool validateAllScenarioSupport)
{
if (nativeMarshalerAttributeData.ConstructorArguments[0].IsNull)
{
Expand Down Expand Up @@ -342,6 +353,14 @@ private void AnalyzeNativeMarshalerType(SymbolAnalysisContext context, ITypeSymb
}

IPropertySymbol? valueProperty = ManualTypeMarshallingHelper.FindValueProperty(nativeType);
bool valuePropertyIsRefReturn = valueProperty is { ReturnsByRef : true } or { ReturnsByRefReadonly: true };

if (valuePropertyIsRefReturn)
{
context.ReportDiagnostic(Diagnostic.Create(RefValuePropertyUnsupportedRule, GetSyntaxReferenceForDiagnostic(valueProperty!).GetSyntax().GetLocation(),
marshalerType.ToDisplayString()));
}

if (valueProperty is not null)
{
nativeType = valueProperty.Type;
Expand Down Expand Up @@ -370,34 +389,42 @@ valueProperty is not null
type.ToDisplayString()));
}

IMethodSymbol? getPinnableReferenceMethod = type.GetMembers("GetPinnableReference")
.OfType<IMethodSymbol>()
.FirstOrDefault(m => m is { Parameters: { Length: 0 } } and ({ ReturnsByRef: true } or { ReturnsByRefReadonly: true }));
if (validateGetPinnableReference && getPinnableReferenceMethod is not null)
IMethodSymbol? managedGetPinnableReferenceMethod = ManualTypeMarshallingHelper.FindGetPinnableReference(type);
if (validateManagedGetPinnableReference && managedGetPinnableReferenceMethod is not null)
{
if (!getPinnableReferenceMethod.ReturnType.IsConsideredBlittable())
if (!managedGetPinnableReferenceMethod.ReturnType.IsConsideredBlittable())
{
context.ReportDiagnostic(Diagnostic.Create(GetPinnableReferenceReturnTypeBlittableRule, managedGetPinnableReferenceMethod.DeclaringSyntaxReferences[0].GetSyntax().GetLocation()));
}
// Validate that our marshaler supports scenarios where GetPinnableReference cannot be used.
if (validateAllScenarioSupport && (!hasConstructor || valueProperty is { GetMethod: null }))
{
context.ReportDiagnostic(Diagnostic.Create(GetPinnableReferenceReturnTypeBlittableRule, getPinnableReferenceMethod.DeclaringSyntaxReferences[0].GetSyntax().GetLocation()));
context.ReportDiagnostic(Diagnostic.Create(GetPinnableReferenceShouldSupportAllocatingMarshallingFallbackRule, nativeMarshalerAttributeData.ApplicationSyntaxReference!.GetSyntax().GetLocation(), type.ToDisplayString()));
}
}

if ((validateManagedGetPinnableReference && managedGetPinnableReferenceMethod is not null)
|| ManualTypeMarshallingHelper.FindGetPinnableReference(marshalerType) is not null)
{
// Validate that the Value property is a pointer-sized primitive type.
if (valueProperty is null ||
valueProperty.Type is not (
if (valueProperty is null
|| (valueProperty.Type is not (
IPointerTypeSymbol _ or
{ SpecialType: SpecialType.System_IntPtr } or
{ SpecialType: SpecialType.System_UIntPtr }))
{ SpecialType: SpecialType.System_UIntPtr })))
{
ITypeSymbol typeWithGetPinnableReference = managedGetPinnableReferenceMethod is not null
? type
: marshalerType;

context.ReportDiagnostic(Diagnostic.Create(NativeTypeMustBePointerSizedRule,
valueProperty is not null
? GetSyntaxReferenceForDiagnostic(valueProperty).GetSyntax().GetLocation()
: GetSyntaxReferenceForDiagnostic(nativeType).GetSyntax().GetLocation(),
nativeType.ToDisplayString(),
type.ToDisplayString()));
}

// Validate that our marshaler supports scenarios where GetPinnableReference cannot be used.
if (validateAllScenarioSupport && (!hasConstructor || valueProperty is { GetMethod: null }))
{
context.ReportDiagnostic(Diagnostic.Create(GetPinnableReferenceShouldSupportAllocatingMarshallingFallbackRule, nativeMarshalerAttributeData.ApplicationSyntaxReference!.GetSyntax().GetLocation(), type.ToDisplayString()));
? GetSyntaxReferenceForDiagnostic(valueProperty).GetSyntax().GetLocation()
: GetSyntaxReferenceForDiagnostic(nativeType).GetSyntax().GetLocation(),
valuePropertyIsRefReturn
? $"ref {nativeType.ToDisplayString()}"
: nativeType.ToDisplayString(),
typeWithGetPinnableReference.ToDisplayString()));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ namespace Microsoft.Interop
static class ManualTypeMarshallingHelper
{
public const string ValuePropertyName = "Value";
public const string GetPinnableReferenceName = "GetPinnableReference";
public const string StackBufferSizeFieldName = "StackBufferSize";
public const string ToManagedMethodName = "ToManaged";
public const string FreeNativeMethodName = "FreeNative";
Expand Down Expand Up @@ -44,7 +45,7 @@ public static bool IsStackallocConstructor(
// fixed statement. We aren't supporting a GetPinnableReference extension method
// (which is apparently supported in the compiler).
// https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-7.3/pattern-based-fixed
return type.GetMembers("GetPinnableReference")
return type.GetMembers(GetPinnableReferenceName)
.OfType<IMethodSymbol>()
.FirstOrDefault(m => m is { Parameters: { Length: 0 } } and
({ ReturnsByRef: true } or { ReturnsByRefReadonly: true }));
Expand Down
Loading

0 comments on commit 80349fb

Please sign in to comment.