SharpGen/Generator/ReverseCallablePrologCodeGenerator.cs (247 lines of code) (raw):

using System.Collections.Generic; using System.Diagnostics; using System.Runtime.CompilerServices; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using SharpGen.Generator.Marshallers; using SharpGen.Model; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; namespace SharpGen.Generator { internal sealed class ReverseCallablePrologCodeGenerator : CodeGeneratorBase, IMultiCodeGenerator<(CsCallable, InteropMethodSignature), StatementSyntax> { private static readonly LiteralExpressionSyntax DefaultLiteral = LiteralExpression( SyntaxKind.DefaultLiteralExpression, Token(SyntaxKind.DefaultKeyword) ); public IEnumerable<StatementSyntax> GenerateCode((CsCallable, InteropMethodSignature) callableSig) { var (csElement, interopSig) = callableSig; if (!interopSig.ForcedReturnBufferSig && csElement.HasReturnTypeValue) { foreach (var statement in GenerateProlog(csElement.ReturnValue, null, null)) { yield return statement; } } foreach (var signatureParameter in interopSig.ParameterTypes) { var publicParameter = signatureParameter.Item; var nativeParameter = IdentifierName(signatureParameter.Name); var interopTypeSyntax = signatureParameter.InteropTypeSyntax; var builder = GetPrologBuilder(publicParameter); foreach (var statement in builder(publicParameter, nativeParameter, interopTypeSyntax)) yield return statement; } } private PrologGenerationDelegate GetPrologBuilder(CsMarshalCallableBase publicParameter) => publicParameter.PassedByNativeReference && !publicParameter.IsArray ? GenerateNativeByRefProlog : GenerateProlog; private delegate IEnumerable<StatementSyntax> PrologGenerationDelegate( CsMarshalCallableBase publicElement, ExpressionSyntax nativeParameter, TypeSyntax nativeParameterType ); private IEnumerable<StatementSyntax> GenerateProlog(CsMarshalCallableBase publicElement, ExpressionSyntax nativeParameter, TypeSyntax nativeParameterType) { ExpressionSyntax CastToPublicType(TypeSyntax targetType, ExpressionSyntax expression) => targetType.IsEquivalentTo(nativeParameterType) ? expression : GeneratorHelpers.CastExpression(targetType, expression); var marshaller = GetMarshaller(publicElement); var publicType = GetPublicType(publicElement); var generatesMarshalVariable = marshaller.GeneratesMarshalVariable(publicElement); var publicTypeVariableValue = nativeParameter != null && !generatesMarshalVariable ? CastToPublicType(publicType, nativeParameter) : DefaultLiteral; yield return LocalDeclarationStatement( VariableDeclaration( publicType, SingletonSeparatedList( VariableDeclarator(Identifier(publicElement.Name)) .WithInitializer(EqualsValueClause(publicTypeVariableValue)) ) ) ); if (generatesMarshalVariable) { var marshalTypeSyntax = marshaller.GetMarshalTypeSyntax(publicElement); var initializerExpression = nativeParameter != null ? CastToPublicType(marshalTypeSyntax, nativeParameter) : DefaultLiteral; yield return LocalDeclarationStatement( VariableDeclaration( marshalTypeSyntax, SingletonSeparatedList( VariableDeclarator( MarshallerBase.GetMarshalStorageLocationIdentifier(publicElement), null, EqualsValueClause(initializerExpression) ) ) ) ); } } internal static TypeSyntax GetPublicType(CsMarshalCallableBase publicElement) { var publicType = ParseTypeName(publicElement.PublicType.QualifiedName); return publicElement.IsArray ? ArrayType(publicType, SingletonList(ArrayRankSpecifier())) : publicType; } private IEnumerable<StatementSyntax> GenerateNativeByRefProlog(CsMarshalCallableBase publicElement, ExpressionSyntax nativeParameter, TypeSyntax nativeParameterType) { var marshaller = GetMarshaller(publicElement); var marshalTypeSyntax = marshaller.GetMarshalTypeSyntax(publicElement); var publicType = ParseTypeName(publicElement.PublicType.QualifiedName); var generatesMarshalVariable = marshaller.GeneratesMarshalVariable(publicElement); ExpressionSyntax publicDefaultValue = publicElement.UsedAsReturn ? default : DefaultLiteral; var refToNativeClause = GenerateAsRefInitializer(publicElement, nativeParameter, marshalTypeSyntax); TypeSyntax publicVariableType, marshalVariableType; ExpressionSyntax publicVariableInitializer, marshalVariableInitializer; if (publicElement is CsParameter {IsOptional: true, IsLocalManagedReference: true} parameter) { Debug.Assert(marshaller is RefWrapperMarshaller); var refVariableDeclaration = LocalDeclarationStatement( VariableDeclaration( RefType(marshalTypeSyntax), SingletonSeparatedList( VariableDeclarator( MarshallerBase.GetRefLocationIdentifier(publicElement), default, EqualsValueClause(refToNativeClause) ) ) ) ); publicVariableType = publicType; marshalVariableType = marshalTypeSyntax; publicVariableInitializer = publicDefaultValue; marshalVariableInitializer = DefaultLiteral; if (generatesMarshalVariable && parameter is {IsRef: true}) { Logger.Error( null, "Optional ref parameter [{0}] that requires generating marshal variable is unsupported.", parameter.QualifiedName ); } else { Debug.Assert(!generatesMarshalVariable || parameter is {IsOut: true}); } yield return refVariableDeclaration; } else { Debug.Assert(marshaller is not RefWrapperMarshaller); marshalVariableInitializer = refToNativeClause; if (publicElement is {IsLocalManagedReference: true}) { Debug.Assert(publicElement is CsReturnValue or CsParameter {IsOptional: false}); marshalVariableType = RefType(marshalTypeSyntax); publicVariableType = generatesMarshalVariable ? publicType : RefType(publicType); publicVariableInitializer = generatesMarshalVariable ? publicDefaultValue : refToNativeClause; } else { Debug.Assert(publicElement is CsParameter {IsRefIn: true}); var isNullable = publicElement is CsParameter {PassedByNullableInstance: true}; marshalVariableType = marshalTypeSyntax; publicVariableType = isNullable ? NullableType(publicType) : publicType; publicVariableInitializer = generatesMarshalVariable ? isNullable ? ConditionalExpression( BinaryExpression( SyntaxKind.NotEqualsExpression, nativeParameter, DefaultLiteral ), ImplicitObjectCreationExpression(), LiteralExpression(SyntaxKind.NullLiteralExpression) ) : publicDefaultValue : refToNativeClause; } } if (generatesMarshalVariable) { yield return LocalDeclarationStatement( VariableDeclaration( marshalVariableType, SingletonSeparatedList( VariableDeclarator( MarshallerBase.GetMarshalStorageLocationIdentifier(publicElement), default, EqualsValueClause(marshalVariableInitializer) ) ) ) ); } yield return LocalDeclarationStatement( VariableDeclaration( publicVariableType, SingletonSeparatedList( VariableDeclarator( Identifier(publicElement.Name), default, publicVariableInitializer != default ? EqualsValueClause(publicVariableInitializer) : default ) ) ) ); } private ExpressionSyntax GenerateAsRefInitializer(CsMarshalCallableBase publicElement, ExpressionSyntax nativeParameter, TypeSyntax marshalTypeSyntax) { var refToNativeExpression = InvocationExpression( MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, GlobalNamespace.GetTypeNameSyntax(BuiltinType.Unsafe), GenericName( Identifier(nameof(Unsafe.AsRef)), TypeArgumentList(SingletonSeparatedList(marshalTypeSyntax)) ) ), ArgumentList(SingletonSeparatedList(Argument(nativeParameter))) ); ExpressionSyntax refToNativeClauseExpression; if (publicElement.IsLocalManagedReference) { Debug.Assert( publicElement is CsReturnValue or CsParameter { Attribute: CsParameterAttribute.Ref or CsParameterAttribute.Out } ); refToNativeClauseExpression = RefExpression(refToNativeExpression); } else { Debug.Assert(publicElement is CsParameter {IsRefIn: true}); if (publicElement is CsParameter {IsOptional: true}) { refToNativeClauseExpression = ConditionalExpression( BinaryExpression(SyntaxKind.NotEqualsExpression, nativeParameter, DefaultLiteral), refToNativeExpression, DefaultLiteral ); } else { refToNativeClauseExpression = refToNativeExpression; } } return refToNativeClauseExpression; } public ReverseCallablePrologCodeGenerator(Ioc ioc) : base(ioc) { } } }