/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/CSharp/Test/Emit/CodeGen/DestructorTests.cs
841 строка
25 KB
Julien Couvreur
Warn for multiple blank lines in source (#65451)
17 ноя 2022, 22:55
Не верифицирован
17 ноя 2022, 22:55
a8824e8
Код
Авторство
О чём код?
// 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; using System.Collections.Generic; using System.Linq; using System.Reflection.Metadata; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.CSharp.UnitTests.Emit; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Text; using Roslyn.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen { public class DestructorTests : EmitMetadataTestBase { [ConditionalFact(typeof(DesktopOnly))] public void ClassDestructor() { var text = @" using System; public class Base { ~Base() { Console.WriteLine(""~Base""); } } public class Program { public static void Main() { Base b = new Base(); b = null; GC.Collect(GC.MaxGeneration); GC.WaitForPendingFinalizers(); } } "; var validator = GetDestructorValidator("Base"); var compVerifier = CompileAndVerify(text, sourceSymbolValidator: validator, symbolValidator: validator, expectedOutput: @"~Base", expectedSignatures: new[] { Signature("Base", "Finalize", ".method family hidebysig virtual instance System.Void Finalize() cil managed") }); compVerifier.VerifyIL("Base.Finalize", @" { // Code size 20 (0x14) .maxstack 1 .try { IL_0000: ldstr ""~Base"" IL_0005: call ""void System.Console.WriteLine(string)"" IL_000a: leave.s IL_0013 } finally { IL_000c: ldarg.0 IL_000d: call ""void object.Finalize()"" IL_0012: endfinally } IL_0013: ret } "); } [ConditionalFact(typeof(DesktopOnly))] [CompilerTrait(CompilerFeature.ExpressionBody)] public void ExpressionBodiedClassDestructor() { var text = @" using System; public class Base { ~Base() => Console.WriteLine(""~Base""); } public class Program { public static void Main() { Base b = new Base(); b = null; GC.Collect(GC.MaxGeneration); GC.WaitForPendingFinalizers(); } } "; var validator = GetDestructorValidator("Base"); var compVerifier = CompileAndVerify(text, sourceSymbolValidator: validator, symbolValidator: validator, expectedOutput: @"~Base", expectedSignatures: new[] { Signature("Base", "Finalize", ".method family hidebysig virtual instance System.Void Finalize() cil managed") }); compVerifier.VerifyIL("Base.Finalize", @" { // Code size 20 (0x14) .maxstack 1 .try { IL_0000: ldstr ""~Base"" IL_0005: call ""void System.Console.WriteLine(string)"" IL_000a: leave.s IL_0013 } finally { IL_000c: ldarg.0 IL_000d: call ""void object.Finalize()"" IL_0012: endfinally } IL_0013: ret } "); } [ConditionalFact(typeof(DesktopOnly))] [CompilerTrait(CompilerFeature.ExpressionBody)] public void ExpressionBodiedSubClassDestructor() { var text = @" using System; public class Base { ~Base() => Console.WriteLine(""~Base""); } public class Derived : Base { ~Derived() => Console.WriteLine(""~Derived""); } public class Program { public static void Main() { Derived d = new Derived(); d = null; GC.Collect(GC.MaxGeneration); GC.WaitForPendingFinalizers(); } } "; var validator = GetDestructorValidator("Derived"); var compVerifier = CompileAndVerify(text, sourceSymbolValidator: validator, symbolValidator: validator, expectedOutput: @"~Derived ~Base", expectedSignatures: new[] { Signature("Base", "Finalize", ".method family hidebysig virtual instance System.Void Finalize() cil managed"), Signature("Derived", "Finalize", ".method family hidebysig virtual instance System.Void Finalize() cil managed") }); compVerifier.VerifyIL("Base.Finalize", @" { // Code size 20 (0x14) .maxstack 1 .try { IL_0000: ldstr ""~Base"" IL_0005: call ""void System.Console.WriteLine(string)"" IL_000a: leave.s IL_0013 } finally { IL_000c: ldarg.0 IL_000d: call ""void object.Finalize()"" IL_0012: endfinally } IL_0013: ret } "); compVerifier.VerifyIL("Derived.Finalize", @" { // Code size 20 (0x14) .maxstack 1 .try { IL_0000: ldstr ""~Derived"" IL_0005: call ""void System.Console.WriteLine(string)"" IL_000a: leave.s IL_0013 } finally { IL_000c: ldarg.0 IL_000d: call ""void Base.Finalize()"" IL_0012: endfinally } IL_0013: ret } "); compVerifier.VerifyDiagnostics(); } [ConditionalFact(typeof(DesktopOnly))] public void SubclassDestructor() { var text = @" using System; public class Base { ~Base() { Console.WriteLine(""~Base""); } } public class Derived : Base { ~Derived() { Console.WriteLine(""~Derived""); } } public class Program { public static void Main() { Base b = new Derived(); b = null; GC.Collect(GC.MaxGeneration); GC.WaitForPendingFinalizers(); } } "; var validator = GetDestructorValidator("Derived"); var compVerifier = CompileAndVerify(text, sourceSymbolValidator: validator, symbolValidator: validator, expectedOutput: @"~Derived ~Base", expectedSignatures: new[] { Signature("Base", "Finalize", ".method family hidebysig virtual instance System.Void Finalize() cil managed"), Signature("Derived", "Finalize", ".method family hidebysig virtual instance System.Void Finalize() cil managed") }); compVerifier.VerifyIL("Base.Finalize", @" { // Code size 20 (0x14) .maxstack 1 .try { IL_0000: ldstr ""~Base"" IL_0005: call ""void System.Console.WriteLine(string)"" IL_000a: leave.s IL_0013 } finally { IL_000c: ldarg.0 IL_000d: call ""void object.Finalize()"" IL_0012: endfinally } IL_0013: ret } "); compVerifier.VerifyIL("Derived.Finalize", @" { // Code size 20 (0x14) .maxstack 1 .try { IL_0000: ldstr ""~Derived"" IL_0005: call ""void System.Console.WriteLine(string)"" IL_000a: leave.s IL_0013 } finally { IL_000c: ldarg.0 IL_000d: call ""void Base.Finalize()"" IL_0012: endfinally } IL_0013: ret } "); compVerifier.VerifyDiagnostics(); } [ConditionalFact(typeof(WindowsDesktopOnly))] public void DestructorOverridesNonDestructor() { var text = @" using System; public class Base { protected virtual void Finalize() //NB: does not override Object.Finalize { Console.WriteLine(""~Base""); } } public class Derived : Base { ~Derived() //NB: in metadata, this will implicitly override Base.Finalize, but explicitly override Object.Finalize { Console.WriteLine(""~Derived""); } } public class Program { public static void Main() { Base b = new Base(); b = null; GC.Collect(GC.MaxGeneration); GC.WaitForPendingFinalizers(); Derived d = new Derived(); d = null; GC.Collect(GC.MaxGeneration); GC.WaitForPendingFinalizers(); } } "; var expectedOutput = @" ~Derived ~Base "; // Destructors generated by Roslyn should still be destructors when they are loaded back in - // even in cases where the user creates their own Finalize methods (which is legal, but ill-advised). // This may not be the case for metadata from other C# compilers (which will likely not have // destructors explicitly override System.Object.Finalize). var validator = GetDestructorValidator("Derived"); var compVerifier = CompileAndVerify(text, sourceSymbolValidator: validator, symbolValidator: validator, expectedOutput: expectedOutput, expectedSignatures: new[] { Signature("Base", "Finalize", ".method family hidebysig newslot virtual instance System.Void Finalize() cil managed"), Signature("Derived", "Finalize", ".method family hidebysig virtual instance System.Void Finalize() cil managed") }); compVerifier.VerifyDiagnostics( // (6,28): warning CS0465: Introducing a 'Finalize' method can interfere with destructor invocation. Did you intend to declare a destructor? Diagnostic(ErrorCode.WRN_FinalizeMethod, "Finalize")); } [WorkItem(542828, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/542828")] [ConditionalFact(typeof(WindowsDesktopOnly))] public void BaseTypeHasNonVirtualFinalize() { var text = @" using System; public class Base { protected void Finalize() //NB: does not override Object.Finalize { Console.WriteLine(""~Base""); } } public class Derived : Base { ~Derived() //NB: in metadata, this will override Base.Finalize { Console.WriteLine(""~Derived""); } } public class Program { public static void Main() { Base b = new Base(); b = null; GC.Collect(GC.MaxGeneration); GC.WaitForPendingFinalizers(); Derived d = new Derived(); d = null; GC.Collect(GC.MaxGeneration); GC.WaitForPendingFinalizers(); } } "; var validator = GetDestructorValidator("Derived"); var compVerifier = CompileAndVerify(text, sourceSymbolValidator: validator, symbolValidator: validator, expectedOutput: @"~Derived ~Base", expectedSignatures: new[] { Signature("Base", "Finalize", ".method family hidebysig instance System.Void Finalize() cil managed"), Signature("Derived", "Finalize", ".method family hidebysig virtual instance System.Void Finalize() cil managed") }); compVerifier.VerifyDiagnostics( // (6,20): warning CS0465: Introducing a 'Finalize' method can interfere with destructor invocation. Did you intend to declare a destructor? Diagnostic(ErrorCode.WRN_FinalizeMethod, "Finalize")); } [WorkItem(542828, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/542828")] [ConditionalFact(typeof(WindowsDesktopOnly))] public void GenericBaseTypeHasNonVirtualFinalize() { var text = @" using System; public class Base<T> { protected void Finalize() //NB: does not override Object.Finalize { Console.WriteLine(""~Base""); } } public class Derived : Base<int> { ~Derived() //NB: in metadata, this will override Base.Finalize { Console.WriteLine(""~Derived""); } } public class Program { public static void Main() { Base<char> b = new Base<char>(); b = null; GC.Collect(GC.MaxGeneration); GC.WaitForPendingFinalizers(); Derived d = new Derived(); d = null; GC.Collect(GC.MaxGeneration); GC.WaitForPendingFinalizers(); } } "; var validator = GetDestructorValidator("Derived"); var compVerifier = CompileAndVerify(text, sourceSymbolValidator: validator, symbolValidator: validator, expectedOutput: @"~Derived ~Base", expectedSignatures: new[] { Signature("Base`1", "Finalize", ".method family hidebysig instance System.Void Finalize() cil managed"), Signature("Derived", "Finalize", ".method family hidebysig virtual instance System.Void Finalize() cil managed") }); compVerifier.VerifyDiagnostics( // (6,20): warning CS0465: Introducing a 'Finalize' method can interfere with destructor invocation. Did you intend to declare a destructor? Diagnostic(ErrorCode.WRN_FinalizeMethod, "Finalize")); } [Fact] public void StructAndInterfaceHasNonVirtualFinalize() { var text = @" public interface I { void Finalize(); } public struct S { void Finalize() { } } class C { private string Finalize; } "; var compVerifier = CompileAndVerify(text); compVerifier.VerifyDiagnostics( // (4,10): warning CS0465: Introducing a 'Finalize' method can interfere with destructor invocation. Did you intend to declare a destructor? // void Finalize(); Diagnostic(ErrorCode.WRN_FinalizeMethod, "Finalize"), // (9,10): warning CS0465: Introducing a 'Finalize' method can interfere with destructor invocation. Did you intend to declare a destructor? // void Finalize() { } Diagnostic(ErrorCode.WRN_FinalizeMethod, "Finalize"), // (14,20): warning CS0169: The field 'C.Finalize' is never used // private string Finalize; Diagnostic(ErrorCode.WRN_UnreferencedField, "Finalize").WithArguments("C.Finalize") ); } [Fact] public void IsRuntimeFinalizer1() { var text = @" public class A { ~A() { } } public class B { public virtual void Finalize() { } } public class C : A { public void Finalize() { } } public class D : B { public override void Finalize() { } } public struct E { public void Finalize() { } } public interface F { void Finalize(); } public class G { protected virtual void Finalize() { } } public class H : G { ~H() { } } public class I { protected virtual void Finalize<T>() { } } public class J<T> { protected virtual void Finalize() { } } public class K<T> { ~K() { } } public class L<T> { ~L() { } } public class M<T> : L<T> { ~M() { } } "; Action<ModuleSymbol> validator = module => { var globalNamespace = module.GlobalNamespace; var mscorlib = module.ContainingAssembly.CorLibrary; var systemNamespace = mscorlib.GlobalNamespace.GetMember<NamespaceSymbol>("System"); Assert.True(systemNamespace.GetMember<NamedTypeSymbol>("Object").GetMember<MethodSymbol>("Finalize").IsRuntimeFinalizer()); Assert.True(globalNamespace.GetMember<NamedTypeSymbol>("A").GetMember<MethodSymbol>("Finalize").IsRuntimeFinalizer()); Assert.False(globalNamespace.GetMember<NamedTypeSymbol>("B").GetMember<MethodSymbol>("Finalize").IsRuntimeFinalizer()); Assert.False(globalNamespace.GetMember<NamedTypeSymbol>("C").GetMember<MethodSymbol>("Finalize").IsRuntimeFinalizer()); Assert.False(globalNamespace.GetMember<NamedTypeSymbol>("D").GetMember<MethodSymbol>("Finalize").IsRuntimeFinalizer()); Assert.False(globalNamespace.GetMember<NamedTypeSymbol>("E").GetMember<MethodSymbol>("Finalize").IsRuntimeFinalizer()); Assert.False(globalNamespace.GetMember<NamedTypeSymbol>("F").GetMember<MethodSymbol>("Finalize").IsRuntimeFinalizer()); Assert.False(globalNamespace.GetMember<NamedTypeSymbol>("G").GetMember<MethodSymbol>("Finalize").IsRuntimeFinalizer()); Assert.True(globalNamespace.GetMember<NamedTypeSymbol>("H").GetMember<MethodSymbol>("Finalize").IsRuntimeFinalizer()); Assert.False(globalNamespace.GetMember<NamedTypeSymbol>("I").GetMember<MethodSymbol>("Finalize").IsRuntimeFinalizer()); var intType = systemNamespace.GetMember<TypeSymbol>("Int32"); Assert.Equal(SpecialType.System_Int32, intType.SpecialType); var classJ = globalNamespace.GetMember<NamedTypeSymbol>("J"); Assert.False(classJ.GetMember<MethodSymbol>("Finalize").IsRuntimeFinalizer()); var classJInt = classJ.Construct(intType); Assert.False(classJInt.GetMember<MethodSymbol>("Finalize").IsRuntimeFinalizer()); var classK = globalNamespace.GetMember<NamedTypeSymbol>("K"); Assert.True(classK.GetMember<MethodSymbol>("Finalize").IsRuntimeFinalizer()); var classKInt = classK.Construct(intType); Assert.True(classKInt.GetMember<MethodSymbol>("Finalize").IsRuntimeFinalizer()); var classL = globalNamespace.GetMember<NamedTypeSymbol>("L"); Assert.True(classL.GetMember<MethodSymbol>("Finalize").IsRuntimeFinalizer()); var classLInt = classL.Construct(intType); Assert.True(classLInt.GetMember<MethodSymbol>("Finalize").IsRuntimeFinalizer()); var classM = globalNamespace.GetMember<NamedTypeSymbol>("M"); Assert.True(classM.GetMember<MethodSymbol>("Finalize").IsRuntimeFinalizer()); var classMInt = classM.Construct(intType); Assert.True(classMInt.GetMember<MethodSymbol>("Finalize").IsRuntimeFinalizer()); }; CompileAndVerify(text, sourceSymbolValidator: validator, symbolValidator: validator); } [Fact] public void IsRuntimeFinalizer2() { var text = @" .class public auto ansi beforefieldinit C extends [mscorlib]System.Object { .method family hidebysig virtual instance void Finalize() cil managed { ret } .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ldarg.0 call instance void [mscorlib]System.Object::.ctor() ret } } // end of class C .class public auto ansi beforefieldinit D extends [mscorlib]System.Object { .method family hidebysig newslot virtual instance void Finalize() cil managed { ret } .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ldarg.0 call instance void [mscorlib]System.Object::.ctor() ret } } // end of class D "; var compilation = CreateCompilationWithILAndMscorlib40("", text); var globalNamespace = compilation.GlobalNamespace; Assert.True(globalNamespace.GetMember<NamedTypeSymbol>("C").GetMember<MethodSymbol>("Finalize").IsRuntimeFinalizer()); //override of object.Finalize Assert.False(globalNamespace.GetMember<NamedTypeSymbol>("D").GetMember<MethodSymbol>("Finalize").IsRuntimeFinalizer()); //same but has "newslot" } [WorkItem(528903, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/528903")] // Won't fix - test just captures behavior. [Fact] public void DestructorOverridesPublicFinalize() { var text = @" public class A { public virtual void Finalize() { } } public class B : A { ~B() { } } "; var compilation = CreateCompilation(text, options: TestOptions.ReleaseDll); // NOTE: has warnings, but not errors. compilation.VerifyDiagnostics( // (4,25): warning CS0465: Introducing a 'Finalize' method can interfere with destructor invocation. Did you intend to declare a destructor? // public virtual void Finalize() { } Diagnostic(ErrorCode.WRN_FinalizeMethod, "Finalize")); // We produce unverifiable code here as per bug resolution (compat concerns, not common case). CompileAndVerify(compilation, verify: Verification.FailsPEVerify).VerifyIL("B.Finalize", @" { // Code size 10 (0xa) .maxstack 1 .try { IL_0000: leave.s IL_0009 } finally { IL_0002: ldarg.0 IL_0003: call ""void object.Finalize()"" IL_0008: endfinally } IL_0009: ret } "); } [WorkItem(528907, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/528907")] [Fact] public void BaseTypeHasGenericFinalize() { var text = @" public class A { protected void Finalize<T>() { } } public class B : A { ~B() { } } "; // NOTE: calling object.Finalize, since A.Finalize has the wrong arity. // (Dev11 called A.Finalize and failed at runtime, since it wasn't providing // a type argument.) CompileAndVerify(text).VerifyIL("B.Finalize", @" { // Code size 10 (0xa) .maxstack 1 .try { IL_0000: leave.s IL_0009 } finally { IL_0002: ldarg.0 IL_0003: call ""void object.Finalize()"" IL_0008: endfinally } IL_0009: ret } "); } [WorkItem(528903, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/528903")] [Fact] public void MethodImplEntry() { var text = @" public class A { ~A() { } } "; CompileAndVerify(text, assemblyValidator: (assembly) => { var peFileReader = assembly.GetMetadataReader(); // Find the handle and row for A. var pairA = peFileReader.TypeDefinitions.AsEnumerable(). Select(handle => new { handle = handle, row = peFileReader.GetTypeDefinition(handle) }). Single(pair => peFileReader.GetString(pair.row.Name) == "A" && string.IsNullOrEmpty(peFileReader.GetString(pair.row.Namespace))); TypeDefinitionHandle handleA = pairA.handle; TypeDefinition typeA = pairA.row; // Find the handle for A's destructor. MethodDefinitionHandle handleDestructorA = typeA.GetMethods().AsEnumerable(). Single(handle => peFileReader.GetString(peFileReader.GetMethodDefinition(handle).Name) == WellKnownMemberNames.DestructorName); // Find the handle for System.Object. TypeReferenceHandle handleObject = peFileReader.TypeReferences.AsEnumerable(). Select(handle => new { handle = handle, row = peFileReader.GetTypeReference(handle) }). Single(pair => peFileReader.GetString(pair.row.Name) == "Object" && peFileReader.GetString(pair.row.Namespace) == "System").handle; // Find the handle for System.Object's destructor. MemberReferenceHandle handleDestructorObject = peFileReader.MemberReferences.AsEnumerable(). Select(handle => new { handle = handle, row = peFileReader.GetMemberReference(handle) }). Single(pair => pair.row.Parent == (EntityHandle)handleObject && peFileReader.GetString(pair.row.Name) == WellKnownMemberNames.DestructorName).handle; // Find the MethodImpl row for A. MethodImplementation methodImpl = typeA.GetMethodImplementations().AsEnumerable(). Select(handle => peFileReader.GetMethodImplementation(handle)). Single(); // The Class column should point to A. Assert.Equal(handleA, methodImpl.Type); // The MethodDeclaration column should point to System.Object.Finalize. Assert.Equal((EntityHandle)handleDestructorObject, methodImpl.MethodDeclaration); // The MethodDeclarationColumn should point to A's destructor. Assert.Equal((EntityHandle)handleDestructorA, methodImpl.MethodBody); }); } private static Action<ModuleSymbol> GetDestructorValidator(string typeName) { return module => ValidateDestructor(module, typeName); } // NOTE: assumes there's a destructor. private static void ValidateDestructor(ModuleSymbol module, string typeName) { var @class = module.GlobalNamespace.GetMember<NamedTypeSymbol>(typeName); var destructor = @class.GetMember<MethodSymbol>(WellKnownMemberNames.DestructorName); Assert.Equal(MethodKind.Destructor, destructor.MethodKind); Assert.True(destructor.IsMetadataVirtual()); Assert.False(destructor.IsVirtual); Assert.False(destructor.IsOverride); Assert.False(destructor.IsSealed); Assert.False(destructor.IsStatic); Assert.False(destructor.IsAbstract); Assert.Null(destructor.OverriddenMethod); Assert.Equal(SpecialType.System_Void, destructor.ReturnType.SpecialType); Assert.Equal(0, destructor.Parameters.Length); Assert.Equal(0, destructor.TypeParameters.Length); Assert.Equal(Accessibility.Protected, destructor.DeclaredAccessibility); } } }