Don't cache the Compilation object in steps in the interop source generators (#76474)
authorJeremy Koritzinsky <jekoritz@microsoft.com>
Fri, 7 Oct 2022 15:07:19 +0000 (08:07 -0700)
committerGitHub <noreply@github.com>
Fri, 7 Oct 2022 15:07:19 +0000 (08:07 -0700)
* Directly pass along target framework kind and version instead of StubEnvironment to avoid rooting the compilation. Update all generators to follow suit and add a test in LibraryImportGenerator to validate that we don't keep the compilation alive.

* Use a wrapper struct to make using elementwise equality for immutable arrays easier.

* Remove accidentally included file

* PR feedback

13 files changed:
src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSExportCodeGenerator.cs
src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSExportGenerator.cs
src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSImportCodeGenerator.cs
src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSImportGenerator.cs
src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/ConvertToLibraryImportAnalyzer.cs
src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/LibraryImportGenerator.cs
src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/LibraryImportGenerator.csproj
src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/PInvokeStubCodeGenerator.cs
src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/ManagedToNativeStubCodeContext.cs
src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/SequenceEqualImmutableArray.cs [new file with mode: 0644]
src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/StubEnvironment.cs
src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/SyntaxEquivalentComparer.cs
src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/IncrementalGenerationTests.cs

index 3c44d66..614bad0 100644 (file)
@@ -20,7 +20,8 @@ namespace Microsoft.Interop.JavaScript
         private readonly JSSignatureContext _signatureContext;
 
         public JSExportCodeGenerator(
-            StubEnvironment environment,
+            TargetFramework targetFramework,
+            Version targetFrameworkVersion,
             ImmutableArray<TypePositionInfo> argTypes,
             JSExportData attributeData,
             JSSignatureContext signatureContext,
@@ -28,13 +29,13 @@ namespace Microsoft.Interop.JavaScript
             IMarshallingGeneratorFactory generatorFactory)
         {
             _signatureContext = signatureContext;
-            ManagedToNativeStubCodeContext innerContext = new ManagedToNativeStubCodeContext(environment, ReturnIdentifier, ReturnIdentifier);
+            ManagedToNativeStubCodeContext innerContext = new ManagedToNativeStubCodeContext(targetFramework, targetFrameworkVersion, ReturnIdentifier, ReturnIdentifier);
             _context = new JSExportCodeContext(attributeData, innerContext);
             _marshallers = new BoundGenerators(argTypes, CreateGenerator);
             if (_marshallers.ManagedReturnMarshaller.Generator.UsesNativeIdentifier(_marshallers.ManagedReturnMarshaller.TypeInfo, null))
             {
                 // If we need a different native return identifier, then recreate the context with the correct identifier before we generate any code.
-                innerContext = new ManagedToNativeStubCodeContext(environment, ReturnIdentifier, ReturnNativeIdentifier);
+                innerContext = new ManagedToNativeStubCodeContext(targetFramework, targetFrameworkVersion, ReturnIdentifier, ReturnNativeIdentifier);
                 _context = new JSExportCodeContext(attributeData, innerContext);
                 _marshallers = new BoundGenerators(argTypes, CreateGenerator);
             }
index c033637..ab4c93d 100644 (file)
@@ -17,33 +17,13 @@ namespace Microsoft.Interop.JavaScript
     public sealed class JSExportGenerator : IIncrementalGenerator
     {
         internal sealed record IncrementalStubGenerationContext(
-            StubEnvironment Environment,
             JSSignatureContext SignatureContext,
             ContainingSyntaxContext ContainingSyntaxContext,
             ContainingSyntax StubMethodSyntaxTemplate,
             MethodSignatureDiagnosticLocations DiagnosticLocation,
             JSExportData JSExportData,
-            MarshallingGeneratorFactoryKey<(TargetFramework, Version, JSGeneratorOptions)> GeneratorFactoryKey,
-            ImmutableArray<Diagnostic> Diagnostics)
-        {
-            public bool Equals(IncrementalStubGenerationContext? other)
-            {
-                return other is not null
-                    && StubEnvironment.AreCompilationSettingsEqual(Environment, other.Environment)
-                    && SignatureContext.Equals(other.SignatureContext)
-                    && ContainingSyntaxContext.Equals(other.ContainingSyntaxContext)
-                    && StubMethodSyntaxTemplate.Equals(other.StubMethodSyntaxTemplate)
-                    && JSExportData.Equals(other.JSExportData)
-                    && DiagnosticLocation.Equals(DiagnosticLocation)
-                    && GeneratorFactoryKey.Equals(other.GeneratorFactoryKey)
-                    && Diagnostics.SequenceEqual(other.Diagnostics);
-            }
-
-            public override int GetHashCode()
-            {
-                throw new UnreachableException();
-            }
-        }
+            MarshallingGeneratorFactoryKey<(TargetFramework TargetFramework, Version TargetFrameworkVersion, JSGeneratorOptions)> GeneratorFactoryKey,
+            SequenceEqualImmutableArray<Diagnostic> Diagnostics);
 
         public static class StepNames
         {
@@ -119,9 +99,8 @@ namespace Microsoft.Interop.JavaScript
                     static (data, ct) => CalculateStubInformation(data.Syntax, data.Symbol, data.Environment, data.Options, ct)
                 )
                 .WithTrackingName(StepNames.CalculateStubInformation)
-                .Combine(stubOptions)
                 .Select(
-                    static (data, ct) => GenerateSource(data.Left, data.Right)
+                    static (data, ct) => GenerateSource(data)
                 )
                 .WithComparer(Comparers.GeneratedSyntax)
                 .WithTrackingName(StepNames.GenerateSingleStub);
@@ -215,14 +194,13 @@ namespace Microsoft.Interop.JavaScript
             var methodSyntaxTemplate = new ContainingSyntax(originalSyntax.Modifiers.StripTriviaFromTokens(), SyntaxKind.MethodDeclaration, originalSyntax.Identifier, originalSyntax.TypeParameterList);
 
             return new IncrementalStubGenerationContext(
-                environment,
                 signatureContext,
                 containingTypeContext,
                 methodSyntaxTemplate,
                 new MethodSignatureDiagnosticLocations(originalSyntax),
                 jsExportData,
                 CreateGeneratorFactory(environment, options),
-                generatorDiagnostics.Diagnostics.ToImmutableArray());
+                new SequenceEqualImmutableArray<Diagnostic>(generatorDiagnostics.Diagnostics.ToImmutableArray()));
         }
 
         private static MarshallingGeneratorFactoryKey<(TargetFramework, Version, JSGeneratorOptions)> CreateGeneratorFactory(StubEnvironment env, JSGeneratorOptions options)
