| File: UnmanagedToManagedStubGenerator.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.Collections.Immutable; using System.Diagnostics; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; namespace Microsoft.Interop { public sealed class UnmanagedToManagedStubGenerator { private const string ReturnIdentifier = "__retVal"; private readonly BoundGenerators _marshallers; private readonly StubIdentifierContext _context; public UnmanagedToManagedStubGenerator( ImmutableArray<TypePositionInfo> argTypes, GeneratorDiagnosticsBag diagnosticsBag, IMarshallingGeneratorResolver generatorResolver) { _marshallers = BoundGenerators.Create(argTypes, generatorResolver, StubCodeContext.DefaultNativeToManagedStub, new Forwarder(), out var bindingDiagnostics); diagnosticsBag.ReportGeneratorDiagnostics(bindingDiagnostics); if (_marshallers.NativeReturnMarshaller.UsesNativeIdentifier) { // If we need a different native return identifier, then recreate the context with the correct identifier before we generate any code. _context = new DefaultIdentifierContext(ReturnIdentifier, $"{ReturnIdentifier}{StubIdentifierContext.GeneratedNativeIdentifierSuffix}", MarshalDirection.UnmanagedToManaged); } else { _context = new DefaultIdentifierContext(ReturnIdentifier, ReturnIdentifier, MarshalDirection.UnmanagedToManaged); } } /// <summary> /// Generate the method body of the unmanaged-to-managed ComWrappers-based method stub. /// </summary> /// <param name="methodToInvoke">Name of the method on the managed type to invoke</param> /// <returns>Method body of the stub</returns> /// <remarks> /// The generated code assumes it will be in an unsafe context. /// </remarks> public BlockSyntax GenerateStubBodyForMethod(ExpressionSyntax methodToInvoke) { GeneratedStatements statements = GeneratedStatements.Create( _marshallers, StubCodeContext.DefaultNativeToManagedStub, _context, methodToInvoke); return BuildBodyFromStatements(statements); } /// <summary> /// Generate the method body of the unmanaged-to-managed ComWrappers-based stub for a property /// accessor. The natural <c>methodToInvoke(args)</c> shape doesn't apply — for a getter the /// body assigns the property read into the return slot (<c>retVal = propertyAccess</c>) and /// for a setter it assigns the value parameter into the property (<c>propertyAccess = value</c>). /// </summary> /// <param name="propertyAccess">Member-access expression naming the managed-side property /// (e.g., <c>@this.Foo</c>).</param> /// <param name="isSetter">True if this stub is the property setter; false for the getter.</param> public BlockSyntax GenerateStubBodyForProperty(ExpressionSyntax propertyAccess, bool isSetter) { GeneratedStatements statements = GeneratedStatements.CreateForProperty( _marshallers, _context, propertyAccess, isSetter); return BuildBodyFromStatements(statements); } /// <summary> /// Generate the method body of the unmanaged-to-managed ComWrappers-based stub for an indexer /// accessor. Wraps the supplied <paramref name="instance"/> in an /// <see cref="ElementAccessExpressionSyntax"/> built from the marshalled identifiers for the /// index parameters (which are all managed parameters for an indexer getter, and all-but-the-last /// managed parameter for an indexer setter — the trailing entry is the implicit /// <c>value</c>). The resulting access expression is then handled by the same property-stub /// pipeline. /// </summary> /// <param name="instance">Expression naming the managed-side target instance (e.g., /// <c>@this</c>); the bracketed argument list is appended to this expression.</param> /// <param name="isSetter">True if this stub is the indexer setter; false for the getter.</param> public BlockSyntax GenerateStubBodyForIndexer(ExpressionSyntax instance, bool isSetter) { var managedParameterMarshallers = _marshallers.ManagedParameterMarshallers; int indexCount = isSetter ? managedParameterMarshallers.Length - 1 : managedParameterMarshallers.Length; var argBuilder = ImmutableArray.CreateBuilder<ArgumentSyntax>(indexCount); for (int i = 0; i < indexCount; i++) { argBuilder.Add(managedParameterMarshallers[i].AsManagedArgument(_context)); } ExpressionSyntax elementAccess = ElementAccessExpression( instance, BracketedArgumentList(SeparatedList(argBuilder.MoveToImmutable()))); return GenerateStubBodyForProperty(elementAccess, isSetter); } private BlockSyntax BuildBodyFromStatements(GeneratedStatements statements) { Debug.Assert(statements.CleanupCalleeAllocated.IsEmpty); bool shouldInitializeVariables = !statements.GuaranteedUnmarshal.IsEmpty || !statements.CleanupCallerAllocated.IsEmpty || !statements.ManagedExceptionCatchClauses.IsEmpty; VariableDeclarations declarations = VariableDeclarations.GenerateDeclarationsForUnmanagedToManaged(_marshallers, _context, shouldInitializeVariables); List<StatementSyntax> setupStatements = [ .. declarations.Initializations, .. declarations.Variables, .. statements.Setup, ]; List<StatementSyntax> tryStatements = [ .. statements.GuaranteedUnmarshal, .. statements.Unmarshal, statements.InvokeStatement, .. statements.NotifyForSuccessfulInvoke, .. statements.Marshal, .. statements.PinnedMarshal, ]; List<StatementSyntax> allStatements = setupStatements; SyntaxList<CatchClauseSyntax> catchClauses = List(statements.ManagedExceptionCatchClauses); ImmutableArray<StatementSyntax> finallyStatements = statements.CleanupCallerAllocated; if (finallyStatements.Length > 0) { allStatements.Add( TryStatement(Block(tryStatements), catchClauses, FinallyClause(Block(finallyStatements)))); } else if (catchClauses.Count > 0) { allStatements.Add( TryStatement(Block(tryStatements), catchClauses, @finally: null)); } else { allStatements.AddRange(tryStatements); } // Return if (!_marshallers.IsUnmanagedVoidReturn) allStatements.Add(ReturnStatement(IdentifierName(_context.GetIdentifiers(_marshallers.NativeReturnMarshaller.TypeInfo).native))); return Block(allStatements); } public (ParameterListSyntax ParameterList, TypeSyntax ReturnType, AttributeListSyntax? ReturnTypeAttributes) GenerateAbiMethodSignatureData() { return _marshallers.GenerateTargetMethodSignatureData(_context); } } }