/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/CSharp/Test/Emit/CodeGen/EventTests.cs
866 строк
26 KB
Charles Stoner
Emit [module: RefSafetyRules] attribute for all modules compiled with updated escape rules (#63916)
16 сен 2022, 21:00
Не верифицирован
16 сен 2022, 21:00
d2b0827
Код
Авторство
О чём код?
// 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.Linq; 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.Text; using Xunit; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; namespace Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen { public class EventTests : EmitMetadataTestBase { #region Metadata and IL [Fact] public void InstanceCustomEvent() { var text = @" class C { public event System.Action E { add { value(); } remove { value(); } } } "; var compVerifier = CompileAndVerify(text, symbolValidator: module => ValidateEvent(module, isFromSource: false, isStatic: false, isFieldLike: false), expectedSignatures: new[] { Signature("C", "E", ".event System.Action E"), Signature("C", "add_E", ".method public hidebysig specialname instance System.Void add_E(System.Action value) cil managed"), Signature("C", "remove_E", ".method public hidebysig specialname instance System.Void remove_E(System.Action value) cil managed"), }); var accessorBody = @" { // Code size 7 (0x7) .maxstack 1 IL_0000: ldarg.1 IL_0001: callvirt ""void System.Action.Invoke()"" IL_0006: ret }"; compVerifier.VerifyIL("C.E.add", accessorBody); compVerifier.VerifyIL("C.E.remove", accessorBody); } [Fact] public void StaticCustomEvent() { var text = @" class C { public static event System.Action E { add { value(); } remove { value(); } } } "; var compVerifier = CompileAndVerify(text, symbolValidator: module => ValidateEvent(module, isFromSource: false, isStatic: true, isFieldLike: false), expectedSignatures: new[] { Signature("C", "E", ".event System.Action E"), Signature("C", "add_E", ".method public hidebysig specialname static System.Void add_E(System.Action value) cil managed"), Signature("C", "remove_E", ".method public hidebysig specialname static System.Void remove_E(System.Action value) cil managed") }); var accessorBody = @" { // Code size 7 (0x7) .maxstack 1 IL_0000: ldarg.0 IL_0001: callvirt ""void System.Action.Invoke()"" IL_0006: ret }"; compVerifier.VerifyIL("C.E.add", accessorBody); compVerifier.VerifyIL("C.E.remove", accessorBody); } [Fact] public void InstanceFieldLikeEvent() { var text = @" class C { public event System.Action E; } "; var compVerifier = CompileAndVerify(text, symbolValidator: module => ValidateEvent(module, isFromSource: false, isStatic: false, isFieldLike: true), expectedSignatures: new[] { Signature("C", "E", ".event System.Action E"), Signature("C", "add_E", ".method [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] public hidebysig specialname instance System.Void add_E(System.Action value) cil managed"), Signature("C", "remove_E", ".method [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] public hidebysig specialname instance System.Void remove_E(System.Action value) cil managed") }); var accessorBodyFormat = @" {{ // Code size 41 (0x29) .maxstack 3 .locals init (System.Action V_0, System.Action V_1, System.Action V_2) IL_0000: ldarg.0 IL_0001: ldfld ""System.Action C.E"" IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: stloc.1 IL_0009: ldloc.1 IL_000a: ldarg.1 IL_000b: call ""System.Delegate System.Delegate.{0}(System.Delegate, System.Delegate)"" IL_0010: castclass ""System.Action"" IL_0015: stloc.2 IL_0016: ldarg.0 IL_0017: ldflda ""System.Action C.E"" IL_001c: ldloc.2 IL_001d: ldloc.1 IL_001e: call ""System.Action System.Threading.Interlocked.CompareExchange<System.Action>(ref System.Action, System.Action, System.Action)"" IL_0023: stloc.0 IL_0024: ldloc.0 IL_0025: ldloc.1 IL_0026: bne.un.s IL_0007 IL_0028: ret }}"; // NOTE: Dev10 used slightly different loop condition code // IL_0023: stloc.0 // IL_0024: ldloc.0 // IL_0025: ldloc.1 // IL_0026: ceq // IL_0028: ldc.i4.0 // IL_0029: ceq // IL_002b: stloc.3 // IL_002c: ldloc.3 // IL_002d: brtrue.s IL_0007 // IL_002f: ret compVerifier.VerifyIL("C.E.add", string.Format(accessorBodyFormat, "Combine")); compVerifier.VerifyIL("C.E.remove", string.Format(accessorBodyFormat, "Remove")); } [Fact] public void StaticFieldLikeEvent() { var text = @" class C { public static event System.Action E; } "; var compVerifier = CompileAndVerify(text, symbolValidator: module => ValidateEvent(module, isFromSource: false, isStatic: true, isFieldLike: true), expectedSignatures: new[] { Signature("C", "E", ".event System.Action E"), Signature("C", "add_E", ".method [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] public hidebysig specialname static System.Void add_E(System.Action value) cil managed"), Signature("C", "remove_E", ".method [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] public hidebysig specialname static System.Void remove_E(System.Action value) cil managed") }); var accessorBodyFormat = @" {{ // Code size 39 (0x27) .maxstack 3 .locals init (System.Action V_0, System.Action V_1, System.Action V_2) IL_0000: ldsfld ""System.Action C.E"" IL_0005: stloc.0 IL_0006: ldloc.0 IL_0007: stloc.1 IL_0008: ldloc.1 IL_0009: ldarg.0 IL_000a: call ""System.Delegate System.Delegate.{0}(System.Delegate, System.Delegate)"" IL_000f: castclass ""System.Action"" IL_0014: stloc.2 IL_0015: ldsflda ""System.Action C.E"" IL_001a: ldloc.2 IL_001b: ldloc.1 IL_001c: call ""System.Action System.Threading.Interlocked.CompareExchange<System.Action>(ref System.Action, System.Action, System.Action)"" IL_0021: stloc.0 IL_0022: ldloc.0 IL_0023: ldloc.1 IL_0024: bne.un.s IL_0006 IL_0026: ret }}"; // NOTE: Dev10 used slightly different loop condition code (as in InstanceFieldLikeEvent) compVerifier.VerifyIL("C.E.add", string.Format(accessorBodyFormat, "Combine")); compVerifier.VerifyIL("C.E.remove", string.Format(accessorBodyFormat, "Remove")); } // NOTE: assumes there's an event E in a type C. private static void ValidateEvent(ModuleSymbol module, bool isFromSource, bool isStatic, bool isFieldLike) { var @class = module.GlobalNamespace.GetMember<NamedTypeSymbol>("C"); var @event = @class.GetMember<EventSymbol>("E"); Assert.Equal(SymbolKind.Event, @event.Kind); Assert.Equal(Accessibility.Public, @event.DeclaredAccessibility); Assert.Equal(isStatic, @event.IsStatic); Assert.False(@event.MustCallMethodsDirectly); var addMethod = @event.AddMethod; Assert.Equal(MethodKind.EventAdd, addMethod.MethodKind); Assert.Equal("void C.E.add", addMethod.ToTestDisplayString()); Assert.True((addMethod.ImplementationAttributes & System.Reflection.MethodImplAttributes.Synchronized) == 0); addMethod.CheckAccessorShape(@event); var removeMethod = @event.RemoveMethod; Assert.Equal(MethodKind.EventRemove, removeMethod.MethodKind); Assert.Equal("void C.E.remove", removeMethod.ToTestDisplayString()); Assert.True((removeMethod.ImplementationAttributes & System.Reflection.MethodImplAttributes.Synchronized) == 0); removeMethod.CheckAccessorShape(@event); // Whether or not the event was field-like in source, it will look custom when loaded from metadata. if (isFieldLike && isFromSource) { Assert.True(@event.HasAssociatedField); var associatedField = @event.AssociatedField; Assert.Equal(SymbolKind.Field, associatedField.Kind); Assert.Equal(Accessibility.Private, associatedField.DeclaredAccessibility); Assert.Equal(isStatic, associatedField.IsStatic); Assert.Equal(@event.Type, associatedField.Type); } else { Assert.False(@event.HasAssociatedField); Assert.Null(@event.AssociatedField); } } [Fact] public void EventOperations() { var text = @" class C { public event System.Action E; public event System.Action F { add { } remove { } } void M(ref System.Action a) { E = a; a = E; M(ref E); E += a; E -= a; F += a; F -= a; F += E; F -= E; } } "; CompileAndVerify(text).VerifyIL("C.M", @" { // Code size 85 (0x55) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: ldind.ref IL_0003: stfld ""System.Action C.E"" IL_0008: ldarg.1 IL_0009: ldarg.0 IL_000a: ldfld ""System.Action C.E"" IL_000f: stind.ref IL_0010: ldarg.0 IL_0011: ldarg.0 IL_0012: ldflda ""System.Action C.E"" IL_0017: call ""void C.M(ref System.Action)"" IL_001c: ldarg.0 IL_001d: ldarg.1 IL_001e: ldind.ref IL_001f: call ""void C.E.add"" IL_0024: ldarg.0 IL_0025: ldarg.1 IL_0026: ldind.ref IL_0027: call ""void C.E.remove"" IL_002c: ldarg.0 IL_002d: ldarg.1 IL_002e: ldind.ref IL_002f: call ""void C.F.add"" IL_0034: ldarg.0 IL_0035: ldarg.1 IL_0036: ldind.ref IL_0037: call ""void C.F.remove"" IL_003c: ldarg.0 IL_003d: ldarg.0 IL_003e: ldfld ""System.Action C.E"" IL_0043: call ""void C.F.add"" IL_0048: ldarg.0 IL_0049: ldarg.0 IL_004a: ldfld ""System.Action C.E"" IL_004f: call ""void C.F.remove"" IL_0054: ret }"); } [Fact] public void StaticEventOperations() { var text = @" class C { public static event System.Action E; public static event System.Action F { add { } remove { } } void M(ref System.Action a) { E = a; a = E; M(ref E); E += a; E -= a; F += a; F -= a; F += E; F -= E; } } "; CompileAndVerify(text).VerifyIL("C.M", @" { // Code size 74 (0x4a) .maxstack 2 IL_0000: ldarg.1 IL_0001: ldind.ref IL_0002: stsfld ""System.Action C.E"" IL_0007: ldarg.1 IL_0008: ldsfld ""System.Action C.E"" IL_000d: stind.ref IL_000e: ldarg.0 IL_000f: ldsflda ""System.Action C.E"" IL_0014: call ""void C.M(ref System.Action)"" IL_0019: ldarg.1 IL_001a: ldind.ref IL_001b: call ""void C.E.add"" IL_0020: ldarg.1 IL_0021: ldind.ref IL_0022: call ""void C.E.remove"" IL_0027: ldarg.1 IL_0028: ldind.ref IL_0029: call ""void C.F.add"" IL_002e: ldarg.1 IL_002f: ldind.ref IL_0030: call ""void C.F.remove"" IL_0035: ldsfld ""System.Action C.E"" IL_003a: call ""void C.F.add"" IL_003f: ldsfld ""System.Action C.E"" IL_0044: call ""void C.F.remove"" IL_0049: ret }"); } [Fact] public void EventAccess() { var text = @" class C { public event System.Action E; public event System.Action F { add { } remove { } } public static event System.Action G; public static event System.Action H { add { } remove { } } } class D { void M(C c, System.Action a) { c.E += a; c.E -= a; c.F += a; c.F -= a; C.G += a; C.G -= a; C.H += a; C.H -= a; } } "; CompileAndVerify(text).VerifyIL("D.M", @" { // Code size 53 (0x35) .maxstack 2 IL_0000: ldarg.1 IL_0001: ldarg.2 IL_0002: callvirt ""void C.E.add"" IL_0007: ldarg.1 IL_0008: ldarg.2 IL_0009: callvirt ""void C.E.remove"" IL_000e: ldarg.1 IL_000f: ldarg.2 IL_0010: callvirt ""void C.F.add"" IL_0015: ldarg.1 IL_0016: ldarg.2 IL_0017: callvirt ""void C.F.remove"" IL_001c: ldarg.2 IL_001d: call ""void C.G.add"" IL_0022: ldarg.2 IL_0023: call ""void C.G.remove"" IL_0028: ldarg.2 IL_0029: call ""void C.H.add"" IL_002e: ldarg.2 IL_002f: call ""void C.H.remove"" IL_0034: ret }"); } // Regresses IsMetadataVirtual issue (no associated bug). [Fact] public void InterfaceEvent() { var text = @" interface C { event System.Action E; } "; var compVerifier = CompileAndVerify(text, symbolValidator: module => ValidateEvent(module, isFromSource: false, isStatic: false, isFieldLike: true), expectedSignatures: new[] { Signature("C", "E", ".event System.Action E"), Signature("C", "add_E", ".method [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] public hidebysig newslot specialname abstract virtual instance System.Void add_E(System.Action value) cil managed"), Signature("C", "remove_E", ".method [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] public hidebysig newslot specialname abstract virtual instance System.Void remove_E(System.Action value) cil managed") }); } #endregion #region Execution [Fact] public void CustomEventExecution() { var text = @" using System; class C { Func<string> e; event Func<string> E { add { Console.Write(""Adding ""); value(); Console.WriteLine(); e += value; } remove { Console.Write(""Removing ""); value(); Console.WriteLine(); e -= value; } } void Fire() { if (e != null) { Console.Write(""Invoking ""); e(); Console.WriteLine(); } else { Console.WriteLine(""No handlers""); } } static void Main() { C c = new C(); Func<string> handler1 = () => { Console.Write(""Handler1 ""); return ""Handler1""; }; Func<string> handler2 = () => { Console.Write(""Handler2 ""); return ""Handler2""; }; c.Fire(); c.E += handler1; c.Fire(); c.E += handler2; c.Fire(); c.E -= handler1; c.Fire(); c.E -= handler2; c.Fire(); } }"; CompileAndVerify(text, expectedOutput: @" No handlers Adding Handler1 Invoking Handler1 Adding Handler2 Invoking Handler1 Handler2 Removing Handler1 Invoking Handler2 Removing Handler2 No handlers "); } [Fact] public void FieldLikeEventExecution() { var text = @" using System; class C { event Func<string> E; void Fire() { if (E != null) { Console.Write(""Invoking ""); E(); Console.WriteLine(); } else { Console.WriteLine(""No handlers""); } } static void Main() { C c = new C(); Func<string> handler1 = () => { Console.Write(""Handler1 ""); return ""Handler1""; }; Func<string> handler2 = () => { Console.Write(""Handler2 ""); return ""Handler2""; }; c.Fire(); c.E += handler1; c.Fire(); c.E += handler2; c.Fire(); c.E -= handler1; c.Fire(); c.E -= handler2; c.Fire(); } }"; CompileAndVerify(text, expectedOutput: @" No handlers Invoking Handler1 Invoking Handler1 Handler2 Invoking Handler2 No handlers "); } [Fact] public void VirtualRaiseAccessor() { var csharpSource = @" using System; static class Program { static void Main() { C c = new C(); c.Fire(); //Prints ""VirtualEventWithRaise Raise"" since raise_e isn't overridden D d = new D(); d.Fire(); //Prints ""D raise"" since raise_e is overridden (regardless of event) } } class C : VirtualEventWithRaise { public override event Action e = () => Console.WriteLine(""C Handler""); } class D : C { public override void raise_e(object sender, object e) { Console.WriteLine(""D Raise""); } } "; var ilAssemblyReference = TestReferences.SymbolsTests.Events; var compilation = CreateCompilation(csharpSource, new MetadataReference[] { ilAssemblyReference }, TestOptions.ReleaseExe); CompileAndVerify(compilation, expectedOutput: @" VirtualEventWithRaise Raise D Raise "); } #endregion Execution [Fact] public void MissingCompareExchange_01() { var source1 = @"namespace System { public class Object { } public struct Void { } public class ValueType { } public struct Boolean { } public abstract class Delegate { } public abstract class MulticastDelegate : Delegate { } public struct IntPtr { private IntPtr m_value; IntPtr Use(IntPtr b) { m_value = b; return m_value; } } } "; var parseOptions = TestOptions.Regular.WithNoRefSafetyRulesAttribute(); var compilation1 = CreateEmptyCompilation(source1, assemblyName: GetUniqueName(), parseOptions: parseOptions); var reference1 = MetadataReference.CreateFromStream(compilation1.EmitToStream()); var source2 = @" public delegate void E1(); class C { public event E1 e; public static void Main() { var v = new C(); v.e += Main; } } "; var compilation2 = CreateEmptyCompilation(source2, new[] { reference1 }, parseOptions: parseOptions); compilation2.VerifyDiagnostics( // (7,21): warning CS0067: The event 'C.e' is never used // public event E1 e; Diagnostic(ErrorCode.WRN_UnreferencedEvent, "e").WithArguments("C.e") ); compilation2.Emit(new System.IO.MemoryStream()).Diagnostics.Verify( // (7,21): error CS0656: Missing compiler required member 'System.Delegate.Combine' // public event E1 e; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "e").WithArguments("System.Delegate", "Combine").WithLocation(7, 21), // (7,21): error CS0656: Missing compiler required member 'System.Delegate.Remove' // public event E1 e; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "e").WithArguments("System.Delegate", "Remove").WithLocation(7, 21), // (7,21): warning CS0067: The event 'C.e' is never used // public event E1 e; Diagnostic(ErrorCode.WRN_UnreferencedEvent, "e").WithArguments("C.e").WithLocation(7, 21) ); } [Fact, WorkItem(1027568, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1027568"), WorkItem(528573, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/528573")] public void MissingCompareExchange_02() { var source = @" public delegate void E1(); class C { public event E1 e; public static void Main() { var v = new C(); System.Console.Write(v.e == null); v.e += Main; System.Console.Write(v.e == null); v.e -= Main; System.Console.Write(v.e == null); } } "; var compilation = CreateCompilation(source, options: TestOptions.DebugExe); compilation.MakeMemberMissing(WellKnownMember.System_Threading_Interlocked__CompareExchange_T); var verifier = CompileAndVerify(compilation, expectedOutput: "TrueFalseTrue", symbolValidator: module => { var @class = module.GlobalNamespace.GetMember<NamedTypeSymbol>("C"); var @event = @class.GetMember<EventSymbol>("e"); var addMethod = @event.AddMethod; Assert.False((addMethod.ImplementationAttributes & System.Reflection.MethodImplAttributes.Synchronized) == 0); var removeMethod = @event.RemoveMethod; Assert.False((removeMethod.ImplementationAttributes & System.Reflection.MethodImplAttributes.Synchronized) == 0); }).VerifyDiagnostics(); verifier.VerifyIL("C.e.add", @" { // Code size 24 (0x18) .maxstack 3 IL_0000: ldarg.0 IL_0001: ldarg.0 IL_0002: ldfld ""E1 C.e"" IL_0007: ldarg.1 IL_0008: call ""System.Delegate System.Delegate.Combine(System.Delegate, System.Delegate)"" IL_000d: castclass ""E1"" IL_0012: stfld ""E1 C.e"" IL_0017: ret } "); verifier.VerifyIL("C.e.remove", @" { // Code size 24 (0x18) .maxstack 3 IL_0000: ldarg.0 IL_0001: ldarg.0 IL_0002: ldfld ""E1 C.e"" IL_0007: ldarg.1 IL_0008: call ""System.Delegate System.Delegate.Remove(System.Delegate, System.Delegate)"" IL_000d: castclass ""E1"" IL_0012: stfld ""E1 C.e"" IL_0017: ret } "); } [Fact, WorkItem(1027568, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1027568"), WorkItem(528573, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/528573")] public void MissingCompareExchange_03() { var source = @" public delegate void E1(); struct C { public event E1 e; public static void Main() { var v = new C(); System.Console.Write(v.e == null); v.e += Main; System.Console.Write(v.e == null); v.e -= Main; System.Console.Write(v.e == null); } } "; var compilation = CreateCompilation(source, options: TestOptions.DebugExe); compilation.MakeMemberMissing(WellKnownMember.System_Threading_Interlocked__CompareExchange_T); var verifier = CompileAndVerify(compilation, expectedOutput: "TrueFalseTrue", symbolValidator: module => { var @class = module.GlobalNamespace.GetMember<NamedTypeSymbol>("C"); var @event = @class.GetMember<EventSymbol>("e"); var addMethod = @event.AddMethod; Assert.True((addMethod.ImplementationAttributes & System.Reflection.MethodImplAttributes.Synchronized) == 0); var removeMethod = @event.RemoveMethod; Assert.True((removeMethod.ImplementationAttributes & System.Reflection.MethodImplAttributes.Synchronized) == 0); }).VerifyDiagnostics(); verifier.VerifyIL("C.e.add", @" { // Code size 24 (0x18) .maxstack 3 IL_0000: ldarg.0 IL_0001: ldarg.0 IL_0002: ldfld ""E1 C.e"" IL_0007: ldarg.1 IL_0008: call ""System.Delegate System.Delegate.Combine(System.Delegate, System.Delegate)"" IL_000d: castclass ""E1"" IL_0012: stfld ""E1 C.e"" IL_0017: ret } "); verifier.VerifyIL("C.e.remove", @" { // Code size 24 (0x18) .maxstack 3 IL_0000: ldarg.0 IL_0001: ldarg.0 IL_0002: ldfld ""E1 C.e"" IL_0007: ldarg.1 IL_0008: call ""System.Delegate System.Delegate.Remove(System.Delegate, System.Delegate)"" IL_000d: castclass ""E1"" IL_0012: stfld ""E1 C.e"" IL_0017: ret } "); } [Fact, WorkItem(14438, "https://github.com/dotnet/roslyn/issues/14438")] [CompilerTrait(CompilerFeature.ExpressionBody)] public void ExpressionBodedEvent() { var source = @" class C { public int x; public event System.Action E { add => x = 1; remove => x = 0; } }"; var compilation = CreateCompilation(source, options: TestOptions.DebugDll); var verifier = CompileAndVerify(compilation); verifier.VerifyIL("C.E.add", @" { // Code size 8 (0x8) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldc.i4.1 IL_0002: stfld ""int C.x"" IL_0007: ret } "); verifier.VerifyIL("C.E.remove", @" { // Code size 8 (0x8) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldc.i4.0 IL_0002: stfld ""int C.x"" IL_0007: ret } "); } } }