| File: Marshalling\MarshallingGeneratorExtensions.cs | Web Access |
| Project: src\runtime\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 System.Runtime.InteropServices; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; namespace Microsoft.Interop { public static class MarshallingGeneratorExtensions { /// <summary> /// Gets the return type for the unmanaged signature that represents the provided <paramref name="info"/>. /// </summary> /// <param name="generator">The marshalling generator for this <paramref name="info"/></param> /// <param name="info">Object to marshal</param> public static TypeSyntax AsReturnType(this IBoundMarshallingGenerator generator) { return generator.NativeSignatureBehavior switch { SignatureBehavior.ManagedTypeAndAttributes => generator.TypeInfo.ManagedType.Syntax, SignatureBehavior.NativeType => generator.NativeType.Syntax, SignatureBehavior.PointerToNativeType => PointerType(generator.NativeType.Syntax), _ => throw new InvalidOperationException() }; } /// <summary> /// Gets any attributes that should be applied to the return type for this <paramref name="info"/>. /// </summary> /// <param name="generator">The marshalling generator for this <paramref name="info"/></param> /// <param name="info">Object to marshal</param> /// <returns>Attributes for the return type for this <paramref name="info"/>, or <c>null</c> if no attributes should be added.</returns> public static AttributeListSyntax? GenerateAttributesForReturnType(this IBoundMarshallingGenerator generator) { if (generator.NativeSignatureBehavior != SignatureBehavior.ManagedTypeAndAttributes) { return null; } if (generator.TypeInfo.MarshallingAttributeInfo is IForwardedMarshallingInfo forwarded && forwarded.TryCreateAttributeSyntax(out AttributeSyntax forwardedAttribute)) { return AttributeList(SingletonSeparatedList(forwardedAttribute)); } return null; } private const string ParameterIdentifierSuffix = "param"; /// <summary> /// 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="context">The stub marshalling context</param> public static ParameterSyntax AsParameter(this IBoundMarshallingGenerator generator, StubIdentifierContext context) { SignatureBehavior behavior = generator.NativeSignatureBehavior; if (behavior == SignatureBehavior.ManagedTypeAndAttributes) { return GenerateForwardingParameter(generator.TypeInfo, context.GetIdentifiers(generator.TypeInfo).managed); } string identifierName; if (generator.CodeContext.Direction == MarshalDirection.ManagedToUnmanaged) { // This name doesn't get introduced into the stub's scope, so we can make it pretty // and reuse the native identifier identifierName = context.GetIdentifiers(generator.TypeInfo).native; } else if (generator.CodeContext.Direction == MarshalDirection.UnmanagedToManaged) { // This name is introduced into the stub's scope. // When we are passing the managed identifier as-is, we can just use that name everywhere. // When we're passing the native identifier as-is or casting the value to the native type in managed->unmanaged cases, // we can use the native identifier. // When we're passing the address of the native identifier, we need to introduce a new name to hold this value // before we assign it to the managed value. (string managed, string native) = context.GetIdentifiers(generator.TypeInfo); string param = context.GetAdditionalIdentifier(generator.TypeInfo, ParameterIdentifierSuffix); identifierName = generator.ValueBoundaryBehavior switch { ValueBoundaryBehavior.ManagedIdentifier => generator.TypeInfo.IsByRef ? param : managed, ValueBoundaryBehavior.NativeIdentifier or ValueBoundaryBehavior.CastNativeIdentifier => native, ValueBoundaryBehavior.AddressOfNativeIdentifier => param, _ => throw new UnreachableException() }; } else { throw new ArgumentException("Context direction must be ManagedToUnmanaged or UnmanagedToManaged"); } return Parameter(Identifier(identifierName)) .WithType(behavior switch { SignatureBehavior.NativeType => generator.NativeType.Syntax, SignatureBehavior.PointerToNativeType => PointerType(generator.NativeType.Syntax), _ => throw new InvalidOperationException() }); } private static ParameterSyntax GenerateForwardingParameter(TypePositionInfo info, string identifier) { ParameterSyntax param = Parameter(Identifier(identifier)) .WithModifiers(MarshallerHelpers.GetManagedParameterModifiers(info)) .WithType(info.ManagedType.Syntax); List<AttributeSyntax> rehydratedAttributes = new(); if (info.MarshallingAttributeInfo is IForwardedMarshallingInfo forwardedMarshallingInfo && forwardedMarshallingInfo.TryCreateAttributeSyntax(out AttributeSyntax forwardedAttribute)) { rehydratedAttributes.Add(forwardedAttribute); } if (info.ByValueContentsMarshalKind.HasFlag(ByValueContentsMarshalKind.In)) { rehydratedAttributes.Add(Attribute(IdentifierName(TypeNames.System_Runtime_InteropServices_InAttribute))); } if (info.ByValueContentsMarshalKind.HasFlag(ByValueContentsMarshalKind.Out)) { rehydratedAttributes.Add(Attribute(IdentifierName(TypeNames.System_Runtime_InteropServices_OutAttribute))); } if (rehydratedAttributes.Count > 0) { param = param.AddAttributeLists(AttributeList(SeparatedList(rehydratedAttributes))); } return param; } /// <summary> /// Gets an argument expression for the unmanaged signature that can be used to pass a value of the provided <paramref name="info" /> in the specified <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">Marshalling context</param> public static ArgumentSyntax AsArgument(this IBoundMarshallingGenerator generator, StubIdentifierContext context) { TypePositionInfo info = generator.TypeInfo; (string managedIdentifier, string nativeIdentifier) = context.GetIdentifiers(info); return generator.ValueBoundaryBehavior switch { ValueBoundaryBehavior.ManagedIdentifier when !info.IsByRef => Argument(IdentifierName(managedIdentifier)), ValueBoundaryBehavior.ManagedIdentifier when info.IsByRef => Argument(IdentifierName(managedIdentifier)).WithRefKindKeyword(MarshallerHelpers.GetManagedArgumentRefKindKeyword(info)), ValueBoundaryBehavior.NativeIdentifier => Argument(IdentifierName(nativeIdentifier)), ValueBoundaryBehavior.AddressOfNativeIdentifier => Argument(PrefixUnaryExpression(SyntaxKind.AddressOfExpression, IdentifierName(nativeIdentifier))), ValueBoundaryBehavior.CastNativeIdentifier => Argument(CastExpression(generator.AsParameter(context).Type, IdentifierName(nativeIdentifier))), _ => throw new InvalidOperationException() }; } public static ArgumentSyntax AsManagedArgument(this IBoundMarshallingGenerator generator, StubIdentifierContext context) { TypePositionInfo info = generator.TypeInfo; var (managedIdentifier, _) = context.GetIdentifiers(info); if (info.IsByRef) { return Argument(IdentifierName(managedIdentifier)).WithRefKindKeyword(MarshallerHelpers.GetManagedArgumentRefKindKeyword(info)); } return Argument(IdentifierName(managedIdentifier)); } public static ExpressionSyntax GenerateNativeByRefInitialization(this IBoundMarshallingGenerator generator, StubIdentifierContext context) { TypePositionInfo info = generator.TypeInfo; string paramIdentifier = context.GetAdditionalIdentifier(info, ParameterIdentifierSuffix); return RefExpression(PrefixUnaryExpression(SyntaxKind.PointerIndirectionExpression, IdentifierName(paramIdentifier))); } public static bool IsForwarder(this IBoundMarshallingGenerator generator) => generator is BoundMarshallingGenerator { IsForwarder: true }; public static bool IsBlittable(this IBoundMarshallingGenerator generator) => generator is BoundMarshallingGenerator { IsBlittable: true }; } }