|
// 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.CSharp.Syntax;
namespace Microsoft.Interop
{
/// <summary>
/// An enumeration describing how a <see cref="TypePositionInfo"/> should be represented in its corresponding native signature element (parameter, field, or return value).
/// </summary>
public enum SignatureBehavior
{
/// <summary>
/// The native type should match the managed type, including rehydrating marshalling attributes and by-ref syntax (pure forwarding).
/// </summary>
ManagedTypeAndAttributes,
/// <summary>
/// The native signature should be the type returned by <see cref="IUnboundMarshallingGenerator.AsNativeType(TypePositionInfo)"/> passed by value.
/// </summary>
NativeType,
/// <summary>
/// The native signature should be a pointer to the type returned by <see cref="IUnboundMarshallingGenerator.AsNativeType(TypePositionInfo)"/> passed by value.
/// </summary>
PointerToNativeType
}
/// <summary>
/// An enumeration describing how a <see cref="TypePositionInfo"/> should be represented in its corresponding native signature element (parameter, field, or return value).
/// </summary>
public enum ValueBoundaryBehavior
{
/// <summary>
/// The managed value should be passed as-is, including any managed by-ref syntax used in the managed declaration.
/// </summary>
ManagedIdentifier,
/// <summary>
/// The native identifier provided by <see cref="StubIdentifierContext.GetIdentifiers(TypePositionInfo)"/> should be passed by value.
/// </summary>
NativeIdentifier,
/// <summary>
/// The address of the native identifier provided by <see cref="StubIdentifierContext.GetIdentifiers(TypePositionInfo)"/> should be passed by value.
/// </summary>
AddressOfNativeIdentifier,
/// <summary>
/// The native identifier provided by <see cref="StubIdentifierContext.GetIdentifiers(TypePositionInfo)"/> should be cast to the native type.
/// </summary>
CastNativeIdentifier
}
/// <summary>
/// An enumeration describing if the provided <see cref="ByValueContentsMarshalKind" /> is supported and changes behavior from the default behavior.
/// </summary>
public enum ByValueMarshalKindSupport
{
/// <summary>
/// The provided <see cref="ByValueContentsMarshalKind" /> is supported and changes behavior from the default behavior.
/// </summary>
Supported,
/// <summary>
/// The provided <see cref="ByValueContentsMarshalKind" /> is not supported.
/// </summary>
NotSupported,
/// <summary>
/// The provided <see cref="ByValueContentsMarshalKind" /> is supported but does not change behavior from the default in this scenario.
/// </summary>
Unnecessary,
/// <summary>
/// The provided <see cref="ByValueContentsMarshalKind" /> is supported but does not follow best practices.
/// </summary>
NotRecommended,
}
/// <summary>
/// Interface for generation of marshalling code for P/Invoke stubs
/// </summary>
public interface IBoundMarshallingGenerator
{
/// <summary>
/// The managed type and position information this generator is bound to.
/// </summary>
TypePositionInfo TypeInfo { get; }
/// <summary>
/// The context into which this generator is bound and will generate code.
/// </summary>
StubCodeContext CodeContext { get; }
/// <summary>
/// Get the native type for the bound element of the generator.
/// </summary>
ManagedTypeInfo NativeType { get; }
/// <summary>
/// Get the shape that represents the bound element in the native signature
/// </summary>
SignatureBehavior NativeSignatureBehavior { get; }
/// <summary>
/// Get the shape of how the value represented by this generator should be passed at the managed/native boundary in the provided <paramref name="context"/>
/// </summary>
/// <param name="context">Code generation context</param>
/// <returns>How to represent the unmanaged value at the managed/unmanaged boundary</returns>
ValueBoundaryBehavior ValueBoundaryBehavior { get; }
/// <summary>
/// Generate code for marshalling
/// </summary>
/// <param name="context">Code generation context</param>
/// <returns>List of statements to be added to the P/Invoke stub</returns>
/// <remarks>
/// The generator should return the appropriate statements based on the
/// <see cref="StubIdentifierContext.CurrentStage" /> of <paramref name="context"/>.
/// For <see cref="StubIdentifierContext.Stage.Pin"/>, any statements not of type
/// <see cref="FixedStatementSyntax"/> will be ignored.
/// </remarks>
IEnumerable<StatementSyntax> Generate(StubIdentifierContext context);
/// <summary>
/// Returns whether or not this marshaller uses an identifier for the native value in addition
/// to an identifier for the managed value.
/// </summary>
/// <param name="context">Code generation context</param>
/// <returns>If the marshaller uses an identifier for the native value, true; otherwise, false.</returns>
/// <remarks>
/// <see cref="StubIdentifierContext.CurrentStage" /> of <paramref name="context"/> may not be valid.
/// </remarks>
bool UsesNativeIdentifier { get; }
/// <summary>
/// Returns if the given ByValueContentsMarshalKind is supported in the current marshalling context.
/// A supported marshal kind has a different behavior than the default behavior.
/// </summary>
/// <param name="marshalKind">The marshal kind.</param>
/// <param name="info">The TypePositionInfo of the parameter.</param>
/// <param name="context">The marshalling context.</param>
/// <param name="diagnostic">
/// The diagnostic to report if the return value is not <see cref="ByValueMarshalKindSupport.Supported"/>.
/// It should be non-null if the value is not <see cref="ByValueMarshalKindSupport.Supported"/>
/// </param>
/// <returns>If the provided <paramref name="marshalKind"/> is supported and if it is required to specify the requested behavior.</returns>
ByValueMarshalKindSupport SupportsByValueMarshalKind(ByValueContentsMarshalKind marshalKind, out GeneratorDiagnostic? diagnostic);
}
/// <summary>
/// A bound generator that binds an <see cref="IUnboundMarshallingGenerator"/> to a specific <see cref="TypePositionInfo"/>.
/// </summary>
/// <param name="info">The element info.</param>
/// <param name="unbound">The unbound generator</param>
internal sealed class BoundMarshallingGenerator(TypePositionInfo info, StubCodeContext context, IUnboundMarshallingGenerator unbound) : IBoundMarshallingGenerator
{
internal bool IsForwarder => unbound is Forwarder;
internal bool IsBlittable => unbound is BlittableMarshaller;
public TypePositionInfo TypeInfo => info;
public StubCodeContext CodeContext => context;
public ManagedTypeInfo NativeType => unbound.AsNativeType(TypeInfo);
public SignatureBehavior NativeSignatureBehavior => unbound.GetNativeSignatureBehavior(TypeInfo);
public IEnumerable<StatementSyntax> Generate(StubIdentifierContext context) => unbound.Generate(TypeInfo, CodeContext, context);
public ValueBoundaryBehavior ValueBoundaryBehavior => unbound.GetValueBoundaryBehavior(TypeInfo, context);
public ByValueMarshalKindSupport SupportsByValueMarshalKind(ByValueContentsMarshalKind marshalKind, out GeneratorDiagnostic? diagnostic)
=> unbound.SupportsByValueMarshalKind(marshalKind, TypeInfo, out diagnostic);
public bool UsesNativeIdentifier => unbound.UsesNativeIdentifier(TypeInfo, context);
}
public static class UnboundMarshallingGeneratorExtensions
{
/// <summary>
/// Bind this marshalling generator to a specific element info.
/// </summary>
/// <param name="unbound">The unbound generator</param>
/// <param name="info">The element info</param>
/// <returns>A generator wrapper that is bound to this info.</returns>
/// <param name="context"></param>
public static IBoundMarshallingGenerator Bind(this IUnboundMarshallingGenerator unbound, TypePositionInfo info, StubCodeContext context) => new BoundMarshallingGenerator(info, context, unbound);
}
/// <summary>
/// Interface for generation of marshalling code for P/Invoke stubs
/// </summary>
public interface IUnboundMarshallingGenerator
{
/// <summary>
/// Get the native type syntax for <paramref name="info"/>
/// </summary>
/// <param name="info">Object to marshal</param>
/// <returns>Managed type info for the native type representing <paramref name="info"/></returns>
ManagedTypeInfo AsNativeType(TypePositionInfo info);
/// <summary>
/// Get shape that represents the provided <paramref name="info"/> in the native signature
/// </summary>
/// <param name="info">Object to marshal</param>
/// <returns>Parameter syntax for <paramref name="info"/></returns>
SignatureBehavior GetNativeSignatureBehavior(TypePositionInfo info);
/// <summary>
/// Get shape of how the value represented by <paramref name="info"/> should be passed at the managed/native boundary in the provided <paramref name="context"/>
/// </summary>
/// <param name="info">Object to marshal</param>
/// <param name="context">Code generation context</param>
/// <returns>Argument syntax for <paramref name="info"/></returns>
ValueBoundaryBehavior GetValueBoundaryBehavior(TypePositionInfo info, StubCodeContext context);
/// <summary>
/// Generate code for marshalling
/// </summary>
/// <param name="info">Object to marshal</param>
/// <param name="codeContext">Code generation context</param>
/// <param name="context">Context to get identifiers</param>
/// <returns>List of statements to be added to the P/Invoke stub</returns>
/// <remarks>
/// The generator should return the appropriate statements based on the
/// <see cref="StubIdentifierContext.CurrentStage" /> of <paramref name="context"/>.
/// For <see cref="StubIdentifierContext.Stage.Pin"/>, any statements not of type
/// <see cref="FixedStatementSyntax"/> will be ignored.
/// </remarks>
IEnumerable<StatementSyntax> Generate(TypePositionInfo info, StubCodeContext codeContext, StubIdentifierContext context);
/// <summary>
/// Returns whether or not this marshaller uses an identifier for the native value in addition
/// to an identifier for the managed value.
/// </summary>
/// <param name="info">Object to marshal</param>
/// <param name="context">Code generation context</param>
/// <returns>If the marshaller uses an identifier for the native value, true; otherwise, false.</returns>
bool UsesNativeIdentifier(TypePositionInfo info, StubCodeContext context);
/// <summary>
/// Returns if the given ByValueContentsMarshalKind is supported in the current marshalling context.
/// A supported marshal kind has a different behavior than the default behavior.
/// </summary>
/// <param name="marshalKind">The marshal kind.</param>
/// <param name="info">The TypePositionInfo of the parameter.</param>
/// <param name="diagnostic">
/// The diagnostic to report if the return value is not <see cref="ByValueMarshalKindSupport.Supported"/>.
/// It should be non-null if the value is not <see cref="ByValueMarshalKindSupport.Supported"/>
/// </param>
/// <returns>If the provided <paramref name="marshalKind"/> is supported and if it is required to specify the requested behavior.</returns>
ByValueMarshalKindSupport SupportsByValueMarshalKind(ByValueContentsMarshalKind marshalKind, TypePositionInfo info, out GeneratorDiagnostic? diagnostic);
}
}
|