File: Marshalling\StaticPinnableManagedValueMarshaller.cs
Web Access
Project: src\src\libraries\System.Runtime.InteropServices\gen\Microsoft.Interop.SourceGeneration\Microsoft.Interop.SourceGeneration.csproj (Microsoft.Interop.SourceGeneration)
// 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 Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
 
namespace Microsoft.Interop
{
    public sealed class StaticPinnableManagedValueMarshaller(IBoundMarshallingGenerator innerMarshallingGenerator, TypeSyntax getPinnableReferenceType) : IBoundMarshallingGenerator
    {
        public TypePositionInfo TypeInfo => innerMarshallingGenerator.TypeInfo;
 
        public StubCodeContext CodeContext => innerMarshallingGenerator.CodeContext;
 
        public ManagedTypeInfo NativeType => innerMarshallingGenerator.NativeType;
 
        public SignatureBehavior NativeSignatureBehavior => innerMarshallingGenerator.NativeSignatureBehavior;
 
        public ValueBoundaryBehavior ValueBoundaryBehavior
        {
            get
            {
                if (IsPinningPathSupported(CodeContext))
                {
                    if (NativeType.Syntax is PointerTypeSyntax pointerType
                        && pointerType.ElementType is PredefinedTypeSyntax predefinedType
                        && predefinedType.Keyword.IsKind(SyntaxKind.VoidKeyword))
                    {
                        return Interop.ValueBoundaryBehavior.NativeIdentifier;
                    }
 
                    // Cast to native type if it is not void*
                    return Interop.ValueBoundaryBehavior.CastNativeIdentifier;
                }
 
                return innerMarshallingGenerator.ValueBoundaryBehavior;
            }
        }
 
        public IEnumerable<StatementSyntax> Generate(StubIdentifierContext context)
        {
            if (IsPinningPathSupported(CodeContext))
            {
                return GeneratePinningPath(context);
            }
 
            return innerMarshallingGenerator.Generate(context);
        }
 
        public bool UsesNativeIdentifier
        {
            get
            {
                if (IsPinningPathSupported(CodeContext))
                {
                    return false;
                }
 
                return innerMarshallingGenerator.UsesNativeIdentifier;
            }
        }
 
        private bool IsPinningPathSupported(StubCodeContext context)
        {
            return context.SingleFrameSpansNativeContext && !TypeInfo.IsByRef && !context.IsInStubReturnPosition(TypeInfo);
        }
 
        private IEnumerable<StatementSyntax> GeneratePinningPath(StubIdentifierContext context)
        {
            if (context.CurrentStage == StubIdentifierContext.Stage.Pin)
            {
                (string managedIdentifier, string nativeIdentifier) = context.GetIdentifiers(innerMarshallingGenerator.TypeInfo);
 
                // fixed (void* <nativeIdentifier> = &<getPinnableReferenceType>.GetPinnableReference(<managedIdentifier>))
                yield return FixedStatement(
                    VariableDeclaration(
                        PointerType(PredefinedType(Token(SyntaxKind.VoidKeyword))),
                        SingletonSeparatedList(
                            VariableDeclarator(Identifier(nativeIdentifier))
                                .WithInitializer(EqualsValueClause(
                                    PrefixUnaryExpression(SyntaxKind.AddressOfExpression,
                                    InvocationExpression(
                                        MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
                                            getPinnableReferenceType,
                                            IdentifierName(ShapeMemberNames.GetPinnableReference)),
                                        ArgumentList(SingletonSeparatedList(
                                            Argument(IdentifierName(managedIdentifier))))))
                                ))
                        )
                    ),
                    EmptyStatement());
            }
        }
 
        public ByValueMarshalKindSupport SupportsByValueMarshalKind(ByValueContentsMarshalKind marshalKind, out GeneratorDiagnostic? diagnostic)
        {
            return innerMarshallingGenerator.SupportsByValueMarshalKind(marshalKind, out diagnostic);
        }
    }
}