File: VariableDeclarations.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.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
using static Microsoft.Interop.SyntaxFactoryExtensions;
 
namespace Microsoft.Interop
{
    public struct VariableDeclarations
    {
        public ImmutableArray<StatementSyntax> Initializations { get; init; }
        public ImmutableArray<LocalDeclarationStatementSyntax> Variables { get; init; }
        public static VariableDeclarations GenerateDeclarationsForManagedToUnmanaged(BoundGenerators marshallers, StubIdentifierContext context, bool initializeDeclarations)
        {
            ImmutableArray<StatementSyntax>.Builder initializations = ImmutableArray.CreateBuilder<StatementSyntax>();
            ImmutableArray<LocalDeclarationStatementSyntax>.Builder variables = ImmutableArray.CreateBuilder<LocalDeclarationStatementSyntax>();
 
            foreach (IBoundMarshallingGenerator marshaller in marshallers.NativeParameterMarshallers)
            {
                TypePositionInfo info = marshaller.TypeInfo;
                if (info.IsManagedReturnPosition)
                    continue;
 
                if (info.RefKind == RefKind.Out)
                {
                    initializations.Add(MarshallerHelpers.DefaultInit(info, context));
                }
 
                // Declare variables for parameters
                AppendVariableDeclarations(variables, marshaller, context, initializeToDefault: initializeDeclarations);
            }
 
            // Stub return is not the same as invoke return
            if (!marshallers.IsManagedVoidReturn)
            {
                // Declare variables for stub return value
                AppendVariableDeclarations(variables, marshallers.ManagedReturnMarshaller, context, initializeToDefault: initializeDeclarations);
            }
 
            if (!marshallers.IsUnmanagedVoidReturn && !marshallers.ManagedNativeSameReturn)
            {
                // Declare variables for invoke return value
                AppendVariableDeclarations(variables, marshallers.NativeReturnMarshaller, context, initializeToDefault: initializeDeclarations);
            }
 
            return new VariableDeclarations
            {
                Initializations = initializations.ToImmutable(),
                Variables = variables.ToImmutable()
            };
 
            static void AppendVariableDeclarations(ImmutableArray<LocalDeclarationStatementSyntax>.Builder statementsToUpdate, IBoundMarshallingGenerator marshaller, StubIdentifierContext context, bool initializeToDefault)
            {
                (string managed, string native) = context.GetIdentifiers(marshaller.TypeInfo);
 
                // Declare variable for return value
                if (marshaller.TypeInfo.IsManagedReturnPosition || marshaller.TypeInfo.IsNativeReturnPosition)
                {
                    statementsToUpdate.Add(Declare(
                        marshaller.TypeInfo.ManagedType.Syntax,
                        managed,
                        initializeToDefault));
                }
 
                // Declare variable with native type for parameter or return value
                if (marshaller.UsesNativeIdentifier)
                {
                    statementsToUpdate.Add(Declare(
                        marshaller.NativeType.Syntax,
                        native,
                        initializeToDefault));
                }
            }
        }
 
        public static VariableDeclarations GenerateDeclarationsForUnmanagedToManaged(BoundGenerators marshallers, StubIdentifierContext context, bool initializeDeclarations)
        {
            ImmutableArray<StatementSyntax>.Builder initializations = ImmutableArray.CreateBuilder<StatementSyntax>();
            ImmutableArray<LocalDeclarationStatementSyntax>.Builder variables = ImmutableArray.CreateBuilder<LocalDeclarationStatementSyntax>();
 
            foreach (IBoundMarshallingGenerator marshaller in marshallers.NativeParameterMarshallers)
            {
                TypePositionInfo info = marshaller.TypeInfo;
                if (info.IsNativeReturnPosition || info.IsManagedReturnPosition)
                    continue;
 
                // Declare variables for parameters
                AppendVariableDeclarations(variables, marshaller, context, initializeToDefault: initializeDeclarations);
            }
 
            if (!marshallers.IsManagedVoidReturn)
            {
                // Declare variables for stub return value
                AppendVariableDeclarations(variables, marshallers.ManagedReturnMarshaller, context, initializeToDefault: initializeDeclarations);
            }
 
            if (!marshallers.IsUnmanagedVoidReturn && !marshallers.ManagedNativeSameReturn)
            {
                // Declare variables for invoke return value
                AppendVariableDeclarations(variables, marshallers.NativeReturnMarshaller, context, initializeToDefault: initializeDeclarations);
            }
 
            return new VariableDeclarations
            {
                Initializations = initializations.ToImmutable(),
                Variables = variables.ToImmutable()
            };
 
            static void AppendVariableDeclarations(ImmutableArray<LocalDeclarationStatementSyntax>.Builder statementsToUpdate, IBoundMarshallingGenerator marshaller, StubIdentifierContext context, bool initializeToDefault)
            {
                (string managed, string native) = context.GetIdentifiers(marshaller.TypeInfo);
 
                // Declare variable for return value
                if (marshaller.TypeInfo.IsNativeReturnPosition)
                {
                    bool nativeReturnUsesNativeIdentifier = marshaller.UsesNativeIdentifier;
 
                    // Always initialize the return value.
                    statementsToUpdate.Add(Declare(
                        marshaller.TypeInfo.ManagedType.Syntax,
                        managed,
                        initializeToDefault || !nativeReturnUsesNativeIdentifier));
 
                    if (nativeReturnUsesNativeIdentifier)
                    {
                        statementsToUpdate.Add(Declare(
                            marshaller.NativeType.Syntax,
                            native,
                            initializeToDefault: true));
                    }
                }
                else
                {
                    ValueBoundaryBehavior boundaryBehavior = marshaller.ValueBoundaryBehavior;
 
                    // Declare variable with native type for parameter
                    // if the marshaller uses the native identifier and the signature uses a different identifier
                    // than the native identifier.
                    if (marshaller.UsesNativeIdentifier
                        && boundaryBehavior is not
                            (ValueBoundaryBehavior.NativeIdentifier or ValueBoundaryBehavior.CastNativeIdentifier))
                    {
                        TypeSyntax localType = marshaller.NativeType.Syntax;
                        if (boundaryBehavior != ValueBoundaryBehavior.AddressOfNativeIdentifier)
                        {
                            statementsToUpdate.Add(Declare(
                                localType,
                                native,
                                false));
                        }
                        else
                        {
                            // To simplify propogating back the value to the "byref" parameter,
                            // we'll just declare the native identifier as a ref to its type.
                            // The rest of the code we generate will work as expected, and we don't need
                            // to manually propogate back the updated values after the call.
                            statementsToUpdate.Add(Declare(
                                RefType(localType),
                                native,
                                marshaller.GenerateNativeByRefInitialization(context)));
                        }
                    }
 
                    if (boundaryBehavior != ValueBoundaryBehavior.ManagedIdentifier)
                    {
                        statementsToUpdate.Add(Declare(
                            marshaller.TypeInfo.ManagedType.Syntax,
                            managed,
                            initializeToDefault));
                    }
                }
            }
        }
    }
}