// Create the stub.
var signatureContext = SignatureContext.Create(symbol, DefaultMarshallingInfoParser.Create(environment, generatorDiagnostics, symbol, new InteropAttributeCompilationData(), generatedComAttribute), environment, typeof(VtableIndexStubGenerator).Assembly);
+ // Search for the element information for the managed return value.
+ // We need to transform it such that any return type is converted to an out parameter at the end of the parameter list.
+ ImmutableArray<TypePositionInfo> returnSwappedSignatureElements = signatureContext.ElementTypeInformation;
+ for (int i = 0; i < returnSwappedSignatureElements.Length; ++i)
+ {
+ if (returnSwappedSignatureElements[i].IsManagedReturnPosition)
+ {
+ if (returnSwappedSignatureElements[i].ManagedType == SpecialTypeInfo.Void)
+ {
+ // Return type is void, just remove the element from the signature list.
+ // We don't introduce an out parameter.
+ returnSwappedSignatureElements = returnSwappedSignatureElements.RemoveAt(i);
+ }
+ else
+ {
+ // Convert the current element into an out parameter on the native signature
+ // while keeping it at the return position in the managed signature.
+ var managedSignatureAsNativeOut = returnSwappedSignatureElements[i] with
+ {
+ RefKind = RefKind.Out,
+ RefKindSyntax = SyntaxKind.OutKeyword,
+ ManagedIndex = TypePositionInfo.ReturnIndex,
+ NativeIndex = symbol.Parameters.Length
+ };
+ returnSwappedSignatureElements = returnSwappedSignatureElements.SetItem(i, managedSignatureAsNativeOut);
+ }
+ break;
+ }
+ }
+
+ signatureContext = signatureContext with
+ {
+ // Add the HRESULT return value in the native signature.
+ // This element does not have any influence on the managed signature, so don't assign a managed index.
+ ElementTypeInformation = returnSwappedSignatureElements.Add(
+ new TypePositionInfo(SpecialTypeInfo.Int32, new ManagedHResultExceptionMarshallingInfo())
+ {
+ NativeIndex = TypePositionInfo.ReturnIndex
+ })
+ };
+
var containingSyntaxContext = new ContainingSyntaxContext(syntax);
var methodSyntaxTemplate = new ContainingSyntax(syntax.Modifiers.StripTriviaFromTokens(), SyntaxKind.MethodDeclaration, syntax.Identifier, syntax.TypeParameterList);
? MarshalMode.ManagedToUnmanagedOut
: MarshalMode.UnmanagedToManagedIn));
+ generatorFactory = new ManagedHResultExceptionMarshallerFactory(generatorFactory, direction);
+
generatorFactory = new NativeToManagedThisMarshallerFactory(generatorFactory);
generatorFactory = new ByValueContentsMarshalKindValidator(generatorFactory);
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
+
+namespace Microsoft.Interop
+{
+ internal sealed record ManagedHResultExceptionMarshallingInfo : MarshallingInfo;
+
+ internal sealed class ManagedHResultExceptionMarshallerFactory : IMarshallingGeneratorFactory
+ {
+ private readonly IMarshallingGeneratorFactory _inner;
+ private readonly MarshalDirection _direction;
+
+ public ManagedHResultExceptionMarshallerFactory(IMarshallingGeneratorFactory inner, MarshalDirection direction)
+ {
+ if (direction is not (MarshalDirection.ManagedToUnmanaged or MarshalDirection.UnmanagedToManaged))
+ {
+ throw new ArgumentOutOfRangeException(nameof(direction));
+ }
+ _inner = inner;
+ _direction = direction;
+ }
+
+ public IMarshallingGenerator Create(TypePositionInfo info, StubCodeContext context)
+ {
+ if (info.MarshallingAttributeInfo is ManagedHResultExceptionMarshallingInfo)
+ {
+ return _direction switch
+ {
+ MarshalDirection.UnmanagedToManaged => new UnmanagedToManagedMarshaller(),
+ MarshalDirection.ManagedToUnmanaged => new ManagedToUnmanagedMarshaller(),
+ _ => throw new UnreachableException()
+ };
+ }
+ else
+ {
+ return _inner.Create(info, context);
+ }
+ }
+
+ private sealed class ManagedToUnmanagedMarshaller : IMarshallingGenerator
+ {
+ public ManagedTypeInfo AsNativeType(TypePositionInfo info) => info.ManagedType;
+ public IEnumerable<StatementSyntax> Generate(TypePositionInfo info, StubCodeContext context)
+ {
+ Debug.Assert(info.MarshallingAttributeInfo is ManagedHResultExceptionMarshallingInfo);
+
+ if (context.CurrentStage != StubCodeContext.Stage.Unmarshal)
+ {
+ yield break;
+ }
+
+ (string managedIdentifier, _) = context.GetIdentifiers(info);
+
+ // Marshal.ThrowExceptionForHR(<managed>);
+ yield return ExpressionStatement(
+ InvocationExpression(
+ MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
+ ParseName(TypeNames.System_Runtime_InteropServices_Marshal),
+ IdentifierName("ThrowExceptionForHR")),
+ ArgumentList(
+ SingletonSeparatedList(Argument(IdentifierName(managedIdentifier))))));
+ }
+
+ public SignatureBehavior GetNativeSignatureBehavior(TypePositionInfo info) => SignatureBehavior.NativeType;
+ public ValueBoundaryBehavior GetValueBoundaryBehavior(TypePositionInfo info, StubCodeContext context) => ValueBoundaryBehavior.ManagedIdentifier;
+ public bool IsSupported(TargetFramework target, Version version) => true;
+ public bool SupportsByValueMarshalKind(ByValueContentsMarshalKind marshalKind, StubCodeContext context) => false;
+ public bool UsesNativeIdentifier(TypePositionInfo info, StubCodeContext context) => false;
+ }
+
+ private sealed class UnmanagedToManagedMarshaller : IMarshallingGenerator
+ {
+ public ManagedTypeInfo AsNativeType(TypePositionInfo info) => info.ManagedType;
+ public IEnumerable<StatementSyntax> Generate(TypePositionInfo info, StubCodeContext context)
+ {
+ Debug.Assert(info.MarshallingAttributeInfo is ManagedHResultExceptionMarshallingInfo);
+
+ if (context.CurrentStage != StubCodeContext.Stage.Unmarshal)
+ {
+ yield break;
+ }
+
+ (string managedIdentifier, _) = context.GetIdentifiers(info);
+
+ //<managed> = 0; // S_OK
+ yield return ExpressionStatement(
+ AssignmentExpression(
+ SyntaxKind.SimpleAssignmentExpression,
+ IdentifierName(managedIdentifier),
+ LiteralExpression(
+ SyntaxKind.NumericLiteralExpression,
+ Literal(0))))
+ .WithSemicolonToken(
+ Token(
+ TriviaList(),
+ SyntaxKind.SemicolonToken,
+ TriviaList(
+ Comment("// S_OK"))));
+ }
+
+ public SignatureBehavior GetNativeSignatureBehavior(TypePositionInfo info) => SignatureBehavior.NativeType;
+ public ValueBoundaryBehavior GetValueBoundaryBehavior(TypePositionInfo info, StubCodeContext context) => ValueBoundaryBehavior.ManagedIdentifier;
+ public bool IsSupported(TargetFramework target, Version version) => true;
+ public bool SupportsByValueMarshalKind(ByValueContentsMarshalKind marshalKind, StubCodeContext context) => false;
+ public bool UsesNativeIdentifier(TypePositionInfo info, StubCodeContext context) => false;
+ }
+ }
+}
marshallingNotSupportedCallback(failure.Info, failure.Exception);
}
- if (_marshallers.ManagedReturnMarshaller.Generator.UsesNativeIdentifier(_marshallers.ManagedReturnMarshaller.TypeInfo, _context))
+ if (_marshallers.NativeReturnMarshaller.Generator.UsesNativeIdentifier(_marshallers.NativeReturnMarshaller.TypeInfo, _context))
{
// If we need a different native return identifier, then recreate the context with the correct identifier before we generate any code.
_context = new NativeToManagedStubCodeContext(targetFramework, targetFrameworkVersion, ReturnIdentifier, $"{ReturnIdentifier}{StubCodeContext.GeneratedNativeIdentifierSuffix}");
return ExpressionStatement(invoke);
}
+ var (managed, native) = context.GetIdentifiers(marshallers.NativeReturnMarshaller.TypeInfo);
+
+ string targetIdentifier = marshallers.NativeReturnMarshaller.Generator.UsesNativeIdentifier(marshallers.NativeReturnMarshaller.TypeInfo, context)
+ ? native
+ : managed;
+
return ExpressionStatement(
AssignmentExpression(
SyntaxKind.SimpleAssignmentExpression,
- IdentifierName(context.GetIdentifiers(marshallers.NativeReturnMarshaller.TypeInfo).native),
+ IdentifierName(targetIdentifier),
invoke));
}
private readonly Version _frameworkVersion;
private const string InvokeReturnIdentifier = "__invokeRetVal";
+ private const string InvokeReturnIdentifierNative = "__invokeRetValUnmanaged";
private readonly string _returnIdentifier;
private readonly string _nativeReturnIdentifier;
// We can't use ReturnIdentifier or ReturnNativeIdentifier since that will be used by the managed return value.
// Additionally, since all use cases today of a TypePositionInfo in the native position but not the managed
// are for infos that aren't in the managed signature at all (PreserveSig scenario), we don't have a name
- // that we can use from source. As a result, we generate another name for the native return value
+ // that we can use from source.
+ // If this changes, the assert below will trigger and we will need to decide what to do.
+ // As a result, we generate another name for the native return value
// and use the same name for native and managed.
else if (info.IsNativeReturnPosition)
{
Debug.Assert(info.ManagedIndex == TypePositionInfo.UnsetIndex);
- return (InvokeReturnIdentifier, InvokeReturnIdentifier);
+ return (InvokeReturnIdentifier, InvokeReturnIdentifierNative);
}
else
{
new LinearCollectionElementMarshallingCodeContext(StubCodeContext.Stage.Setup, string.Empty, string.Empty, context));
ExpressionSyntax numElementsExpression = LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(0));
- if (info.IsManagedReturnPosition || (info.IsByRef && info.RefKind != RefKind.In))
+ if (MarshallerHelpers.GetMarshalDirection(info, context) != MarshalDirection.ManagedToUnmanaged)
{
// In this case, we need a numElementsExpression supplied from metadata, so we'll calculate it here.
numElementsExpression = GetNumElementsExpressionFromMarshallingInfo(info, marshalInfo.ElementCountInfo, context);
{
return ValueBoundaryBehavior.ManagedIdentifier;
}
- else if (context.SingleFrameSpansNativeContext && !info.IsManagedReturnPosition)
+ else if (context.SingleFrameSpansNativeContext && !context.IsInStubReturnPosition(info))
{
return ValueBoundaryBehavior.NativeIdentifier;
}
public IEnumerable<StatementSyntax> Generate(TypePositionInfo info, StubCodeContext context)
{
- if (!info.IsByRef || info.IsManagedReturnPosition)
+ if (!info.IsByRef || context.IsInStubReturnPosition(info))
yield break;
(string managedIdentifier, string nativeIdentifier) = context.GetIdentifiers(info);
public bool UsesNativeIdentifier(TypePositionInfo info, StubCodeContext context)
{
- return info.IsByRef && !info.IsManagedReturnPosition && !context.SingleFrameSpansNativeContext;
+ return info.IsByRef && !context.IsInStubReturnPosition(info) && !context.SingleFrameSpansNativeContext;
}
public bool SupportsByValueMarshalKind(ByValueContentsMarshalKind marshalKind, StubCodeContext context) => false;
public bool UsesNativeIdentifier(TypePositionInfo info, StubCodeContext context)
{
- return info.IsManagedReturnPosition || (info.IsByRef && !context.SingleFrameSpansNativeContext);
+ return context.IsInStubReturnPosition(info) || (info.IsByRef && !context.SingleFrameSpansNativeContext);
}
public bool SupportsByValueMarshalKind(ByValueContentsMarshalKind marshalKind, StubCodeContext context) => false;
private static bool IsPinningPathSupported(TypePositionInfo info, StubCodeContext context)
{
return context.SingleFrameSpansNativeContext
- && !info.IsManagedReturnPosition
+ && !context.IsInStubReturnPosition(info)
&& info.IsByRef;
}
private const string ParameterIdentifierSuffix = "param";
/// <summary>
- /// Gets a parameter for the unmanaged signature that represents the provided <paramref name="info"/>.
+ /// Gets a parameter for the unmanaged signature that represents the provided <paramref name="info"/> in the given <paramref name="context"/>.
/// </summary>
/// <param name="generator">The marshalling generator for this <paramref name="info"/></param>
/// <param name="info">Object to marshal</param>
+ /// <param name="context">The stub marshalling context</param>
public static ParameterSyntax AsParameter(this IMarshallingGenerator generator, TypePositionInfo info, StubCodeContext context)
{
SignatureBehavior behavior = generator.GetNativeSignatureBehavior(info);
if (behavior == SignatureBehavior.ManagedTypeAndAttributes)
{
- return GenerateForwardingParameter(info);
+ return GenerateForwardingParameter(info, context.GetIdentifiers(info).managed);
}
string identifierName;
if (context.Direction == MarshalDirection.ManagedToUnmanaged)
});
}
- private static ParameterSyntax GenerateForwardingParameter(TypePositionInfo info)
+ private static ParameterSyntax GenerateForwardingParameter(TypePositionInfo info, string identifier)
{
- ParameterSyntax param = Parameter(Identifier(info.InstanceIdentifier))
+ ParameterSyntax param = Parameter(Identifier(identifier))
.WithModifiers(TokenList(Token(info.RefKindSyntax)))
.WithType(info.ManagedType.Syntax);
}
private static bool IsPinningPathSupported(TypePositionInfo info, StubCodeContext context)
{
- return context.SingleFrameSpansNativeContext && !info.IsByRef && !info.IsManagedReturnPosition;
+ return context.SingleFrameSpansNativeContext && !info.IsByRef && !context.IsInStubReturnPosition(info);
}
private IEnumerable<StatementSyntax> GeneratePinningPath(TypePositionInfo info, StubCodeContext context)
}
// If the info is in the managed return position but is not in the native return position,
// then that means that the stub is introducing an additional info for the return position.
- // This means that there is no name in source for this info, so we must provide one here.
+ // This element can be in any position in the native signature,
+ // but since it isn't in the managed signature, there is no name in source for this info, so we must provide one here.
// We can't use ReturnIdentifier or ReturnNativeIdentifier since that will be used by the return value of the stub itself.
- // Additionally, we don't have a name that we can use from source.
// As a result, we generate another name for the native return value.
if (info.IsManagedReturnPosition)
{
- Debug.Assert(info.NativeIndex == TypePositionInfo.UnsetIndex);
return (InvokeReturnIdentifier, InvokeReturnIdentifierNative);
}
retTypeInfo = retTypeInfo with
{
ManagedIndex = TypePositionInfo.ReturnIndex,
- NativeIndex = TypePositionInfo.ReturnIndex
+ NativeIndex = TypePositionInfo.ReturnIndex,
};
typeInfos.Add(retTypeInfo);
{
return $"{GetIdentifiers(info).native}__{name}";
}
+
+ /// <summary>
+ /// Compute if the provided element is the return element for the stub that is being generated (not any inner call).
+ /// </summary>
+ /// <param name="info">The element information</param>
+ /// <returns><c>true</c> if the element is in the return position for this stub; otherwise, false.</returns>
+ public bool IsInStubReturnPosition(TypePositionInfo info)
+ {
+ if (Direction == MarshalDirection.ManagedToUnmanaged)
+ {
+ return info.IsManagedReturnPosition;
+ }
+ else if (Direction == MarshalDirection.UnmanagedToManaged)
+ {
+ return info.IsNativeReturnPosition;
+ }
+
+ throw new InvalidOperationException("Stub contexts should not be bidirectional");
+ }
}
}
}
// Stub return is not the same as invoke return
- if (!marshallers.IsManagedVoidReturn && !marshallers.ManagedNativeSameReturn)
+ if (!marshallers.IsManagedVoidReturn)
{
// Declare variables for stub return value
AppendVariableDeclarations(variables, marshallers.ManagedReturnMarshaller, context, initializeToDefault: initializeDeclarations);
}
- if (!marshallers.IsManagedVoidReturn)
+ if (!marshallers.IsUnmanagedVoidReturn && !marshallers.ManagedNativeSameReturn)
{
// Declare variables for invoke return value
AppendVariableDeclarations(variables, marshallers.NativeReturnMarshaller, context, initializeToDefault: initializeDeclarations);
foreach (BoundGenerator marshaller in marshallers.NativeParameterMarshallers)
{
TypePositionInfo info = marshaller.TypeInfo;
- if (info.IsNativeReturnPosition)
+ if (info.IsNativeReturnPosition || info.IsManagedReturnPosition)
continue;
// Declare variables for parameters
AppendVariableDeclarations(variables, marshaller, context, initializeToDefault: initializeDeclarations);
}
- // Stub return is not the same as invoke return
- if (!marshallers.IsManagedVoidReturn && !marshallers.ManagedNativeSameReturn)
+ if (!marshallers.IsManagedVoidReturn)
{
// Declare variables for stub return value
AppendVariableDeclarations(variables, marshallers.ManagedReturnMarshaller, context, initializeToDefault: initializeDeclarations);
}
- if (!marshallers.IsManagedVoidReturn)
+ if (!marshallers.IsUnmanagedVoidReturn && !marshallers.ManagedNativeSameReturn)
{
// Declare variables for invoke return value
AppendVariableDeclarations(variables, marshallers.NativeReturnMarshaller, context, initializeToDefault: initializeDeclarations);
initializeToDefault: true));
}
}
- else if (marshaller.TypeInfo.IsManagedReturnPosition)
- {
- statementsToUpdate.Add(MarshallerHelpers.Declare(
- marshaller.TypeInfo.ManagedType.Syntax,
- managed,
- initializeToDefault));
-
- if (marshaller.Generator.UsesNativeIdentifier(marshaller.TypeInfo, context))
- {
- statementsToUpdate.Add(MarshallerHelpers.Declare(
- marshaller.Generator.AsNativeType(marshaller.TypeInfo).Syntax,
- native,
- initializeToDefault));
- }
- }
else
{
ValueBoundaryBehavior boundaryBehavior = marshaller.Generator.GetValueBoundaryBehavior(marshaller.TypeInfo, context);
{VirtualMethodIndex(0)}
[return:MarshalUsing(ConstantElementCount=10)]
{collectionType} Method(
- {collectionType} p,
- in {collectionType} pIn,
+ [MarshalUsing(CountElementName = ""pSize"")] {collectionType} p,
+ int pSize,
+ [MarshalUsing(CountElementName = ""pInSize"")] in {collectionType} pIn,
+ in int pInSize,
int pRefSize,
[MarshalUsing(CountElementName = ""pRefSize"")] ref {collectionType} pRef,
[MarshalUsing(CountElementName = ""pOutSize"")] out {collectionType} pOut,
{AttributeProvider.VirtualMethodIndex(0, ImplicitThisParameter: ImplicitThisParameter, Direction: Direction)}
[return:MarshalUsing(ConstantElementCount=10)]
{collectionType} Method(
- {collectionType} p,
- in {collectionType} pIn,
+ [MarshalUsing(CountElementName = ""pSize"")] {collectionType} p,
+ int pSize,
+ [MarshalUsing(CountElementName = ""pInSize"")] in {collectionType} pIn,
+ in int pInSize,
int pRefSize,
[MarshalUsing(CountElementName = ""pRefSize"")] ref {collectionType} pRef,
[MarshalUsing(CountElementName = ""pOutSize"")] out {collectionType} pOut,
{AttributeProvider.VirtualMethodIndex(0, ImplicitThisParameter: ImplicitThisParameter, Direction: Direction)}
[return:MarshalUsing(typeof({marshallerType}), ConstantElementCount=10)]
{collectionType} Method(
- [MarshalUsing(typeof({marshallerType}))] {collectionType} p,
- [MarshalUsing(typeof({marshallerType}))] in {collectionType} pIn,
+ [MarshalUsing(typeof({marshallerType}), CountElementName = ""pSize"")] {collectionType} p,
+ [MarshalUsing(typeof({marshallerType}), CountElementName = ""pInSize"")] in {collectionType} pIn,
+ int pSize,
+ in int pInSize,
int pRefSize,
[MarshalUsing(typeof({marshallerType}), CountElementName = ""pRefSize"")] ref {collectionType} pRef,
[MarshalUsing(typeof({marshallerType}), CountElementName = ""pOutSize"")] out {collectionType} pOut,
[return:MarshalUsing(ConstantElementCount=10)]
[return:MarshalUsing(typeof({elementMarshaller}), ElementIndirectionDepth = 1)]
TestCollection<int> Method(
- [MarshalUsing(typeof({elementMarshaller}), ElementIndirectionDepth = 1)] {collectionType} p,
- [MarshalUsing(typeof({elementMarshaller}), ElementIndirectionDepth = 1)] in {collectionType} pIn,
+ [MarshalUsing(CountElementName = ""pSize""), MarshalUsing(typeof({elementMarshaller}), ElementIndirectionDepth = 1)] {collectionType} p,
+ [MarshalUsing(CountElementName = ""pInSize""), MarshalUsing(typeof({elementMarshaller}), ElementIndirectionDepth = 1)] in {collectionType} pIn,
+ int pSize,
+ in int pInSize,
int pRefSize,
[MarshalUsing(CountElementName = ""pRefSize""), MarshalUsing(typeof({elementMarshaller}), ElementIndirectionDepth = 1)] ref {collectionType} pRef,
[MarshalUsing(CountElementName = ""pOutSize"")][MarshalUsing(typeof({elementMarshaller}), ElementIndirectionDepth = 1)] out {collectionType} pOut,