| File: Marshalling\UnmanagedToManagedOwnershipTrackingStrategy.cs | Web Access |
| Project: 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. namespace Microsoft.Interop { /// <summary> /// Tracks the original unmanaged value and whether it has been replaced with a new value. /// </summary> /// <seealso cref="CleanupOwnedOriginalValueMarshalling"/> internal sealed class UnmanagedToManagedOwnershipTrackingStrategy(ICustomTypeMarshallingStrategy innerMarshaller) : ICustomTypeMarshallingStrategy { public ManagedTypeInfo NativeType => innerMarshaller.NativeType; public bool UsesNativeIdentifier => innerMarshaller.UsesNativeIdentifier; public TypePositionInfo TypeInfo => innerMarshaller.TypeInfo; public StubCodeContext CodeContext => innerMarshaller.CodeContext; public void GenerateCleanupCallerAllocatedResourcesStatements(IndentedTextWriter writer, StubIdentifierContext context) => innerMarshaller.GenerateCleanupCallerAllocatedResourcesStatements(writer, context); public void GenerateCleanupCalleeAllocatedResourcesStatements(IndentedTextWriter writer, StubIdentifierContext context) => innerMarshaller.GenerateCleanupCalleeAllocatedResourcesStatements(writer, context); public void GenerateGuaranteedUnmarshalStatements(IndentedTextWriter writer, StubIdentifierContext context) => innerMarshaller.GenerateGuaranteedUnmarshalStatements(writer, context); public void GenerateMarshalStatements(IndentedTextWriter writer, StubIdentifierContext context) { innerMarshaller.GenerateMarshalStatements(writer, context); // Only take ownership after the replacement has been marshalled successfully. writer.WriteLine($"{context.GetAdditionalIdentifier(TypeInfo, OwnershipTrackingHelpers.OwnOriginalValueIdentifier)} = true;"); } public void GenerateNotifyForSuccessfulInvokeStatements(IndentedTextWriter writer, StubIdentifierContext context) => innerMarshaller.GenerateNotifyForSuccessfulInvokeStatements(writer, context); public void GeneratePinnedMarshalStatements(IndentedTextWriter writer, StubIdentifierContext context) => innerMarshaller.GeneratePinnedMarshalStatements(writer, context); public void GeneratePinStatements(IndentedTextWriter writer, StubIdentifierContext context) => innerMarshaller.GeneratePinStatements(writer, context); public void GenerateSetupStatements(IndentedTextWriter writer, StubIdentifierContext context) { innerMarshaller.GenerateSetupStatements(writer, context); writer.WriteLine($"bool {context.GetAdditionalIdentifier(TypeInfo, OwnershipTrackingHelpers.OwnOriginalValueIdentifier)} = false;"); OwnershipTrackingHelpers.DeclareOriginalValueIdentifier(writer, TypeInfo, context, NativeType); } public void GenerateUnmarshalCaptureStatements(IndentedTextWriter writer, StubIdentifierContext context) => innerMarshaller.GenerateUnmarshalCaptureStatements(writer, context); public void GenerateUnmarshalStatements(IndentedTextWriter writer, StubIdentifierContext context) => innerMarshaller.GenerateUnmarshalStatements(writer, context); } /// <summary> /// Cleans the original unmanaged value once the ownership-tracking strategy has replaced it. /// </summary> internal sealed class CleanupOwnedOriginalValueMarshalling(ICustomTypeMarshallingStrategy innerMarshaller) : ICustomTypeMarshallingStrategy { public ManagedTypeInfo NativeType => innerMarshaller.NativeType; public bool UsesNativeIdentifier => innerMarshaller.UsesNativeIdentifier; public TypePositionInfo TypeInfo => innerMarshaller.TypeInfo; public StubCodeContext CodeContext => innerMarshaller.CodeContext; public void GenerateCleanupCallerAllocatedResourcesStatements(IndentedTextWriter writer, StubIdentifierContext context) { if (MarshallerHelpers.GetCleanupStage(TypeInfo, CodeContext) is not StubIdentifierContext.Stage.CleanupCallerAllocated) { return; } writer.WriteLine($"if ({context.GetAdditionalIdentifier(TypeInfo, OwnershipTrackingHelpers.OwnOriginalValueIdentifier)})"); using (writer.WriteBlock()) { innerMarshaller.GenerateCleanupCallerAllocatedResourcesStatements(writer, new OwnedValueCodeContext(context)); } } public void GenerateCleanupCalleeAllocatedResourcesStatements(IndentedTextWriter writer, StubIdentifierContext context) { if (MarshallerHelpers.GetCleanupStage(TypeInfo, CodeContext) is not StubIdentifierContext.Stage.CleanupCalleeAllocated) { return; } writer.WriteLine($"if ({context.GetAdditionalIdentifier(TypeInfo, OwnershipTrackingHelpers.OwnOriginalValueIdentifier)})"); using (writer.WriteBlock()) { innerMarshaller.GenerateCleanupCalleeAllocatedResourcesStatements(writer, new OwnedValueCodeContext(context)); } } public void GenerateGuaranteedUnmarshalStatements(IndentedTextWriter writer, StubIdentifierContext context) => innerMarshaller.GenerateGuaranteedUnmarshalStatements(writer, context); public void GenerateMarshalStatements(IndentedTextWriter writer, StubIdentifierContext context) => innerMarshaller.GenerateMarshalStatements(writer, context); public void GenerateNotifyForSuccessfulInvokeStatements(IndentedTextWriter writer, StubIdentifierContext context) => innerMarshaller.GenerateNotifyForSuccessfulInvokeStatements(writer, context); public void GeneratePinnedMarshalStatements(IndentedTextWriter writer, StubIdentifierContext context) => innerMarshaller.GeneratePinnedMarshalStatements(writer, context); public void GeneratePinStatements(IndentedTextWriter writer, StubIdentifierContext context) => innerMarshaller.GeneratePinStatements(writer, context); public void GenerateSetupStatements(IndentedTextWriter writer, StubIdentifierContext context) => innerMarshaller.GenerateSetupStatements(writer, context); public void GenerateUnmarshalCaptureStatements(IndentedTextWriter writer, StubIdentifierContext context) => innerMarshaller.GenerateUnmarshalCaptureStatements(writer, context); public void GenerateUnmarshalStatements(IndentedTextWriter writer, StubIdentifierContext context) => innerMarshaller.GenerateUnmarshalStatements(writer, context); } /// <summary> /// Caches and cleans the original native value when every path reaching cleanup owns that value. /// </summary> internal sealed class FreeAlwaysOwnedOriginalValueGenerator(IBoundMarshallingGenerator inner) : IBoundMarshallingGenerator { public ManagedTypeInfo NativeType => inner.NativeType; public TypePositionInfo TypeInfo => inner.TypeInfo; public StubCodeContext CodeContext => inner.CodeContext; public SignatureBehavior NativeSignatureBehavior => inner.NativeSignatureBehavior; public bool UsesNativeIdentifier => inner.UsesNativeIdentifier; public ValueBoundaryBehavior ValueBoundaryBehavior => inner.ValueBoundaryBehavior; public void Generate(IndentedTextWriter writer, StubIdentifierContext context) { if (context.CurrentStage == StubIdentifierContext.Stage.Setup) { inner.Generate(writer, new OwnedValueCodeContext(context)); OwnershipTrackingHelpers.DeclareOriginalValueIdentifier(writer, TypeInfo, context, NativeType); return; } inner.Generate(writer, context.CurrentStage == StubIdentifierContext.Stage.CleanupCallerAllocated ? new OwnedValueCodeContext(context) : context); } public ByValueMarshalKindSupport SupportsByValueMarshalKind(ByValueContentsMarshalKind marshalKind, out GeneratorDiagnostic? diagnostic) => inner.SupportsByValueMarshalKind(marshalKind, out diagnostic); } file sealed record OwnedValueCodeContext : StubIdentifierContext { private readonly StubIdentifierContext _innerContext; public OwnedValueCodeContext(StubIdentifierContext innerContext) { _innerContext = innerContext; CurrentStage = innerContext.CurrentStage; } public override (string managed, string native) GetIdentifiers(TypePositionInfo info) { string managed = _innerContext.GetIdentifiers(info).managed; return (managed, _innerContext.GetAdditionalIdentifier(info, OwnershipTrackingHelpers.OriginalValueIdentifier)); } public override string GetAdditionalIdentifier(TypePositionInfo info, string name) => _innerContext.GetAdditionalIdentifier(info, name); } file static class OwnershipTrackingHelpers { public const string OwnOriginalValueIdentifier = "ownOriginal"; public const string OriginalValueIdentifier = "original"; public static void DeclareOriginalValueIdentifier(IndentedTextWriter writer, TypePositionInfo info, StubIdentifierContext context, ManagedTypeInfo nativeType) { writer.WriteLine($"{nativeType.FullTypeName} {context.GetAdditionalIdentifier(info, OriginalValueIdentifier)} = {context.GetIdentifiers(info).native};"); } } }