Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
ad0172f
Hook up initial infrastructure for unmanaged-to-managed vtable stubs.
jkoritzinsky Oct 17, 2022
f571cd0
Make the signatures for the UnmanagedCallersOnly wrapper have the cor…
jkoritzinsky Nov 1, 2022
c2f2fdf
Fix signature and add runtime tests for unmanaged->managed
jkoritzinsky Nov 2, 2022
32f856c
Update incremental generation liveness test to be a theory and add an…
jkoritzinsky Nov 10, 2022
9a38bee
Change IMarshallingGenerator.AsNativeType to return a ManagedTypeInfo…
jkoritzinsky Nov 10, 2022
71fa346
Use MarshalDirection to provide a nice abstraction for determining wh…
jkoritzinsky Nov 10, 2022
8646adf
Implement unmanaged->managed exception handling spec and get compilat…
jkoritzinsky Nov 10, 2022
baf8a36
Initial PR feedback.
jkoritzinsky Nov 10, 2022
6bf9f2c
Merge branch 'main' of github.com:dotnet/runtime into vtable-unmanged…
jkoritzinsky Nov 11, 2022
6819628
Fix function pointer signature
jkoritzinsky Nov 14, 2022
972fd18
Emit a PopulateUnmanagedVirtualMethodTable wrapper function to fill t…
jkoritzinsky Nov 15, 2022
ddaa876
Add methods for providing virtual method table length.
jkoritzinsky Nov 16, 2022
ec57196
Merge branch 'main' of github.com:dotnet/runtime into vtable-unmanged…
jkoritzinsky Nov 21, 2022
7f05abb
PR feedback
jkoritzinsky Nov 21, 2022
4132fd9
Fix bad merge that lost the native-to-managed-this marshaller factory.
jkoritzinsky Nov 21, 2022
96ac635
Enhance docs for the error case
jkoritzinsky Nov 21, 2022
76ce3f5
Internalize the "fallback-to-fowarder" logic to BoundGenerators and h…
jkoritzinsky Nov 29, 2022
8d89495
Pass in the fallback generator to BoundGenerators
jkoritzinsky Nov 30, 2022
7c05d70
Change the BoundGenerators constructor to a factory method.
jkoritzinsky Dec 1, 2022
d4e71c4
Add lots of comments and docs
jkoritzinsky Dec 2, 2022
0a45772
Fix a few missing init properties
jkoritzinsky Dec 2, 2022
9879853
PR feedback and finish wiring up the exception marshallers to actuall…
jkoritzinsky Dec 6, 2022
b7d1ab3
Add diagnostics for ExceptionMarshalling and other misc PR feedback.
jkoritzinsky Dec 7, 2022
8e9e5ca
Allow setting ExceptionMarshallingCustomType without setting Exceptio…
jkoritzinsky Dec 8, 2022
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
Prev Previous commit
Next Next commit
Implement unmanaged->managed exception handling spec and get compilat…
…ion unit tests passing.
  • Loading branch information
jkoritzinsky committed Nov 10, 2022
commit 8646adfa5adbb6402df422fbfbf16a7cf6c7d765
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,13 @@ namespace System.Runtime.InteropServices.Marshalling;
public class VirtualMethodTableIndexAttribute
{
+ public ExceptionMarshalling ExceptionMarshalling { get; }
+ public Type ExceptionMarshallingType { get; }
+ public Type? ExceptionMarshallingType { get; } // When set to null or not set, equivalent to option 1
}

