File: Marshallers\StructAsHResultMarshallerFactory.cs
Web Access
Project: src\src\libraries\System.Runtime.InteropServices\gen\ComInterfaceGenerator\ComInterfaceGenerator.csproj (Microsoft.Interop.ComInterfaceGenerator)
// 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.Runtime.InteropServices;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
using static Microsoft.Interop.SyntaxFactoryExtensions;
 
namespace Microsoft.Interop
{
    internal sealed class StructAsHResultMarshallerFactory : IMarshallingGeneratorResolver
    {
        private static readonly Marshaller s_marshaller = new();
 
        public ResolvedGenerator Create(TypePositionInfo info, StubCodeContext context)
        {
            // Value type with MarshalAs(UnmanagedType.Error), to be marshalled as an unmanaged HRESULT.
            if (info is { ManagedType: ValueTypeInfo, MarshallingAttributeInfo: MarshalAsInfo(UnmanagedType.Error, _) })
            {
                return ResolvedGenerator.Resolved(s_marshaller.Bind(info, context));
            }
 
            return ResolvedGenerator.UnresolvedGenerator;
        }
 
        private sealed class Marshaller : IUnboundMarshallingGenerator
        {
            public ManagedTypeInfo AsNativeType(TypePositionInfo info) => SpecialTypeInfo.Int32;
 
            public IEnumerable<StatementSyntax> Generate(TypePositionInfo info, StubCodeContext codeContext, StubIdentifierContext context)
            {
                var (managed, unmanaged) = context.GetIdentifiers(info);
 
                switch (context.CurrentStage)
                {
                    case StubIdentifierContext.Stage.Marshal:
                        if (MarshallerHelpers.GetMarshalDirection(info, codeContext) is MarshalDirection.ManagedToUnmanaged or MarshalDirection.Bidirectional)
                        {
                            // unmanaged = Unsafe.BitCast<managedType, int>(managed);
                            yield return AssignmentStatement(
                                IdentifierName(unmanaged),
                                MethodInvocation(
                                    ParseTypeName(TypeNames.System_Runtime_CompilerServices_Unsafe),
                                    GenericName(Identifier("BitCast"),
                                        TypeArgumentList(
                                            SeparatedList(new[]
                                                {
                                                    info.ManagedType.Syntax,
                                                    AsNativeType(info).Syntax
                                                }))),
                                    Argument(IdentifierName(managed))));
                        }
                        break;
                    case StubIdentifierContext.Stage.Unmarshal:
                        if (MarshallerHelpers.GetMarshalDirection(info, codeContext) is MarshalDirection.UnmanagedToManaged or MarshalDirection.Bidirectional)
                        {
                            // managed = Unsafe.BitCast<int, managedType>(unmanaged);
                            yield return AssignmentStatement(
                            IdentifierName(managed),
                            MethodInvocation(
                                ParseTypeName(TypeNames.System_Runtime_CompilerServices_Unsafe),
                                GenericName(Identifier("BitCast"),
                                    TypeArgumentList(
                                        SeparatedList(new[]
                                            {
                                                AsNativeType(info).Syntax,
                                                info.ManagedType.Syntax
                                            }))),
                                Argument(IdentifierName(unmanaged))));
                        }
                        break;
                    default:
                        break;
                }
            }
 
            public SignatureBehavior GetNativeSignatureBehavior(TypePositionInfo info)
            {
                return info.IsByRef ? SignatureBehavior.PointerToNativeType : SignatureBehavior.NativeType;
            }
 
            public ValueBoundaryBehavior GetValueBoundaryBehavior(TypePositionInfo info, StubCodeContext context)
            {
                if (info.IsByRef)
                {
                    return ValueBoundaryBehavior.AddressOfNativeIdentifier;
                }
 
                return ValueBoundaryBehavior.NativeIdentifier;
            }
 
            public ByValueMarshalKindSupport SupportsByValueMarshalKind(ByValueContentsMarshalKind marshalKind, TypePositionInfo info, out GeneratorDiagnostic? diagnostic)
                => ByValueMarshalKindSupportDescriptor.Default.GetSupport(marshalKind, info, out diagnostic);
 
            public bool UsesNativeIdentifier(TypePositionInfo info, StubCodeContext context) => true;
        }
    }
}