|
// 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 Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using System;
using System.Linq;
using Xunit;
namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Symbols.Metadata.PE
{
public class LoadingEvents : CSharpTestBase
{
[Fact]
public void LoadNonGenericEvents()
{
var assemblies = MetadataTestHelpers.GetSymbolsForReferences(
new[]
{
NetFramework.mscorlib,
TestReferences.SymbolsTests.Events,
});
var globalNamespace = assemblies.ElementAt(1).GlobalNamespace;
var @class = globalNamespace.GetMember<NamedTypeSymbol>("NonGeneric");
CheckInstanceAndStaticEvents(@class, "System.Action");
}
[Fact]
public void LoadGenericEvents()
{
var assemblies = MetadataTestHelpers.GetSymbolsForReferences(
new[]
{
NetFramework.mscorlib,
TestReferences.SymbolsTests.Events,
});
var globalNamespace = assemblies.ElementAt(1).GlobalNamespace;
var @class = globalNamespace.GetMember<NamedTypeSymbol>("Generic");
CheckInstanceAndStaticEvents(@class, "System.Action<T>");
}
[Fact]
public void LoadClosedGenericEvents()
{
var assemblies = MetadataTestHelpers.GetSymbolsForReferences(
new[]
{
NetFramework.mscorlib,
TestReferences.SymbolsTests.Events,
});
var globalNamespace = assemblies.ElementAt(1).GlobalNamespace;
var @class = globalNamespace.GetMember<NamedTypeSymbol>("ClosedGeneric");
CheckInstanceAndStaticEvents(@class, "System.Action<System.Int32>");
}
private static void CheckInstanceAndStaticEvents(NamedTypeSymbol @class, string eventTypeDisplayString)
{
var instanceEvent = @class.GetMember<EventSymbol>("InstanceEvent");
Assert.Equal(SymbolKind.Event, instanceEvent.Kind);
Assert.False(instanceEvent.IsStatic);
Assert.Equal(eventTypeDisplayString, instanceEvent.Type.ToTestDisplayString());
CheckAccessorShape(instanceEvent.AddMethod, instanceEvent);
CheckAccessorShape(instanceEvent.RemoveMethod, instanceEvent);
var staticEvent = @class.GetMember<EventSymbol>("StaticEvent");
Assert.Equal(SymbolKind.Event, staticEvent.Kind);
Assert.True(staticEvent.IsStatic);
Assert.Equal(eventTypeDisplayString, staticEvent.Type.ToTestDisplayString());
CheckAccessorShape(staticEvent.AddMethod, staticEvent);
CheckAccessorShape(staticEvent.RemoveMethod, staticEvent);
}
private static void CheckAccessorShape(MethodSymbol accessor, EventSymbol @event)
{
Assert.Same(@event, accessor.AssociatedSymbol);
switch (accessor.MethodKind)
{
case MethodKind.EventAdd:
Assert.Same(@event.AddMethod, accessor);
break;
case MethodKind.EventRemove:
Assert.Same(@event.RemoveMethod, accessor);
break;
default:
Assert.False(true, string.Format("Accessor {0} has unexpected MethodKind {1}", accessor, accessor.MethodKind));
break;
}
Assert.Equal(@event.IsAbstract, accessor.IsAbstract);
Assert.Equal(@event.IsOverride, @accessor.IsOverride);
Assert.Equal(@event.IsVirtual, @accessor.IsVirtual);
Assert.Equal(@event.IsSealed, @accessor.IsSealed);
Assert.Equal(@event.IsExtern, @accessor.IsExtern);
Assert.Equal(SpecialType.System_Void, accessor.ReturnType.SpecialType);
Assert.Equal(@event.Type, accessor.Parameters.Single().Type);
}
[Fact]
public void LoadSignatureMismatchEvents()
{
var assemblies = MetadataTestHelpers.GetSymbolsForReferences(
new[]
{
NetFramework.mscorlib,
TestReferences.SymbolsTests.Events,
});
var globalNamespace = assemblies.ElementAt(1).GlobalNamespace;
var @class = globalNamespace.GetMember<NamedTypeSymbol>("SignatureMismatch");
var mismatchedAddEvent = @class.GetMember<EventSymbol>("AddMismatch");
var mismatchedRemoveEvent = @class.GetMember<EventSymbol>("RemoveMismatch");
Assert.NotEqual(mismatchedAddEvent.Type, mismatchedAddEvent.AddMethod.Parameters.Single().Type);
Assert.True(mismatchedAddEvent.MustCallMethodsDirectly);
Assert.NotEqual(mismatchedRemoveEvent.Type, mismatchedRemoveEvent.RemoveMethod.Parameters.Single().Type);
Assert.True(mismatchedRemoveEvent.MustCallMethodsDirectly);
}
[Fact]
public void LoadMissingParameterEvents()
{
var assemblies = MetadataTestHelpers.GetSymbolsForReferences(
new[]
{
NetFramework.mscorlib,
TestReferences.SymbolsTests.Events,
});
var globalNamespace = assemblies.ElementAt(1).GlobalNamespace;
var @class = globalNamespace.GetMember<NamedTypeSymbol>("AccessorMissingParameter");
var noParamAddEvent = @class.GetMember<EventSymbol>("AddNoParam");
var noParamRemoveEvent = @class.GetMember<EventSymbol>("RemoveNoParam");
Assert.Equal(0, noParamAddEvent.AddMethod.Parameters.Length);
Assert.True(noParamAddEvent.MustCallMethodsDirectly);
Assert.Equal(0, noParamRemoveEvent.RemoveMethod.Parameters.Length);
Assert.True(noParamRemoveEvent.MustCallMethodsDirectly);
}
[Fact]
public void LoadNonDelegateEvent()
{
var assemblies = MetadataTestHelpers.GetSymbolsForReferences(
new[]
{
NetFramework.mscorlib,
TestReferences.SymbolsTests.Events,
});
var globalNamespace = assemblies.ElementAt(1).GlobalNamespace;
var @class = globalNamespace.GetMember<NamedTypeSymbol>("NonDelegateEvent");
var nonDelegateEvent = @class.GetMember<EventSymbol>("NonDelegate");
Assert.Equal(SpecialType.System_Int32, nonDelegateEvent.Type.SpecialType);
}
[Fact]
public void TestExplicitImplementationSimple()
{
var assemblies = MetadataTestHelpers.GetSymbolsForReferences(
new[]
{
NetFramework.mscorlib,
TestReferences.SymbolsTests.ExplicitInterfaceImplementation.Events.CSharp,
});
var globalNamespace = assemblies.ElementAt(1).GlobalNamespace;
var @interface = (NamedTypeSymbol)globalNamespace.GetTypeMembers("Interface").Single();
Assert.Equal(TypeKind.Interface, @interface.TypeKind);
var interfaceEvent = (EventSymbol)@interface.GetMembers("Event").Single();
var @class = (NamedTypeSymbol)globalNamespace.GetTypeMembers("Class").Single();
Assert.Equal(TypeKind.Class, @class.TypeKind);
Assert.True(@class.Interfaces().Contains(@interface));
var classEvent = (EventSymbol)@class.GetMembers("Interface.Event").Single();
var explicitImpl = classEvent.ExplicitInterfaceImplementations.Single();
Assert.Equal(interfaceEvent, explicitImpl);
}
[Fact]
public void TestExplicitImplementationGeneric()
{
var assemblies = MetadataTestHelpers.GetSymbolsForReferences(
new[]
{
NetFramework.mscorlib,
TestReferences.SymbolsTests.ExplicitInterfaceImplementation.Events.CSharp,
});
var globalNamespace = assemblies.ElementAt(1).GlobalNamespace;
var @interface = (NamedTypeSymbol)globalNamespace.GetTypeMembers("IGeneric").Single();
Assert.Equal(TypeKind.Interface, @interface.TypeKind);
var interfaceEvent = (EventSymbol)@interface.GetMembers("Event").Single();
var @class = (NamedTypeSymbol)globalNamespace.GetTypeMembers("Generic").Single();
Assert.Equal(TypeKind.Class, @class.TypeKind);
var substitutedInterface = @class.Interfaces().Single();
Assert.Equal(@interface, substitutedInterface.ConstructedFrom);
var substitutedInterfaceEvent = (EventSymbol)substitutedInterface.GetMembers("Event").Single();
Assert.Equal(interfaceEvent, substitutedInterfaceEvent.OriginalDefinition);
var classEvent = (EventSymbol)@class.GetMembers("IGeneric<S>.Event").Single();
var explicitImpl = classEvent.ExplicitInterfaceImplementations.Single();
Assert.Equal(substitutedInterfaceEvent, explicitImpl);
}
[Fact]
public void TestExplicitImplementationConstructed()
{
var assemblies = MetadataTestHelpers.GetSymbolsForReferences(
new[]
{
NetFramework.mscorlib,
TestReferences.SymbolsTests.ExplicitInterfaceImplementation.Events.CSharp,
});
var globalNamespace = assemblies.ElementAt(1).GlobalNamespace;
var @interface = (NamedTypeSymbol)globalNamespace.GetTypeMembers("IGeneric").Single();
Assert.Equal(TypeKind.Interface, @interface.TypeKind);
var interfaceEvent = (EventSymbol)@interface.GetMembers("Event").Single();
var @class = (NamedTypeSymbol)globalNamespace.GetTypeMembers("Constructed").Single();
Assert.Equal(TypeKind.Class, @class.TypeKind);
var substitutedInterface = @class.Interfaces().Single();
Assert.Equal(@interface, substitutedInterface.ConstructedFrom);
var substitutedInterfaceEvent = (EventSymbol)substitutedInterface.GetMembers("Event").Single();
Assert.Equal(interfaceEvent, substitutedInterfaceEvent.OriginalDefinition);
var classEvent = (EventSymbol)@class.GetMembers("IGeneric<System.Int32>.Event").Single();
var explicitImpl = classEvent.ExplicitInterfaceImplementations.Single();
Assert.Equal(substitutedInterfaceEvent, explicitImpl);
}
/// <summary>
/// A type def explicitly implements an interface, also a type def, but only
/// indirectly, via a type ref.
/// </summary>
[Fact]
public void TestExplicitImplementationDefRefDef()
{
var assemblies = MetadataTestHelpers.GetSymbolsForReferences(
new[]
{
NetFramework.mscorlib,
TestReferences.SymbolsTests.ExplicitInterfaceImplementation.Events.CSharp,
});
var globalNamespace = assemblies.ElementAt(1).GlobalNamespace;
var defInterface = (NamedTypeSymbol)globalNamespace.GetTypeMembers("Interface").Single();
Assert.Equal(TypeKind.Interface, defInterface.TypeKind);
var defInterfaceEvent = (EventSymbol)defInterface.GetMembers("Event").Single();
var refInterface = (NamedTypeSymbol)globalNamespace.GetTypeMembers("IGenericInterface").Single();
Assert.Equal(TypeKind.Interface, defInterface.TypeKind);
Assert.True(refInterface.Interfaces().Contains(defInterface));
var @class = (NamedTypeSymbol)globalNamespace.GetTypeMembers("IndirectImplementation").Single();
Assert.Equal(TypeKind.Class, @class.TypeKind);
var classInterfacesConstructedFrom = @class.Interfaces().Select(i => i.ConstructedFrom);
Assert.Equal(2, classInterfacesConstructedFrom.Count());
Assert.Contains(defInterface, classInterfacesConstructedFrom);
Assert.Contains(refInterface, classInterfacesConstructedFrom);
var classEvent = (EventSymbol)@class.GetMembers("Interface.Event").Single();
var explicitImpl = classEvent.ExplicitInterfaceImplementations.Single();
Assert.Equal(defInterfaceEvent, explicitImpl);
}
/// <summary>
/// In metadata, nested types implicitly share all type parameters of their containing types.
/// This results in some extra computations when mapping a type parameter position to a type
/// parameter symbol.
/// </summary>
[Fact]
public void TestTypeParameterPositions()
{
var assemblies = MetadataTestHelpers.GetSymbolsForReferences(
new[]
{
NetFramework.mscorlib,
TestReferences.SymbolsTests.ExplicitInterfaceImplementation.Events.CSharp,
});
var globalNamespace = assemblies.ElementAt(1).GlobalNamespace;
var outerInterface = (NamedTypeSymbol)globalNamespace.GetTypeMembers("IGeneric2").Single();
Assert.Equal(1, outerInterface.Arity);
Assert.Equal(TypeKind.Interface, outerInterface.TypeKind);
var outerInterfaceEvent = outerInterface.GetMembers().Single(m => m.Kind == SymbolKind.Event);
var outerClass = (NamedTypeSymbol)globalNamespace.GetTypeMembers("Outer").Single();
Assert.Equal(1, outerClass.Arity);
Assert.Equal(TypeKind.Class, outerClass.TypeKind);
var innerInterface = (NamedTypeSymbol)outerClass.GetTypeMembers("IInner").Single();
Assert.Equal(1, innerInterface.Arity);
Assert.Equal(TypeKind.Interface, innerInterface.TypeKind);
var innerInterfaceEvent = innerInterface.GetMembers().Single(m => m.Kind == SymbolKind.Event);
var innerClass1 = (NamedTypeSymbol)outerClass.GetTypeMembers("Inner1").Single();
CheckInnerClassHelper(innerClass1, "IGeneric2<A>.Event", outerInterfaceEvent);
var innerClass2 = (NamedTypeSymbol)outerClass.GetTypeMembers("Inner2").Single();
CheckInnerClassHelper(innerClass2, "IGeneric2<T>.Event", outerInterfaceEvent);
var innerClass3 = (NamedTypeSymbol)outerClass.GetTypeMembers("Inner3").Single();
CheckInnerClassHelper(innerClass3, "Outer<T>.IInner<C>.Event", innerInterfaceEvent);
var innerClass4 = (NamedTypeSymbol)outerClass.GetTypeMembers("Inner4").Single();
CheckInnerClassHelper(innerClass4, "Outer<T>.IInner<T>.Event", innerInterfaceEvent);
}
private static void CheckInnerClassHelper(NamedTypeSymbol innerClass, string methodName, Symbol interfaceEvent)
{
var @interface = interfaceEvent.ContainingType;
Assert.Equal(1, innerClass.Arity);
Assert.Equal(TypeKind.Class, innerClass.TypeKind);
Assert.Equal(@interface, innerClass.Interfaces().Single().ConstructedFrom);
var innerClassEvent = (EventSymbol)innerClass.GetMembers(methodName).Single();
var innerClassImplementingEvent = innerClassEvent.ExplicitInterfaceImplementations.Single();
Assert.Equal(interfaceEvent, innerClassImplementingEvent.OriginalDefinition);
Assert.Equal(@interface, innerClassImplementingEvent.ContainingType.ConstructedFrom);
}
// NOTE: results differ from corresponding property test.
[WorkItem(543263, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543263")]
[Fact]
public void TestMixedAccessorModifiers()
{
var assembly = MetadataTestHelpers.GetSymbolForReference(TestReferences.SymbolsTests.Events);
var globalNamespace = assembly.GlobalNamespace;
var type = globalNamespace.GetMember<NamedTypeSymbol>("AccessorModifierMismatch");
const VirtualnessModifiers @none = VirtualnessModifiers.None;
const VirtualnessModifiers @abstract = VirtualnessModifiers.Abstract;
const VirtualnessModifiers @virtual = VirtualnessModifiers.Virtual;
const VirtualnessModifiers @override = VirtualnessModifiers.Override;
const VirtualnessModifiers @sealed = VirtualnessModifiers.Sealed;
VirtualnessModifiers[] modList = new[]
{
@none,
@abstract,
@virtual,
@override,
@sealed,
};
int length = 1 + modList.Cast<int>().Max();
VirtualnessModifiers[,] expected = new VirtualnessModifiers[length, length];
expected[(int)@none, (int)@none] = @none;
expected[(int)@none, (int)@abstract] = @abstract;
expected[(int)@none, (int)@virtual] = @virtual;
expected[(int)@none, (int)@override] = @override;
expected[(int)@none, (int)@sealed] = @sealed;
expected[(int)@abstract, (int)@none] = @abstract;
expected[(int)@abstract, (int)@abstract] = @abstract;
expected[(int)@abstract, (int)@virtual] = @abstract;
expected[(int)@abstract, (int)@override] = @abstract | @override;
expected[(int)@abstract, (int)@sealed] = @abstract | @sealed;
expected[(int)@virtual, (int)@none] = @virtual;
expected[(int)@virtual, (int)@abstract] = @abstract;
expected[(int)@virtual, (int)@virtual] = @virtual;
expected[(int)@virtual, (int)@override] = @override;
expected[(int)@virtual, (int)@sealed] = @sealed;
expected[(int)@override, (int)@none] = @override;
expected[(int)@override, (int)@abstract] = @override | @abstract;
expected[(int)@override, (int)@virtual] = @override;
expected[(int)@override, (int)@override] = @override;
expected[(int)@override, (int)@sealed] = @sealed;
expected[(int)@sealed, (int)@none] = @sealed;
expected[(int)@sealed, (int)@abstract] = @abstract | @sealed;
expected[(int)@sealed, (int)@virtual] = @sealed;
expected[(int)@sealed, (int)@override] = @sealed;
expected[(int)@sealed, (int)@sealed] = @sealed;
// Table should be symmetrical.
for (int i = 0; i < length; i++)
{
for (int j = 0; j < length; j++)
{
Assert.Equal(expected[i, j], expected[j, i]);
}
}
foreach (var mod1 in modList)
{
foreach (var mod2 in modList)
{
var @event = type.GetMember<EventSymbol>(mod1.ToString() + mod2.ToString());
var addMethod = @event.AddMethod;
var removeMethod = @event.RemoveMethod;
Assert.Equal(mod1, GetVirtualnessModifiers(addMethod));
Assert.Equal(mod2, GetVirtualnessModifiers(removeMethod));
Assert.Equal(expected[(int)mod1, (int)mod2], GetVirtualnessModifiers(@event));
}
}
}
[Fact]
[WorkItem(1055825, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1055825")]
public void AssociatedField()
{
var source = @"
public class C
{
public event System.Action E;
}
";
var reference = CreateCompilation(source).EmitToImageReference();
var comp = CreateCompilation("", new[] { reference }, TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All));
var type = comp.GlobalNamespace.GetMember<NamedTypeSymbol>("C");
var @event = type.GetMember<PEEventSymbol>("E");
Assert.True(@event.HasAssociatedField);
var field = @event.AssociatedField;
Assert.NotNull(field);
Assert.Equal(@event, field.AssociatedSymbol);
}
[Fact]
[WorkItem(1055825, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1055825")]
public void AssociatedField_MultipleFields()
{
var ilSource = @"
.class public auto ansi beforefieldinit C
extends [mscorlib]System.Object
{
.field private int32 E
.field private class [mscorlib]System.Action E
.method public hidebysig specialname instance void
add_E(class [mscorlib]System.Action 'value') cil managed
{
ldnull
throw
}
.method public hidebysig specialname instance void
remove_E(class [mscorlib]System.Action 'value') cil managed
{
ldnull
throw
}
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
ldarg.0
call instance void [mscorlib]System.Object::.ctor()
ret
}
.event [mscorlib]System.Action E
{
.addon instance void C::add_E(class [mscorlib]System.Action)
.removeon instance void C::remove_E(class [mscorlib]System.Action)
} // end of event C::E
} // end of class C
";
var ilRef = CompileIL(ilSource);
var comp = CreateCompilation("", new[] { ilRef }, TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All));
comp.VerifyDiagnostics();
var type = comp.GlobalNamespace.GetMember<NamedTypeSymbol>("C");
var @event = type.GetMembers().OfType<PEEventSymbol>().Single();
Assert.True(@event.HasAssociatedField);
var field = @event.AssociatedField;
Assert.NotNull(field);
Assert.Equal(@event, field.AssociatedSymbol);
Assert.Equal(@event.Type, field.Type);
}
[Fact]
[WorkItem(1055825, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1055825")]
public void AssociatedField_DuplicateEvents()
{
var ilSource = @"
.class public auto ansi beforefieldinit C
extends [mscorlib]System.Object
{
.field private class [mscorlib]System.Action E
.method public hidebysig specialname instance void
add_E(class [mscorlib]System.Action 'value') cil managed
{
ldnull
throw
}
.method public hidebysig specialname instance void
remove_E(class [mscorlib]System.Action 'value') cil managed
{
ldnull
throw
}
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
ldarg.0
call instance void [mscorlib]System.Object::.ctor()
ret
}
.event [mscorlib]System.Action E
{
.addon instance void C::add_E(class [mscorlib]System.Action)
.removeon instance void C::remove_E(class [mscorlib]System.Action)
} // end of event C::E
.event [mscorlib]System.Action E
{
.addon instance void C::add_E(class [mscorlib]System.Action)
.removeon instance void C::remove_E(class [mscorlib]System.Action)
} // end of event C::E
} // end of class C
";
var ilRef = CompileIL(ilSource);
var comp = CreateCompilation("", new[] { ilRef }, TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All));
comp.VerifyDiagnostics();
var type = comp.GlobalNamespace.GetMember<NamedTypeSymbol>("C");
var events = type.GetMembers().OfType<PEEventSymbol>();
Assert.Equal(2, events.Count());
AssertEx.All(events, e => e.HasAssociatedField);
var field = events.First().AssociatedField;
Assert.NotNull(field);
AssertEx.All(events, e => e.AssociatedField == field);
Assert.Contains(field.AssociatedSymbol, events);
}
[Flags]
private enum VirtualnessModifiers
{
None = 0,
Abstract = 1,
Virtual = 2,
Override = 4,
Sealed = 8, //actually indicates sealed override
}
private static VirtualnessModifiers GetVirtualnessModifiers(Symbol symbol)
{
VirtualnessModifiers mods = VirtualnessModifiers.None;
if (symbol.IsAbstract) mods |= VirtualnessModifiers.Abstract;
if (symbol.IsVirtual) mods |= VirtualnessModifiers.Virtual;
if (symbol.IsSealed) mods |= VirtualnessModifiers.Sealed;
else if (symbol.IsOverride) mods |= VirtualnessModifiers.Override;
return mods;
}
}
}
|