+ public enum ExceptionMarshalling
+ {
+ None, // No exception handling (equivalent to Option 1)
+ Custom,
+ Com, // Match the COM-focused model described in Option 2
+ Custom
+ }
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,18 @@ public JSExportCodeGenerator(
Action<TypePositionInfo, MarshallingNotSupportedException> marshallingNotSupportedCallback,
IMarshallingGeneratorFactory generatorFactory)
{
Action<TypePositionInfo, string> extendedInvariantViolationsCallback = (info, details) =>
marshallingNotSupportedCallback(info, new MarshallingNotSupportedException(info, _context) { NotSupportedDetails = details });
_signatureContext = signatureContext;
ManagedToNativeStubCodeContext innerContext = new ManagedToNativeStubCodeContext(targetFramework, targetFrameworkVersion, ReturnIdentifier, ReturnIdentifier);
NativeToManagedStubCodeContext innerContext = new NativeToManagedStubCodeContext(targetFramework, targetFrameworkVersion, ReturnIdentifier, ReturnIdentifier);
_context = new JSExportCodeContext(attributeData, innerContext);
_marshallers = new BoundGenerators(argTypes, CreateGenerator);
_marshallers = new BoundGenerators(argTypes, CreateGenerator, extendedInvariantViolationsCallback);
if (_marshallers.ManagedReturnMarshaller.Generator.UsesNativeIdentifier(_marshallers.ManagedReturnMarshaller.TypeInfo, null))
{
// If we need a different native return identifier, then recreate the context with the correct identifier before we generate any code.
innerContext = new ManagedToNativeStubCodeContext(targetFramework, targetFrameworkVersion, ReturnIdentifier, ReturnNativeIdentifier);
innerContext = new NativeToManagedStubCodeContext(targetFramework, targetFrameworkVersion, ReturnIdentifier, ReturnNativeIdentifier);
_context = new JSExportCodeContext(attributeData, innerContext);
_marshallers = new BoundGenerators(argTypes, CreateGenerator);
_marshallers = new BoundGenerators(argTypes, CreateGenerator, extendedInvariantViolationsCallback);
}

// validate task + span mix
Expand Down Expand Up @@ -72,7 +74,7 @@ public BlockSyntax GenerateJSExportBody()
StatementSyntax invoke = InvokeSyntax();
GeneratedStatements statements = GeneratedStatements.Create(_marshallers, _context);
bool shouldInitializeVariables = !statements.GuaranteedUnmarshal.IsEmpty || !statements.Cleanup.IsEmpty;
VariableDeclarations declarations = VariableDeclarations.GenerateDeclarationsForManagedToNative(_marshallers, _context, shouldInitializeVariables);
VariableDeclarations declarations = VariableDeclarations.GenerateDeclarationsForUnmanagedToManaged(_marshallers, _context, shouldInitializeVariables);

var setupStatements = new List<StatementSyntax>();
SetupSyntax(setupStatements);
Expand Down Expand Up @@ -222,7 +224,7 @@ private StatementSyntax InvokeSyntax()

public (ParameterListSyntax ParameterList, TypeSyntax ReturnType, AttributeListSyntax? ReturnTypeAttributes) GenerateTargetMethodSignatureData()
{
return _marshallers.GenerateTargetMethodSignatureData();
return _marshallers.GenerateTargetMethodSignatureData(_context);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,18 @@ public JSImportCodeGenerator(
Action<TypePositionInfo, MarshallingNotSupportedException> marshallingNotSupportedCallback,
IMarshallingGeneratorFactory generatorFactory)
{
Action<TypePositionInfo, string> extendedInvariantViolationsCallback = (info, details) =>
marshallingNotSupportedCallback(info, new MarshallingNotSupportedException(info, _context) { NotSupportedDetails = details });
_signatureContext = signatureContext;
ManagedToNativeStubCodeContext innerContext = new ManagedToNativeStubCodeContext(targetFramework, targetFrameworkVersion, ReturnIdentifier, ReturnIdentifier);
_context = new JSImportCodeContext(attributeData, innerContext);
_marshallers = new BoundGenerators(argTypes, CreateGenerator);
_marshallers = new BoundGenerators(argTypes, CreateGenerator, extendedInvariantViolationsCallback);
if (_marshallers.ManagedReturnMarshaller.Generator.UsesNativeIdentifier(_marshallers.ManagedReturnMarshaller.TypeInfo, null))
{
// If we need a different native return identifier, then recreate the context with the correct identifier before we generate any code.
innerContext = new ManagedToNativeStubCodeContext(targetFramework, targetFrameworkVersion, ReturnIdentifier, ReturnNativeIdentifier);
_context = new JSImportCodeContext(attributeData, innerContext);
_marshallers = new BoundGenerators(argTypes, CreateGenerator);
_marshallers = new BoundGenerators(argTypes, CreateGenerator, extendedInvariantViolationsCallback);
}

// validate task + span mix
Expand Down Expand Up @@ -80,7 +82,7 @@ public BlockSyntax GenerateJSImportBody()
StatementSyntax invoke = InvokeSyntax();
GeneratedStatements statements = GeneratedStatements.Create(_marshallers, _context);
bool shouldInitializeVariables = !statements.GuaranteedUnmarshal.IsEmpty || !statements.Cleanup.IsEmpty;
VariableDeclarations declarations = VariableDeclarations.GenerateDeclarationsForManagedToNative(_marshallers, _context, shouldInitializeVariables);
VariableDeclarations declarations = VariableDeclarations.GenerateDeclarationsForManagedToUnmanaged(_marshallers, _context, shouldInitializeVariables);

var setupStatements = new List<StatementSyntax>();
BindSyntax(setupStatements);
Expand Down Expand Up @@ -209,7 +211,7 @@ private StatementSyntax InvokeSyntax()

public (ParameterListSyntax ParameterList, TypeSyntax ReturnType, AttributeListSyntax? ReturnTypeAttributes) GenerateTargetMethodSignatureData()
{
return _marshallers.GenerateTargetMethodSignatureData();
return _marshallers.GenerateTargetMethodSignatureData(_context);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ protected BaseJSGenerator(MarshalerType marshalerType, IMarshallingGenerator inn
}

public ManagedTypeInfo AsNativeType(TypePositionInfo info) => _inner.AsNativeType(info);
public ParameterSyntax AsParameter(TypePositionInfo info) => _inner.AsParameter(info);
public bool IsSupported(TargetFramework target, Version version) => _inner.IsSupported(target, version);
public virtual bool UsesNativeIdentifier(TypePositionInfo info, StubCodeContext context) => _inner.UsesNativeIdentifier(info, context);
public SignatureBehavior GetNativeSignatureBehavior(TypePositionInfo info) => _inner.GetNativeSignatureBehavior(info);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
<None Include="$(TargetPath)" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
<None Include="$(AssemblyName).props" Pack="true" PackagePath="build" />
<Compile Include="..\Common\DefaultMarshallingInfoParser.cs" Link="Common\DefaultMarshallingInfoParser.cs" />
<Compile Include="..\..\tests\Common\ExceptionMarshalling.cs" Link="Common\ExceptionMarshalling.cs" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public ManagedToNativeVTableMethodGenerator(
}

_context = new ManagedToNativeStubCodeContext(targetFramework, targetFrameworkVersion, ReturnIdentifier, ReturnIdentifier);
_marshallers = new BoundGenerators(argTypes, CreateGenerator);
_marshallers = new BoundGenerators(argTypes, CreateGenerator, (info, details) => marshallingNotSupportedCallback(info, new MarshallingNotSupportedException(info, _context) { NotSupportedDetails = details }));

if (_marshallers.ManagedReturnMarshaller.Generator.UsesNativeIdentifier(_marshallers.ManagedReturnMarshaller.TypeInfo, _context))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public UnmanagedToManagedStubGenerator(
IMarshallingGeneratorFactory generatorFactory)
{
_context = new NativeToManagedStubCodeContext(targetFramework, targetFrameworkVersion, ReturnIdentifier, ReturnIdentifier);
_marshallers = new BoundGenerators(argTypes, CreateGenerator);
_marshallers = new BoundGenerators(argTypes, CreateGenerator, (info, details) => marshallingNotSupportedCallback(info, new MarshallingNotSupportedException(info, _context) { NotSupportedDetails = details }));

if (_marshallers.ManagedReturnMarshaller.Generator.UsesNativeIdentifier(_marshallers.ManagedReturnMarshaller.TypeInfo, _context))
{
Expand Down Expand Up @@ -60,14 +60,17 @@ IMarshallingGenerator CreateGenerator(TypePositionInfo p)
/// <remarks>
/// The generated code assumes it will be in an unsafe context.
/// </remarks>
public BlockSyntax GenerateStubBody(ExpressionSyntax methodToInvoke, CatchClauseSyntax? catchClause)
public BlockSyntax GenerateStubBody(ExpressionSyntax methodToInvoke)
{
List<StatementSyntax> setupStatements = new();
GeneratedStatements statements = GeneratedStatements.Create(
_marshallers,
_context,
methodToInvoke);
bool shouldInitializeVariables = !statements.GuaranteedUnmarshal.IsEmpty || !statements.Cleanup.IsEmpty;
bool shouldInitializeVariables =
!statements.GuaranteedUnmarshal.IsEmpty
|| !statements.Cleanup.IsEmpty
|| !statements.ManagedExceptionCatchClauses.IsEmpty;
VariableDeclarations declarations = VariableDeclarations.GenerateDeclarationsForUnmanagedToManaged(_marshallers, _context, shouldInitializeVariables);

if (!statements.GuaranteedUnmarshal.IsEmpty)
Expand Down Expand Up @@ -102,23 +105,27 @@ public BlockSyntax GenerateStubBody(ExpressionSyntax methodToInvoke, CatchClause
finallyStatements.Add(IfStatement(IdentifierName(InvokeSucceededIdentifier), Block(statements.GuaranteedUnmarshal)));
}

SyntaxList<CatchClauseSyntax> catchClauses = catchClause is not null ? SingletonList(catchClause) : default;
SyntaxList<CatchClauseSyntax> catchClauses = List(statements.ManagedExceptionCatchClauses);

finallyStatements.AddRange(statements.Cleanup);
if (finallyStatements.Count > 0)
{
allStatements.Add(
TryStatement(Block(tryStatements), catchClauses, FinallyClause(Block(finallyStatements))));
}
else
else if (catchClauses.Count > 0)
{
allStatements.Add(
TryStatement(Block(tryStatements), catchClauses, @finally: null));
}
else
{
allStatements.AddRange(tryStatements);
}

// Return
if (!_marshallers.IsUnmanagedVoidReturn)
allStatements.Add(ReturnStatement(IdentifierName(_context.GetIdentifiers(_marshallers.ManagedReturnMarshaller.TypeInfo).managed)));
allStatements.Add(ReturnStatement(IdentifierName(_context.GetIdentifiers(_marshallers.NativeReturnMarshaller.TypeInfo).native)));

return Block(allStatements);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.CodeAnalysis;

namespace Microsoft.Interop
{
/// <summary>
Expand All @@ -11,5 +13,8 @@ internal sealed record VirtualMethodIndexData(int Index) : InteropAttributeData
public bool ImplicitThisParameter { get; init; }

public MarshalDirection Direction { get; init; }

public ExceptionMarshalling ExceptionMarshalling { get; init; }
public INamedTypeSymbol? ExceptionMarshallingCustomType { get; init; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ internal sealed record IncrementalStubGenerationContext(
MethodSignatureDiagnosticLocations DiagnosticLocation,
SequenceEqualImmutableArray<FunctionPointerUnmanagedCallingConventionSyntax> CallingConvention,
VirtualMethodIndexData VtableIndexData,
MarshallingGeneratorFactoryKey<(TargetFramework TargetFramework, Version TargetFrameworkVersion)> GeneratorFactory,
MarshallingInfo ExceptionMarshallingInfo,
MarshallingGeneratorFactoryKey<(TargetFramework TargetFramework, Version TargetFrameworkVersion)> ManagedToUnmanagedGeneratorFactory,
MarshallingGeneratorFactoryKey<(TargetFramework TargetFramework, Version TargetFrameworkVersion)> UnmanagedToManagedGeneratorFactory,
ManagedTypeInfo TypeKeyType,
Expand Down Expand Up @@ -182,6 +182,8 @@ private static MethodDeclarationSyntax PrintGeneratedSource(

MarshalDirection direction = MarshalDirection.Bidirectional;
bool implicitThis = true;
ExceptionMarshalling exceptionMarshalling = ExceptionMarshalling.Custom;
INamedTypeSymbol? exceptionMarshallingCustomType = null;
if (namedArguments.TryGetValue(nameof(VirtualMethodIndexData.Direction), out TypedConstant directionValue))
{
// TypedConstant's Value property only contains primitive values.
Expand All @@ -200,11 +202,31 @@ private static MethodDeclarationSyntax PrintGeneratedSource(
}
implicitThis = (bool)implicitThisValue.Value!;
}
if (namedArguments.TryGetValue(nameof(VirtualMethodIndexData.ExceptionMarshalling), out TypedConstant exceptionMarshallingValue))
{
// TypedConstant's Value property only contains primitive values.
if (exceptionMarshallingValue.Value is not int)
{
return null;
}
// A boxed primitive can be unboxed to an enum with the same underlying type.
exceptionMarshalling = (ExceptionMarshalling)exceptionMarshallingValue.Value!;
}
if (namedArguments.TryGetValue(nameof(VirtualMethodIndexData.ExceptionMarshallingCustomType), out TypedConstant exceptionMarshallingCustomTypeValue))
{
if (exceptionMarshallingCustomTypeValue.Value is not INamedTypeSymbol)
{
return null;
}
exceptionMarshallingCustomType = (INamedTypeSymbol)exceptionMarshallingCustomTypeValue.Value;
}

return new VirtualMethodIndexData((int)attrData.ConstructorArguments[0].Value).WithValuesFromNamedArguments(namedArguments) with
{
Direction = direction,
ImplicitThisParameter = implicitThis
ImplicitThisParameter = implicitThis,
ExceptionMarshalling = exceptionMarshalling,
ExceptionMarshallingCustomType = exceptionMarshallingCustomType,
};
}

Expand Down Expand Up @@ -307,21 +329,41 @@ private static IncrementalStubGenerationContext CalculateStubInformation(MethodD
typeKeyType = ManagedTypeInfo.CreateTypeInfoForTypeSymbol(iUnmanagedInterfaceTypeInstantiation.TypeArguments[1]);
}

MarshallingInfo exceptionMarshallingInfo = CreateExceptionMarshallingInfo(virtualMethodIndexAttr, environment.Compilation, generatorDiagnostics, virtualMethodIndexData.ExceptionMarshalling, virtualMethodIndexData.ExceptionMarshallingCustomType);

return new IncrementalStubGenerationContext(
signatureContext,
containingSyntaxContext,
methodSyntaxTemplate,
new MethodSignatureDiagnosticLocations(syntax),
new SequenceEqualImmutableArray<FunctionPointerUnmanagedCallingConventionSyntax>(callConv, SyntaxEquivalentComparer.Instance),
virtualMethodIndexData,
ComInterfaceGeneratorHelpers.CreateGeneratorFactory(environment),
exceptionMarshallingInfo,
ComInterfaceGeneratorHelpers.CreateGeneratorFactory(environment, MarshalDirection.ManagedToUnmanaged),
ComInterfaceGeneratorHelpers.CreateGeneratorFactory(environment, MarshalDirection.UnmanagedToManaged),
typeKeyType,
typeKeyOwner,
new SequenceEqualImmutableArray<Diagnostic>(generatorDiagnostics.Diagnostics.ToImmutableArray()));
}

private static MarshallingInfo CreateExceptionMarshallingInfo(AttributeData triggerAttribute, Compilation compilation, IGeneratorDiagnostics diagnostics, ExceptionMarshalling marshalling, INamedTypeSymbol? exceptionMarshallingCustomType)
{
if (marshalling == ExceptionMarshalling.Com)
{
return new ComExceptionMarshalling();
}
if (exceptionMarshallingCustomType is null)
{
return NoMarshallingInfo.Instance;
}
return CustomMarshallingInfoHelper.CreateNativeMarshallingInfoForNonSignatureElement(
compilation.GetTypeByMetadataName(TypeNames.System_Exception),
exceptionMarshallingCustomType,
triggerAttribute,
compilation,
diagnostics);
}

private static (MemberDeclarationSyntax, ImmutableArray<Diagnostic>) GenerateManagedToNativeStub(
IncrementalStubGenerationContext methodStub)
{
Expand Down Expand Up @@ -395,12 +437,10 @@ private static (MemberDeclarationSyntax, ImmutableArray<Diagnostic>) GenerateNat
},
methodStub.UnmanagedToManagedGeneratorFactory.GeneratorFactory);

// TODO: Implement our EH design based on the additional property on the attribute.
BlockSyntax code = stubGenerator.GenerateStubBody(
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
IdentifierName(thisParameterIdentifier),
IdentifierName(methodStub.StubMethodSyntaxTemplate.Identifier)),
catchClause: null);
IdentifierName(methodStub.StubMethodSyntaxTemplate.Identifier)));

(ParameterListSyntax unmanagedParameterList, TypeSyntax returnType, _) = stubGenerator.GenerateAbiMethodSignatureData();

Expand All @@ -414,7 +454,7 @@ private static (MemberDeclarationSyntax, ImmutableArray<Diagnostic>) GenerateNat
ImplicitArrayCreationExpression(
InitializerExpression(SyntaxKind.CollectionInitializerExpression,
SeparatedList<ExpressionSyntax>(
methodStub.CallingConvention.Array.Select(callConv => TypeOfExpression(ParseName($"CallConv{callConv.Name.ValueText}")))))))
methodStub.CallingConvention.Array.Select(callConv => TypeOfExpression(ParseName($"System.Runtime.CompilerServices.CallConv{callConv.Name.ValueText}")))))))
.WithNameEquals(NameEquals(IdentifierName("CallConvs"))));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbo
mayRequireAdditionalWork = true;
return forwarder;
}
});
}, (info, details) => mayRequireAdditionalWork = true);

if (anyExplicitlyUnsupportedInfo)
{
Expand Down
Loading