@@ -232,14 +210,14 @@ namespace Microsoft.Interop.JavaScript
         }
 
         private static (MemberDeclarationSyntax, ImmutableArray<Diagnostic>) GenerateSource(
-            IncrementalStubGenerationContext incrementalContext,
-            JSGeneratorOptions options)
+            IncrementalStubGenerationContext incrementalContext)
         {
             var diagnostics = new GeneratorDiagnostics();
 
             // Generate stub code
             var stubGenerator = new JSExportCodeGenerator(
-            incrementalContext.Environment,
+            incrementalContext.GeneratorFactoryKey.Key.TargetFramework,
+            incrementalContext.GeneratorFactoryKey.Key.TargetFrameworkVersion,
             incrementalContext.SignatureContext.SignatureContext.ElementTypeInformation,
             incrementalContext.JSExportData,
             incrementalContext.SignatureContext,
@@ -252,7 +230,7 @@ namespace Microsoft.Interop.JavaScript
             BlockSyntax wrapper = stubGenerator.GenerateJSExportBody();
             BlockSyntax registration = stubGenerator.GenerateJSExportRegistration();
 
-            return (PrintGeneratedSource(incrementalContext.StubMethodSyntaxTemplate, incrementalContext.SignatureContext, incrementalContext.ContainingSyntaxContext, wrapper, registration), incrementalContext.Diagnostics.AddRange(diagnostics.Diagnostics));
+            return (PrintGeneratedSource(incrementalContext.StubMethodSyntaxTemplate, incrementalContext.SignatureContext, incrementalContext.ContainingSyntaxContext, wrapper, registration), incrementalContext.Diagnostics.Array.AddRange(diagnostics.Diagnostics));
         }
 
         private static bool ShouldVisitNode(SyntaxNode syntaxNode)
index b7c1a32..1f1ebdf 100644 (file)
@@ -27,7 +27,8 @@ namespace Microsoft.Interop.JavaScript
         private readonly JSSignatureContext _signatureContext;
 
         public JSImportCodeGenerator(
-            StubEnvironment environment,
+            TargetFramework targetFramework,
+            Version targetFrameworkVersion,
             ImmutableArray<TypePositionInfo> argTypes,
             JSImportData attributeData,
             JSSignatureContext signatureContext,
@@ -35,13 +36,13 @@ namespace Microsoft.Interop.JavaScript
             IMarshallingGeneratorFactory generatorFactory)
         {
             _signatureContext = signatureContext;
-            ManagedToNativeStubCodeContext innerContext = new ManagedToNativeStubCodeContext(environment, ReturnIdentifier, ReturnIdentifier);
+            ManagedToNativeStubCodeContext innerContext = new ManagedToNativeStubCodeContext(targetFramework, targetFrameworkVersion, ReturnIdentifier, ReturnIdentifier);
             _context = new JSImportCodeContext(attributeData, innerContext);
             _marshallers = new BoundGenerators(argTypes, CreateGenerator);
             if (_marshallers.ManagedReturnMarshaller.Generator.UsesNativeIdentifier(_marshallers.ManagedReturnMarshaller.TypeInfo, null))
             {
                 // If we need a different native return identifier, then recreate the context with the correct identifier before we generate any code.
-                innerContext = new ManagedToNativeStubCodeContext(environment, ReturnIdentifier, ReturnNativeIdentifier);
+                innerContext = new ManagedToNativeStubCodeContext(targetFramework, targetFrameworkVersion, ReturnIdentifier, ReturnNativeIdentifier);
                 _context = new JSImportCodeContext(attributeData, innerContext);
                 _marshallers = new BoundGenerators(argTypes, CreateGenerator);
             }
index 0ce7744..4a38741 100644 (file)
@@ -2,12 +2,9 @@
 // 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 System.Linq;
-using System.Runtime.InteropServices;
-using System.Text;
 using System.Threading;
 using Microsoft.CodeAnalysis;
 using Microsoft.CodeAnalysis.CSharp;
@@ -22,33 +19,13 @@ namespace Microsoft.Interop.JavaScript
     public sealed class JSImportGenerator : IIncrementalGenerator
     {
         internal sealed record IncrementalStubGenerationContext(
-            StubEnvironment Environment,
             JSSignatureContext SignatureContext,
             ContainingSyntaxContext ContainingSyntaxContext,
             ContainingSyntax StubMethodSyntaxTemplate,
             MethodSignatureDiagnosticLocations DiagnosticLocation,
             JSImportData JSImportData,
-            MarshallingGeneratorFactoryKey<(TargetFramework, Version, JSGeneratorOptions)> GeneratorFactoryKey,
-            ImmutableArray<Diagnostic> Diagnostics)
-        {
-            public bool Equals(IncrementalStubGenerationContext? other)
-            {
-                return other is not null
-                    && StubEnvironment.AreCompilationSettingsEqual(Environment, other.Environment)
-                    && SignatureContext.Equals(other.SignatureContext)
-                    && ContainingSyntaxContext.Equals(other.ContainingSyntaxContext)
-                    && StubMethodSyntaxTemplate.Equals(other.StubMethodSyntaxTemplate)
-                    && JSImportData.Equals(other.JSImportData)
-                    && DiagnosticLocation.Equals(DiagnosticLocation)
-                    && GeneratorFactoryKey.Equals(other.GeneratorFactoryKey)
-                    && Diagnostics.SequenceEqual(other.Diagnostics);
-            }
-
-            public override int GetHashCode()
-            {
-                throw new UnreachableException();
-            }
-        }
+            MarshallingGeneratorFactoryKey<(TargetFramework TargetFramework, Version TargetFrameworkVersion, JSGeneratorOptions)> GeneratorFactoryKey,
+            SequenceEqualImmutableArray<Diagnostic> Diagnostics);
 
         public static class StepNames
         {
@@ -228,14 +205,13 @@ namespace Microsoft.Interop.JavaScript
 
             var methodSyntaxTemplate = new ContainingSyntax(originalSyntax.Modifiers.StripTriviaFromTokens(), SyntaxKind.MethodDeclaration, originalSyntax.Identifier, originalSyntax.TypeParameterList);
             return new IncrementalStubGenerationContext(
-                environment,
                 signatureContext,
                 containingTypeContext,
                 methodSyntaxTemplate,
                 new MethodSignatureDiagnosticLocations(originalSyntax),
                 jsImportData,
                 CreateGeneratorFactory(environment, options),
-                generatorDiagnostics.Diagnostics.ToImmutableArray());
+                new SequenceEqualImmutableArray<Diagnostic>(generatorDiagnostics.Diagnostics.ToImmutableArray()));
         }
 
         private static MarshallingGeneratorFactoryKey<(TargetFramework, Version, JSGeneratorOptions)> CreateGeneratorFactory(StubEnvironment env, JSGeneratorOptions options)
@@ -253,7 +229,8 @@ namespace Microsoft.Interop.JavaScript
 
             // Generate stub code
             var stubGenerator = new JSImportCodeGenerator(
-            incrementalContext.Environment,
+            incrementalContext.GeneratorFactoryKey.Key.TargetFramework,
+            incrementalContext.GeneratorFactoryKey.Key.TargetFrameworkVersion,
             incrementalContext.SignatureContext.SignatureContext.ElementTypeInformation,
             incrementalContext.JSImportData,
             incrementalContext.SignatureContext,
@@ -265,7 +242,7 @@ namespace Microsoft.Interop.JavaScript
 
             BlockSyntax code = stubGenerator.GenerateJSImportBody();
 
-            return (PrintGeneratedSource(incrementalContext.StubMethodSyntaxTemplate, incrementalContext.SignatureContext, incrementalContext.ContainingSyntaxContext, code), incrementalContext.Diagnostics.AddRange(diagnostics.Diagnostics));
+            return (PrintGeneratedSource(incrementalContext.StubMethodSyntaxTemplate, incrementalContext.SignatureContext, incrementalContext.ContainingSyntaxContext, code), incrementalContext.Diagnostics.Array.AddRange(diagnostics.Diagnostics));
         }
 
         private static bool ShouldVisitNode(SyntaxNode syntaxNode)
index 77855ea..e2dba4a 100644 (file)
@@ -95,7 +95,7 @@ namespace Microsoft.Interop.Analyzers
             bool mayRequireAdditionalWork = diagnostics.AnyDiagnostics;
             bool anyExplicitlyUnsupportedInfo = false;
 
-            var stubCodeContext = new ManagedToNativeStubCodeContext(env, "return", "nativeReturn");
+            var stubCodeContext = new ManagedToNativeStubCodeContext(env.TargetFramework, env.TargetFrameworkVersion, "return", "nativeReturn");
 
             var forwarder = new Forwarder();
             // We don't actually need the bound generators. We just need them to be attempted to be bound to determine if the generator will be able to bind them.
index 70ada65..88aaa11 100644 (file)
@@ -22,35 +22,14 @@ namespace Microsoft.Interop
     public sealed class LibraryImportGenerator : IIncrementalGenerator
     {
         internal sealed record IncrementalStubGenerationContext(
-            StubEnvironment Environment,
             SignatureContext SignatureContext,
             ContainingSyntaxContext ContainingSyntaxContext,
             ContainingSyntax StubMethodSyntaxTemplate,
             MethodSignatureDiagnosticLocations DiagnosticLocation,
-            ImmutableArray<AttributeSyntax> ForwardedAttributes,
+            SequenceEqualImmutableArray<AttributeSyntax> ForwardedAttributes,
             LibraryImportData LibraryImportData,
-            MarshallingGeneratorFactoryKey<(TargetFramework, Version, LibraryImportGeneratorOptions)> GeneratorFactoryKey,
-            ImmutableArray<Diagnostic> Diagnostics)
-        {
-            public bool Equals(IncrementalStubGenerationContext? other)
-            {
-                return other is not null
-                    && StubEnvironment.AreCompilationSettingsEqual(Environment, other.Environment)
-                    && SignatureContext.Equals(other.SignatureContext)
-                    && ContainingSyntaxContext.Equals(other.ContainingSyntaxContext)
-                    && StubMethodSyntaxTemplate.Equals(other.StubMethodSyntaxTemplate)
-                    && LibraryImportData.Equals(other.LibraryImportData)
-                    && DiagnosticLocation.Equals(DiagnosticLocation)
-                    && ForwardedAttributes.SequenceEqual(other.ForwardedAttributes, (IEqualityComparer<AttributeSyntax>)SyntaxEquivalentComparer.Instance)
-                    && GeneratorFactoryKey.Equals(other.GeneratorFactoryKey)
-                    && Diagnostics.SequenceEqual(other.Diagnostics);
-            }
-
-            public override int GetHashCode()
-            {
-                throw new UnreachableException();
-            }
-        }
+            MarshallingGeneratorFactoryKey<(TargetFramework TargetFramework, Version Version, LibraryImportGeneratorOptions Options)> GeneratorFactoryKey,
+            SequenceEqualImmutableArray<Diagnostic> Diagnostics);
 
         public static class StepNames
         {
@@ -318,15 +297,15 @@ namespace Microsoft.Interop
 
             List<AttributeSyntax> additionalAttributes = GenerateSyntaxForForwardedAttributes(suppressGCTransitionAttribute, unmanagedCallConvAttribute, defaultDllImportSearchPathsAttribute);
             return new IncrementalStubGenerationContext(
-                environment,
                 signatureContext,
                 containingTypeContext,
                 methodSyntaxTemplate,
                 new MethodSignatureDiagnosticLocations(originalSyntax),
-                additionalAttributes.ToImmutableArray(),
+                new SequenceEqualImmutableArray<AttributeSyntax>(additionalAttributes.ToImmutableArray(), SyntaxEquivalentComparer.Instance),
                 libraryImportData,
                 LibraryImportGeneratorHelpers.CreateGeneratorFactory(environment, options),
-                generatorDiagnostics.Diagnostics.ToImmutableArray());
+                new SequenceEqualImmutableArray<Diagnostic>(generatorDiagnostics.Diagnostics.ToImmutableArray())
+                );
         }
 
         private static (MemberDeclarationSyntax, ImmutableArray<Diagnostic>) GenerateSource(
@@ -336,12 +315,13 @@ namespace Microsoft.Interop
             var diagnostics = new GeneratorDiagnostics();
             if (options.GenerateForwarders)
             {
-                return (PrintForwarderStub(pinvokeStub.StubMethodSyntaxTemplate, explicitForwarding: true, pinvokeStub, diagnostics), pinvokeStub.Diagnostics.AddRange(diagnostics.Diagnostics));
+                return (PrintForwarderStub(pinvokeStub.StubMethodSyntaxTemplate, explicitForwarding: true, pinvokeStub, diagnostics), pinvokeStub.Diagnostics.Array.AddRange(diagnostics.Diagnostics));
             }
 
             // Generate stub code
             var stubGenerator = new PInvokeStubCodeGenerator(
-                pinvokeStub.Environment,
+                pinvokeStub.GeneratorFactoryKey.Key.TargetFramework,
+                pinvokeStub.GeneratorFactoryKey.Key.Version,
                 pinvokeStub.SignatureContext.ElementTypeInformation,
                 pinvokeStub.LibraryImportData.SetLastError && !options.GenerateForwarders,
                 (elementInfo, ex) =>
@@ -355,10 +335,10 @@ namespace Microsoft.Interop
             if (stubGenerator.StubIsBasicForwarder
                 || !stubGenerator.SupportsTargetFramework)
             {
-                return (PrintForwarderStub(pinvokeStub.StubMethodSyntaxTemplate, !stubGenerator.SupportsTargetFramework, pinvokeStub, diagnostics), pinvokeStub.Diagnostics.AddRange(diagnostics.Diagnostics));
+                return (PrintForwarderStub(pinvokeStub.StubMethodSyntaxTemplate, !stubGenerator.SupportsTargetFramework, pinvokeStub, diagnostics), pinvokeStub.Diagnostics.Array.AddRange(diagnostics.Diagnostics));
             }
 
-            ImmutableArray<AttributeSyntax> forwardedAttributes = pinvokeStub.ForwardedAttributes;
+            ImmutableArray<AttributeSyntax> forwardedAttributes = pinvokeStub.ForwardedAttributes.Array;
 
             const string innerPInvokeName = "__PInvoke";
 
@@ -379,7 +359,7 @@ namespace Microsoft.Interop
             dllImport = dllImport.WithLeadingTrivia(Comment("// Local P/Invoke"));
             code = code.AddStatements(dllImport);
 
-            return (pinvokeStub.ContainingSyntaxContext.WrapMemberInContainingSyntaxWithUnsafeModifier(PrintGeneratedSource(pinvokeStub.StubMethodSyntaxTemplate, pinvokeStub.SignatureContext, code)), pinvokeStub.Diagnostics.AddRange(diagnostics.Diagnostics));
+            return (pinvokeStub.ContainingSyntaxContext.WrapMemberInContainingSyntaxWithUnsafeModifier(PrintGeneratedSource(pinvokeStub.StubMethodSyntaxTemplate, pinvokeStub.SignatureContext, code)), pinvokeStub.Diagnostics.Array.AddRange(diagnostics.Diagnostics));
         }
 
         private static MemberDeclarationSyntax PrintForwarderStub(ContainingSyntax userDeclaredMethod, bool explicitForwarding, IncrementalStubGenerationContext stub, GeneratorDiagnostics diagnostics)
index 9077f4c..0a331d0 100644 (file)
@@ -9,7 +9,7 @@
     <!-- Disable RS2008: Enable analyzer release tracking
          Diagnostics in runtime use a different mechanism (docs/project/list-of-diagnostics.md) -->
     <NoWarn>RS2008;$(NoWarn)</NoWarn>
-    <AnalyzerLanguage>cs</AnalyzerLanguage>    
+    <AnalyzerLanguage>cs</AnalyzerLanguage>
   </PropertyGroup>
 
   <ItemGroup>
index 66b7c5f..71cb7e0 100644 (file)
@@ -4,9 +4,6 @@
 using System;
 using System.Collections.Generic;
 using System.Collections.Immutable;
-using System.Diagnostics;
-using System.Linq;
-using Microsoft.CodeAnalysis;
 using Microsoft.CodeAnalysis.CSharp;
 using Microsoft.CodeAnalysis.CSharp.Syntax;
 using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
@@ -49,7 +46,8 @@ namespace Microsoft.Interop
         private readonly ManagedToNativeStubCodeContext _context;
 
         public PInvokeStubCodeGenerator(
-            StubEnvironment environment,
+            TargetFramework targetFramework,
+            Version targetFrameworkVersion,
             ImmutableArray<TypePositionInfo> argTypes,
             bool setLastError,
             Action<TypePositionInfo, MarshallingNotSupportedException> marshallingNotSupportedCallback,
@@ -61,21 +59,21 @@ namespace Microsoft.Interop
             // supports target framework value with this value.
             if (_setLastError)
             {
-                SupportsTargetFramework = environment.TargetFramework == TargetFramework.Net
-                    && environment.TargetFrameworkVersion.Major >= 6;
+                SupportsTargetFramework = targetFramework == TargetFramework.Net
+                    && targetFrameworkVersion.Major >= 6;
             }
             else
             {
                 SupportsTargetFramework = true;
             }
 
-            _context = new ManagedToNativeStubCodeContext(environment, ReturnIdentifier, ReturnIdentifier);
+            _context = new ManagedToNativeStubCodeContext(targetFramework, targetFrameworkVersion, ReturnIdentifier, ReturnIdentifier);
             _marshallers = new BoundGenerators(argTypes, CreateGenerator);
 
             if (_marshallers.ManagedReturnMarshaller.Generator.UsesNativeIdentifier(_marshallers.ManagedReturnMarshaller.TypeInfo, _context))
             {
                 // If we need a different native return identifier, then recreate the context with the correct identifier before we generate any code.
-                _context = new ManagedToNativeStubCodeContext(environment, ReturnIdentifier, $"{ReturnIdentifier}{StubCodeContext.GeneratedNativeIdentifierSuffix}");
+                _context = new ManagedToNativeStubCodeContext(targetFramework, targetFrameworkVersion, ReturnIdentifier, $"{ReturnIdentifier}{StubCodeContext.GeneratedNativeIdentifierSuffix}");
             }
 
             bool noMarshallingNeeded = true;
@@ -84,7 +82,7 @@ namespace Microsoft.Interop
             {
                 // Check if marshalling info and generator support the current target framework.
                 SupportsTargetFramework &= generator.TypeInfo.MarshallingAttributeInfo is not MissingSupportMarshallingInfo
-                    && generator.Generator.IsSupported(environment.TargetFramework, environment.TargetFrameworkVersion);
+                    && generator.Generator.IsSupported(targetFramework, targetFrameworkVersion);
 
                 // Check if generator is either blittable or just a forwarder.
                 noMarshallingNeeded &= generator is { Generator: BlittableMarshaller, TypeInfo.IsByRef: false }
index 36b59f4..c953e24 100644 (file)
@@ -2,10 +2,7 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 
 using System;
-using System.Collections.Generic;
 using System.Diagnostics;
-using System.Text;
-
 namespace Microsoft.Interop
 {
     public sealed record ManagedToNativeStubCodeContext : StubCodeContext
@@ -22,12 +19,13 @@ namespace Microsoft.Interop
         private readonly string _nativeReturnIdentifier;
 
         public ManagedToNativeStubCodeContext(
-            StubEnvironment environment,
+            TargetFramework targetFramework,
+            Version targetFrameworkVersion,
             string returnIdentifier,
             string nativeReturnIdentifier)
         {
-            _framework = environment.TargetFramework;
-            _frameworkVersion = environment.TargetFrameworkVersion;
+            _framework = targetFramework;
+            _frameworkVersion = targetFrameworkVersion;
             _returnIdentifier = returnIdentifier;
             _nativeReturnIdentifier = nativeReturnIdentifier;
         }
diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/SequenceEqualImmutableArray.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/SequenceEqualImmutableArray.cs
new file mode 100644 (file)
index 0000000..f91d7be
--- /dev/null
@@ -0,0 +1,31 @@
+// 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.Linq;
+
+namespace Microsoft.Interop
+{
+    /// <summary>
+    /// This method provides a wrapper for an <see cref="ImmutableArray{T}" /> that overrides the equality operation to provide elementwise comparison.
+    /// The default equality operation for an <see cref="ImmutableArray{T}" /> is reference equality of the underlying array, which is too strict
+    /// for many scenarios. This wrapper type allows us to use <see cref="ImmutableArray{T}" />s in our other record types without having to write an Equals method
+    /// that we may forget to update if we add new elements to the record.
+    /// </summary>
+    public readonly record struct SequenceEqualImmutableArray<T>(ImmutableArray<T> Array, IEqualityComparer<T> Comparer)
+    {
+        public SequenceEqualImmutableArray(ImmutableArray<T> array)
+            : this(array, EqualityComparer<T>.Default)
+        {
+        }
+
+        public bool Equals(SequenceEqualImmutableArray<T> other)
+        {
+            return Array.SequenceEqual(other.Array, Comparer);
+        }
+
+        public override int GetHashCode() => throw new UnreachableException();
+    }
+}
index 268749d..769c391 100644 (file)
@@ -2,18 +2,8 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 
 using System;
-using System.Collections.Generic;
-using System.Collections.Immutable;
-using System.Linq;
-using System.Reflection;
-using System.Runtime.InteropServices;
-using System.Threading;
 
 using Microsoft.CodeAnalysis;
-using Microsoft.CodeAnalysis.CSharp;
-using Microsoft.CodeAnalysis.CSharp.Syntax;
-using Microsoft.CodeAnalysis.Diagnostics;
-using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
 
 namespace Microsoft.Interop
 {
@@ -21,21 +11,5 @@ namespace Microsoft.Interop
         Compilation Compilation,
         TargetFramework TargetFramework,
         Version TargetFrameworkVersion,
-        bool ModuleSkipLocalsInit)
-    {
-        /// <summary>
-        /// Override for determining if two StubEnvironment instances are
-        /// equal. This intentionally excludes the Compilation instance
-        /// since that represents the actual compilation and not just the settings.
-        /// </summary>
-        /// <param name="env1">The first StubEnvironment</param>
-        /// <param name="env2">The second StubEnvironment</param>
-        /// <returns>True if the settings are equal, otherwise false.</returns>
-        public static bool AreCompilationSettingsEqual(StubEnvironment env1, StubEnvironment env2)
-        {
-            return env1.TargetFramework == env2.TargetFramework
-                && env1.TargetFrameworkVersion == env2.TargetFrameworkVersion
-                && env1.ModuleSkipLocalsInit == env2.ModuleSkipLocalsInit;
-        }
-    }
+        bool ModuleSkipLocalsInit);
 }
index e561653..3188778 100644 (file)
@@ -1,9 +1,7 @@
 // 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.Text;
 using Microsoft.CodeAnalysis;
 
 namespace Microsoft.Interop
@@ -12,8 +10,6 @@ namespace Microsoft.Interop
     {
         public static readonly SyntaxEquivalentComparer Instance = new();
 
-        private SyntaxEquivalentComparer() { }
-
         public bool Equals(SyntaxNode x, SyntaxNode y)
         {
             if ((x is null) != (y is null))
index 2048641..215ee20 100644 (file)
@@ -7,6 +7,7 @@ using Microsoft.CodeAnalysis.Text;
 using System;
 using System.Collections.Generic;
 using System.Linq;
+using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using System.Text;
 using System.Threading.Tasks;
@@ -66,11 +67,10 @@ namespace LibraryImportGenerator.UnitTests
             GeneratorDriver driver2 = driver.RunGenerators(comp2);
             GeneratorRunResult runResult = driver2.GetRunResult().Results[0];
 
-            Assert.Collection(runResult.TrackedSteps[StepNames.CalculateStubInformation],
+            Assert.Collection(runResult.TrackedSteps[StepNames.GenerateSingleStub],
                 step =>
                 {
-                    // The input contains symbols and Compilation objects, so it will always be different.
-                    // However, we validate that the calculated stub information is identical.
+                    // The calculated stub information will differ since we have a new syntax tree for where to report diagnostics.
                     Assert.Collection(step.Outputs,
                         output => Assert.Equal(IncrementalStepRunReason.Unchanged, output.Reason));
                 });
@@ -210,5 +210,53 @@ namespace LibraryImportGenerator.UnitTests
                         output => Assert.Equal(IncrementalStepRunReason.Unchanged, output.Reason));
                 });
         }
+
+        [Fact]
+        public async Task GeneratorRun_WithNewCompilation_DoesNotKeepOldCompilationAlive()
+        {
+            string source = $"namespace NS{{{CodeSnippets.BasicParametersAndModifiers<int>()}}}";
+
+            SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(source, new CSharpParseOptions(LanguageVersion.Preview));
+
+            Compilation comp1 = await TestUtils.CreateCompilation(new[] { syntaxTree });
+
+            var (reference, driver) = RunTwoGeneratorOnTwoIterativeCompilationsAndReturnFirst(comp1);
+
+            GC.Collect();
+
+            Assert.False(reference.IsAlive);
+            GC.KeepAlive(driver);
+
+            [MethodImpl(MethodImplOptions.NoInlining)]
+            static (WeakReference reference, GeneratorDriver driver) RunTwoGeneratorOnTwoIterativeCompilationsAndReturnFirst(Compilation startingCompilation)
+            {
+                Compilation comp2 = startingCompilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText("struct NewType {}", new CSharpParseOptions(LanguageVersion.Preview)));
+
+                Microsoft.Interop.LibraryImportGenerator generator = new();
+                GeneratorDriver driver = TestUtils.CreateDriver(comp2, null, new[] { generator }, EnableIncrementalTrackingDriverOptions);
+
+                driver = driver.RunGenerators(comp2);
+
+                Compilation comp3 = comp2.AddSyntaxTrees(CSharpSyntaxTree.ParseText("struct NewType2 {}", new CSharpParseOptions(LanguageVersion.Preview)));
+
+                GeneratorDriver driver2 = driver.RunGenerators(comp3);
+
+                // Assert here that we did use the last result and didn't regenerate.
+                Assert.Collection(driver2.GetRunResult().Results,
+                    result =>
+                    {
+                        Assert.Collection(result.TrackedSteps[StepNames.CalculateStubInformation],
+                            step =>
+                            {
+                                Assert.Collection(step.Outputs,
+                                    output => Assert.Equal(IncrementalStepRunReason.Unchanged, output.Reason));
+                            });
+                    });
+
+                // Return a weak reference to the first edited compilation and the driver from the most recent run.
+                // The most recent run with comp3 shouldn't keep anything from comp2 alive.
+                return (new WeakReference(comp2), driver2);
+            }
+        }
     }
 }