/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueTestBase.cs
288 строк
13 KB
Tomáš Matoušek
EnC: Mark deleted members with an attribute (#79764)
28 авг 2025, 22:47
Не верифицирован
28 авг 2025, 22:47
b2da93c
Код
Авторство
О чём код?
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. #nullable disable using System; using System.Collections.Generic; using System.Collections.Immutable; using System.IO; using System.Linq; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.CSharp.UnitTests; using Microsoft.CodeAnalysis.EditAndContinue.UnitTests; using Microsoft.CodeAnalysis.Emit; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.Metadata.Tools; using Roslyn.Test.Utilities; using Roslyn.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.EditAndContinue.UnitTests { using static EditAndContinueTestUtilities; public abstract class EditAndContinueTestBase : EmitMetadataTestBase { public static readonly string MetadataUpdateDeletedAttributeSource = """ namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.All, AllowMultiple = false, Inherited = false)] public sealed class MetadataUpdateDeletedAttribute : Attribute; } """; // PDB reader can only be accessed from a single thread, so avoid concurrent compilation: internal static readonly CSharpCompilationOptions ComSafeDebugDll = TestOptions.DebugDll.WithConcurrentBuild(false); internal static readonly Func<MethodDefinitionHandle, EditAndContinueMethodDebugInformation> EmptyLocalsProvider = handle => default(EditAndContinueMethodDebugInformation); internal static string Visualize(ModuleMetadata baseline, params PinnedMetadata[] deltas) { var result = new StringWriter(); new MetadataVisualizer(new[] { baseline.MetadataReader }.Concat(deltas.Select(d => d.Reader)).ToArray(), result).VisualizeAllGenerations(); return result.ToString(); } public static EmitBaseline CreateInitialBaseline(Compilation compilation, ModuleMetadata module, Func<MethodDefinitionHandle, EditAndContinueMethodDebugInformation> debugInformationProvider) => EditAndContinueTestUtilities.CreateInitialBaseline(compilation, module, debugInformationProvider); internal static SourceWithMarkedNodes MarkedSource(string markedSource, string fileName = "", CSharpParseOptions options = null, bool removeTags = false) => new SourceWithMarkedNodes( WithWindowsLineBreaks(markedSource), parser: s => Parse(s, fileName, options ?? TestOptions.Regular.WithNoRefSafetyRulesAttribute()), getSyntaxKind: s => (int)(SyntaxKind)typeof(SyntaxKind).GetField(s).GetValue(null), removeTags); internal static Func<SyntaxNode, SyntaxNode> GetSyntaxMapFromMarkers(SourceWithMarkedNodes source0, SourceWithMarkedNodes source1) => SourceWithMarkedNodes.GetSyntaxMap(source0, source1); internal static ImmutableArray<SyntaxNode> GetAllLocals(MethodSymbol method) { var sourceMethod = method as SourceMemberMethodSymbol; if (sourceMethod == null) { return ImmutableArray<SyntaxNode>.Empty; } return LocalVariableDeclaratorsCollector.GetDeclarators(sourceMethod); } internal static Func<SyntaxNode, SyntaxNode> GetSyntaxMapByKind(MethodSymbol method0, params SyntaxKind[] kinds) { return newNode => { foreach (SyntaxKind kind in kinds) { if (newNode.IsKind(kind)) { return method0.DeclaringSyntaxReferences.Single().SyntaxTree.GetRoot().DescendantNodes().Single(n => n.IsKind(kind)); } } return null; }; } internal static Func<SyntaxNode, SyntaxNode> GetEquivalentNodesMap(MethodSymbol method1, MethodSymbol method0) { var tree1 = method1.Locations[0].SourceTree; var tree0 = method0.Locations[0].SourceTree; Assert.NotEqual(tree1, tree0); var locals0 = GetAllLocals(method0); return s => { var s1 = s; Assert.Equal(s1.SyntaxTree, tree1); foreach (var s0 in locals0) { if (!SyntaxFactory.AreEquivalent(s0, s1)) { continue; } // Make sure the containing statements are the same. var p0 = GetNearestStatement(s0); var p1 = GetNearestStatement(s1); if (SyntaxFactory.AreEquivalent(p0, p1)) { return s0; } } return null; }; } internal static string GetLocalName(SyntaxNode node) { switch (node.Kind()) { case SyntaxKind.VariableDeclarator: return ((VariableDeclaratorSyntax)node).Identifier.ToString(); default: throw new NotImplementedException(); } } internal static StatementSyntax GetNearestStatement(SyntaxNode node) { while (node != null) { var statement = node as StatementSyntax; if (statement != null) { return statement; } node = node.Parent; } return null; } #nullable enable internal static SemanticEditDescription Edit( SemanticEditKind kind, Func<Compilation, ISymbol> symbolProvider, Func<Compilation, ISymbol>? newSymbolProvider = null, bool preserveLocalVariables = false, Func<SyntaxNode, RuntimeRudeEdit?>? rudeEdits = null) => new(kind, symbolProvider, newSymbolProvider, rudeEdits, preserveLocalVariables); #nullable disable internal static EditAndContinueLogEntry Row(int rowNumber, TableIndex table, EditAndContinueOperation operation) { return new EditAndContinueLogEntry(MetadataTokens.Handle(table, rowNumber), operation); } internal static EntityHandle Handle(int rowNumber, TableIndex table) { return MetadataTokens.Handle(table, rowNumber); } /// <summary> /// Checks that the EncLog contains specified rows. /// Any default values in the expected <paramref name="rows"/> are ignored to facilitate conditional code. /// </summary> internal static void CheckEncLog(MetadataReader reader, params EditAndContinueLogEntry[] rows) { AssertEx.Equal( rows.Where(r => r.Handle != default), reader.GetEditAndContinueLogEntries(), itemInspector: EncLogRowToString); } /// <summary> /// Checks that the EncLog contains specified definition rows. References are ignored as they are usually not interesting to validate. They are emitted as needed. /// Any default values in the expected <paramref name="rows"/> are ignored to facilitate conditional code. /// </summary> internal static void CheckEncLogDefinitions(MetadataReader reader, params EditAndContinueLogEntry[] rows) { AssertEx.Equal( rows.Where(r => r.Handle != default), reader.GetEditAndContinueLogEntries().Where(e => IsDefinition(e.Handle.Kind)), itemInspector: EncLogRowToString); } /// <summary> /// Checks that the EncMap contains specified handles. /// Any default values in the expected <paramref name="handles"/> are ignored to facilitate conditional code. /// </summary> internal static void CheckEncMap(MetadataReader reader, params EntityHandle[] handles) { AssertEx.Equal( handles.Where(h => h != default), reader.GetEditAndContinueMapEntries(), itemInspector: EncMapRowToString); } /// <summary> /// Checks that the EncMap contains specified definition handles. References are ignored as they are usually not interesting to validate. They are emitted as needed. /// Any default values in the expected <paramref name="handles"/> are ignored to facilitate conditional code. /// </summary> internal static void CheckEncMapDefinitions(MetadataReader reader, params EntityHandle[] handles) { AssertEx.Equal( handles.Where(h => h != default), reader.GetEditAndContinueMapEntries().Where(e => IsDefinition(e.Kind)), itemInspector: EncMapRowToString); } internal static void CheckAttributes(MetadataReader reader, params CustomAttributeRow[] rows) { AssertEx.Equal(rows, reader.GetCustomAttributeRows(), itemInspector: AttributeRowToString); } internal static void CheckNames(MetadataReader reader, IEnumerable<StringHandle> handles, params string[] expectedNames) => EditAndContinueTestUtilities.CheckNames(reader, handles, expectedNames); internal static void CheckNames(IEnumerable<MetadataReader> readers, IEnumerable<StringHandle> handles, params string[] expectedNames) => EditAndContinueTestUtilities.CheckNames(readers, handles, expectedNames); internal static void CheckNames(IReadOnlyList<MetadataReader> readers, IEnumerable<(StringHandle Namespace, StringHandle Name)> handles, params string[] expectedNames) => EditAndContinueTestUtilities.CheckNames(readers, handles, expectedNames); public static void CheckNames(IReadOnlyList<MetadataReader> readers, ImmutableArray<TypeDefinitionHandle> typeHandles, params string[] expectedNames) => EditAndContinueTestUtilities.CheckNames(readers, typeHandles, expectedNames); public static void CheckNames(IReadOnlyList<MetadataReader> readers, ImmutableArray<MethodDefinitionHandle> methodHandles, params string[] expectedNames) => EditAndContinueTestUtilities.CheckNames(readers, methodHandles, expectedNames); public static void CheckBlobValue(IList<MetadataReader> readers, BlobHandle valueHandle, byte[] expectedValue) { var aggregator = GetAggregator(readers); var genHandle = (BlobHandle)aggregator.GetGenerationHandle(valueHandle, out int blobGeneration); var attributeData = readers[blobGeneration].GetBlobBytes(genHandle); AssertEx.Equal(expectedValue, attributeData); } public static void CheckStringValue(IList<MetadataReader> readers, StringHandle valueHandle, string expectedValue) { var aggregator = GetAggregator(readers); var genHandle = (StringHandle)aggregator.GetGenerationHandle(valueHandle, out int blobGeneration); var attributeData = readers[blobGeneration].GetString(genHandle); AssertEx.Equal(expectedValue, attributeData); } public static MetadataAggregator GetAggregator(IList<MetadataReader> readers) => new MetadataAggregator(readers[0], readers.Skip(1).ToArray()); internal static void SaveImages(string outputDirectory, CompilationVerifier baseline, params CompilationDifference[] diffs) { bool IsPortablePdb(ImmutableArray<byte> image) => image[0] == 'B' && image[1] == 'S' && image[2] == 'J' && image[3] == 'B'; string baseName = baseline.Compilation.AssemblyName; string extSuffix = IsPortablePdb(baseline.EmittedAssemblyPdb) ? "x" : ""; Directory.CreateDirectory(outputDirectory); File.WriteAllBytes(Path.Combine(outputDirectory, baseName + ".dll" + extSuffix), baseline.EmittedAssemblyData.ToArray()); File.WriteAllBytes(Path.Combine(outputDirectory, baseName + ".pdb" + extSuffix), baseline.EmittedAssemblyPdb.ToArray()); for (int i = 0; i < diffs.Length; i++) { File.WriteAllBytes(Path.Combine(outputDirectory, $"{baseName}.{i + 1}.metadata{extSuffix}"), diffs[i].MetadataDelta.ToArray()); File.WriteAllBytes(Path.Combine(outputDirectory, $"{baseName}.{i + 1}.pdb{extSuffix}"), diffs[i].PdbDelta.ToArray()); } } } public static class EditAndContinueTestExtensions { internal static CSharpCompilation WithSource(this CSharpCompilation compilation, CSharpTestSource newSource) { var previousParseOptions = (CSharpParseOptions)compilation.SyntaxTrees.FirstOrDefault()?.Options; return compilation.RemoveAllSyntaxTrees().AddSyntaxTrees(newSource.GetSyntaxTrees(previousParseOptions)); } internal static CSharpCompilation WithSource(this CSharpCompilation compilation, SyntaxTree newTree) { return compilation.RemoveAllSyntaxTrees().AddSyntaxTrees(newTree); } internal static CSharpCompilation WithSource(this CSharpCompilation compilation, SyntaxTree[] newTrees) { return compilation.RemoveAllSyntaxTrees().AddSyntaxTrees(newTrees); } } }