From 9cad35a47654eccb8c74e2bd3640634d0cda3043 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Mon, 18 Mar 2024 09:50:47 -0700 Subject: [PATCH 1/8] ILVerify after kept member validation - ILVerify will fail due to members not being kept, so to make the errors more informative, run kept member validation first, then ILVerify --- .../test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs b/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs index 7e730cb0b59211..2f05eab1e4c666 100644 --- a/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs +++ b/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs @@ -101,6 +101,7 @@ public virtual void Check (TrimmedTestCaseResult linkResult) if (!HasActiveSkipKeptItemsValidationAttribute(linkResult.TestCase.FindTypeDefinition (original))) { CreateAssemblyChecker (original, linked, linkResult).Verify (); } + CreateILChecker ().Check(linkResult, original); } VerifyLinkingOfOtherAssemblies (original); @@ -279,7 +280,6 @@ protected virtual void AdditionalChecking (TrimmedTestCaseResult linkResult, Ass protected virtual void InitialChecking (TrimmedTestCaseResult linkResult, AssemblyDefinition original, AssemblyDefinition linked) { - CreateILChecker ().Check(linkResult, original); ValidateTypeRefsHaveValidAssemblyRefs (linked); } From 99dabd552bf86fdcd49123a47b40f30fff248874 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Mon, 18 Mar 2024 11:48:17 -0700 Subject: [PATCH 2/8] Look at recursive interfaces in interface impl marking --- .../src/linker/Linker.Steps/MarkStep.cs | 149 ++++++++++++++++-- .../linker/Linker/TypeReferenceExtensions.cs | 6 + ...terfaces.DefaultInterfaceMethodsTests.g.cs | 24 +++ ...e.Interfaces.RecursiveInterfacesTests.g.cs | 31 ++++ .../Inheritance.InterfacesTests.g.cs | 6 + .../DataFlow/FeatureCheckDataFlow.cs | 3 +- .../DimProvidedByRecursiveInterface.il | 86 ++++++++++ ...ultipleDimsProvidedByRecursiveInterface.il | 86 ++++++++++ ...mProvidedByUnreferencedIfaceInHierarchy.il | 70 ++++++++ .../DimProvidedByRecursiveInterface.cs | 67 ++++++++ ...ultipleDimsProvidedByRecursiveInterface.cs | 88 +++++++++++ ...mProvidedByUnreferencedIfaceInHierarchy.cs | 69 ++++++++ .../InterfaceVariantsGeneric.cs | 50 ++++++ ...enericInterfaceWithMethodManyVariations.cs | 2 +- .../GenericInterfaceImplementedRecursively.il | 45 ++++++ .../InterfaceImplementedRecursively.il | 67 ++++++++ ...ecursiveInterfaceTwoImplementationPaths.il | 62 ++++++++ .../GenericInterfaceImplementedRecursively.cs | 42 +++++ .../InterfaceImplementedRecursively.cs | 49 ++++++ .../RecursiveInterfaceKept.cs | 36 +++++ .../CompilerGeneratedCodeSubstitutions.cs | 4 +- .../TestCasesRunner/AssemblyChecker.cs | 2 +- 22 files changed, 1031 insertions(+), 13 deletions(-) create mode 100644 src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.Interfaces.RecursiveInterfacesTests.g.cs create mode 100644 src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/Dependencies/DimProvidedByRecursiveInterface.il create mode 100644 src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/Dependencies/MultipleDimsProvidedByRecursiveInterface.il create mode 100644 src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/Dependencies/StaticDimProvidedByUnreferencedIfaceInHierarchy.il create mode 100644 src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/DimProvidedByRecursiveInterface.cs create mode 100644 src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/MultipleDimsProvidedByRecursiveInterface.cs create mode 100644 src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/StaticDimProvidedByUnreferencedIfaceInHierarchy.cs create mode 100644 src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceVariantsGeneric.cs create mode 100644 src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/Dependencies/GenericInterfaceImplementedRecursively.il create mode 100644 src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/Dependencies/InterfaceImplementedRecursively.il create mode 100644 src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/Dependencies/RecursiveInterfaceTwoImplementationPaths.il create mode 100644 src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/GenericInterfaceImplementedRecursively.cs create mode 100644 src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/InterfaceImplementedRecursively.cs create mode 100644 src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/RecursiveInterfaceKept.cs diff --git a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs index 32fd98cbe039ca..c8602d62553a70 100644 --- a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs +++ b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs @@ -36,7 +36,6 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection.Runtime.TypeParsing; -using System.Runtime.CompilerServices; using System.Text.RegularExpressions; using ILLink.Shared; using ILLink.Shared.TrimAnalysis; @@ -2453,23 +2452,150 @@ void MarkInterfaceImplementations (TypeDefinition type) if (!type.HasInterfaces) return; - foreach (var iface in type.Interfaces) { + var ifaces = GetRecursiveInterfaceImplementations (type); + foreach (var (ifaceType, impls) in ifaces) { // Only mark interface implementations of interface types that have been marked. // This enables stripping of interfaces that are never used - if (ShouldMarkInterfaceImplementation (type, iface)) - MarkInterfaceImplementation (iface, new MessageOrigin (type)); + if (ShouldMarkInterfaceImplementationList (type, impls, ifaceType)) + MarkInterfaceImplementationList (impls, new MessageOrigin (type)); } } - protected virtual bool ShouldMarkInterfaceImplementation (TypeDefinition type, InterfaceImplementation iface) + List<(TypeReference, List)> GetRecursiveInterfaceImplementations (TypeDefinition type) { - if (Annotations.IsMarked (iface)) + List<(TypeReference, List)> firstImplementationChain = new (); + + AddRecursiveInterfaces (type, [], firstImplementationChain, Context); + Debug.Assert (firstImplementationChain.All (kvp => Context.Resolve (kvp.Item1) == Context.Resolve (kvp.Item2.Last ().InterfaceType))); + + return firstImplementationChain; + + static void AddRecursiveInterfaces (TypeReference typeRef, IEnumerable pathToType, List<(TypeReference, List)> firstImplementationChain, LinkContext Context) + { + var type = Context.TryResolve (typeRef); + if (type is null) + return; + // GeVt all explicit interfaces of this type + foreach (var directIface in type.Interfaces) { + //var directlyImplementedType = Context.Resolve (directIface.InterfaceType); + var directlyImplementedType = directIface.InterfaceType.TryInflateFrom (typeRef, Context); + if (directlyImplementedType is null) { + continue; + } + if (!firstImplementationChain.Any (i => InterfaceTypeEquals(i.Item1, directlyImplementedType, Context))) { + firstImplementationChain.Add ((directlyImplementedType, pathToType.Append (directIface).ToList ())); + } + } + + // Add interfaces on base type + if (type.BaseType is { } baseType && Context.Resolve (baseType) is { } baseDef) { + AddRecursiveInterfaces (baseDef, pathToType, firstImplementationChain, Context); + } + + // Recursive interfaces next to preserve Inherit/Implement tree order + foreach (var directIface in type.Interfaces) { + // If we can't resolve the interface type we can't find recursive interfaces + var ifaceDirectlyOnType = directIface.InterfaceType.TryInflateFrom(typeRef, Context); + if (ifaceDirectlyOnType is null) { + continue; + } + AddRecursiveInterfaces (ifaceDirectlyOnType, pathToType.Append (directIface), firstImplementationChain, Context); + } + } + /// + /// Compares two TypeReferences to interface types and determines if they are equivalent references, taking into account generic arguments and element types. + /// + static bool InterfaceTypeEquals (TypeReference? type, TypeReference? other, ITryResolveMetadata resolver) + { + Debug.Assert (type is not null && other is not null); + Debug.Assert (resolver.TryResolve (type)?.IsInterface is null or true); + Debug.Assert (resolver.TryResolve (other)?.IsInterface is null or true); + return TypeEquals (type, other); + + bool TypeEquals (TypeReference type1, TypeReference type2) + { + if (type1 == type2) + return true; + + if (resolver.TryResolve (type1) != resolver.TryResolve (type2)) + return false; + + if (type1 is GenericInstanceType genericInstance1) { + if (type2 is not GenericInstanceType genericInstance2) + return false; + if (genericInstance1.HasGenericParameters != genericInstance2.HasGenericParameters) + return false; + if (genericInstance1.GenericParameters.Count != genericInstance2.GenericParameters.Count + || genericInstance2.GenericArguments.Count != genericInstance2.GenericArguments.Count) + return false; + for (var i = 0; i < genericInstance1.GenericArguments.Count; ++i) { + if (!TypeEquals (genericInstance1.GenericArguments[i], genericInstance2.GenericArguments[i])) + return false; + } + return true; + } + + if (type1 is TypeSpecification typeSpec1) { + if (type2 is not TypeSpecification typeSpec2) + return false; + return TypeEquals (typeSpec1.ElementType, typeSpec2.ElementType); + } + return type1.FullName == type2.FullName; + } + } + } + // Dictionary> waysToImplementIface = new (InflatedInterfaceComparer); + + //// Get all explicit interfaces of this type + //foreach (var directIface in type.Interfaces) { + //InterfaceImplementationNode directlyImplementedNode = new InterfaceImplementationNode (directIface, type, []); + // TypeReference inflatedDirectIface = directIface.InterfaceType.TryInflateFrom (type, context)!; + // waysToImplementIface.AddToList (inflatedDirectIface, directlyImplementedNode); + //} + + //// Add interfaces on base type + //if (type.BaseType is { } baseType && context.Resolve (baseType) is { } baseDef) { + // MapInterfacesOnType (baseDef); + // var baseInterfaces = _interfaces[baseDef]; + // foreach (var item in baseInterfaces) { + // var inflatedInterface = item.InflatedInterface.TryInflateFrom (type.BaseType, context); + // Debug.Assert (inflatedInterface is not null); + // foreach (var node in item.InterfaceImplementationNodes) { + // waysToImplementIface.AddToList (inflatedInterface, node); + // } + // } + //} + + //// Recursive interfaces next to preserve Inherit/Implement tree order + //foreach (var directIface in type.Interfaces) { + // // If we can't resolve the interface type we can't find recursive interfaces + // var ifaceDirectlyOnType = context.Resolve (directIface.InterfaceType); + // if (ifaceDirectlyOnType is null) { + // continue; + // } + // MapInterfacesOnType (ifaceDirectlyOnType); + // TypeReference inflatedDirectIface = directIface.InterfaceType.TryInflateFrom (type, context)!; + // var recursiveInterfaces = _interfaces[ifaceDirectlyOnType]; + // foreach (var recursiveInterface in recursiveInterfaces) { + // var implToRecursiveIfaceChain = new InterfaceImplementationNode (directIface, type, recursiveInterface.InterfaceImplementationNodes); + // // Inflate the generic arguments up to the terminal interfaceImpl to get the inflated interface type implemented by this type + // TypeReference inflatedRecursiveInterface = inflatedDirectIface; + // foreach (var interfaceImpl in recursiveInterface.MostDirectInterfaceImplementationPath) { + // inflatedRecursiveInterface = interfaceImpl.InterfaceType.TryInflateFrom (inflatedRecursiveInterface, context)!; + // } + // waysToImplementIface.AddToList (inflatedRecursiveInterface, implToRecursiveIfaceChain); + // } + //} + + protected virtual bool ShouldMarkInterfaceImplementationList (TypeDefinition type, List ifaces, TypeReference ifaceType) + { + if (ifaces.All (Annotations.IsMarked)) return false; if (!Context.IsOptimizationEnabled (CodeOptimizations.UnusedInterfaces, type)) return true; - if (Context.Resolve (iface.InterfaceType) is not TypeDefinition resolvedInterfaceType) + if (Context.Resolve (ifaceType) is not TypeDefinition resolvedInterfaceType) return false; if (Annotations.IsMarked (resolvedInterfaceType)) @@ -3764,8 +3890,7 @@ protected virtual void MarkInstruction (Instruction instruction, MethodDefinitio ScopeStack.UpdateCurrentScopeInstructionOffset (instruction.Offset); if (markForReflectionAccess) { MarkMethodVisibleToReflection (methodReference, new DependencyInfo (dependencyKind, method), ScopeStack.CurrentScope.Origin); - } - else { + } else { MarkMethod (methodReference, new DependencyInfo (dependencyKind, method), ScopeStack.CurrentScope.Origin); } break; @@ -3826,6 +3951,12 @@ protected virtual void MarkInstruction (Instruction instruction, MethodDefinitio } + void MarkInterfaceImplementationList (List ifaces, MessageOrigin? origin = null, DependencyInfo? reason = null) + { + foreach (var iface in ifaces) { + MarkInterfaceImplementation (iface, origin, reason); + } + } protected internal virtual void MarkInterfaceImplementation (InterfaceImplementation iface, MessageOrigin? origin = null, DependencyInfo? reason = null) { if (Annotations.IsMarked (iface)) diff --git a/src/tools/illink/src/linker/Linker/TypeReferenceExtensions.cs b/src/tools/illink/src/linker/Linker/TypeReferenceExtensions.cs index 5092fe1158e34b..46dbf96bd379f3 100644 --- a/src/tools/illink/src/linker/Linker/TypeReferenceExtensions.cs +++ b/src/tools/illink/src/linker/Linker/TypeReferenceExtensions.cs @@ -150,6 +150,12 @@ void parseArrayDimensions (ArrayType at) Debug.Assert (false); return null; } + public static TypeReference? TryInflateFrom (this TypeReference type, TypeReference maybeGenericInstanceProvider, ITryResolveMetadata resolver) + { + if (maybeGenericInstanceProvider is GenericInstanceType git) + return InflateGenericType (git, type, resolver); + return type; + } public static IEnumerable<(TypeReference InflatedInterface, InterfaceImplementation OriginalImpl)> GetInflatedInterfaces (this TypeReference typeRef, ITryResolveMetadata resolver) { diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.Interfaces.DefaultInterfaceMethodsTests.g.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.Interfaces.DefaultInterfaceMethodsTests.g.cs index 4b3f387a390151..2ed8346211e4b0 100644 --- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.Interfaces.DefaultInterfaceMethodsTests.g.cs +++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.Interfaces.DefaultInterfaceMethodsTests.g.cs @@ -15,6 +15,18 @@ public Task DefaultInterfaceMethodCallIntoClass () return RunTest (allowMissingWarnings: true); } + [Fact] + public Task DimProvidedByRecursiveInterface () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task GenericDefaultInterfaceMethod () + { + return RunTest (allowMissingWarnings: true); + } + [Fact] public Task GenericDefaultInterfaceMethods () { @@ -39,6 +51,12 @@ public Task MostSpecificDefaultImplementationKeptStatic () return RunTest (allowMissingWarnings: true); } + [Fact] + public Task MultipleDimsProvidedByRecursiveInterface () + { + return RunTest (allowMissingWarnings: true); + } + [Fact] public Task SimpleDefaultInterfaceMethod () { @@ -51,6 +69,12 @@ public Task StaticDefaultInterfaceMethodOnStruct () return RunTest (allowMissingWarnings: true); } + [Fact] + public Task StaticDimProvidedByUnreferencedIfaceInHierarchy () + { + return RunTest (allowMissingWarnings: true); + } + [Fact] public Task UnusedDefaultInterfaceImplementation () { diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.Interfaces.RecursiveInterfacesTests.g.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.Interfaces.RecursiveInterfacesTests.g.cs new file mode 100644 index 00000000000000..d436348e800bc7 --- /dev/null +++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.Interfaces.RecursiveInterfacesTests.g.cs @@ -0,0 +1,31 @@ +using System; +using System.Threading.Tasks; +using Xunit; + +namespace ILLink.RoslynAnalyzer.Tests.Inheritance.Interfaces +{ + public sealed partial class RecursiveInterfacesTests : LinkerTestBase + { + + protected override string TestSuiteName => "Inheritance.Interfaces.RecursiveInterfaces"; + + [Fact] + public Task GenericInterfaceImplementedRecursively () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task InterfaceImplementedRecursively () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task RecursiveInterfaceKept () + { + return RunTest (allowMissingWarnings: true); + } + + } +} diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.InterfacesTests.g.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.InterfacesTests.g.cs index 649b8449527f75..07680c1c9d5e6a 100644 --- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.InterfacesTests.g.cs +++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.InterfacesTests.g.cs @@ -33,6 +33,12 @@ public Task InterfaceVariants () return RunTest (allowMissingWarnings: true); } + [Fact] + public Task InterfaceVariantsGeneric () + { + return RunTest (allowMissingWarnings: true); + } + [Fact] public Task InterfaceWithoutNewSlot () { diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/FeatureCheckDataFlow.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/FeatureCheckDataFlow.cs index d0d236997445ed..f26a38a8043079 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/FeatureCheckDataFlow.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/FeatureCheckDataFlow.cs @@ -1155,7 +1155,8 @@ static void GuardedLocalFunction () public static void Test () { - GuardInIterator (); + // Use the IEnumerable to mark the IEnumerable methods + foreach (var _ in GuardInIterator ()) ; StateFlowsAcrossYield (); GuardInAsync (); StateFlowsAcrossAwait (); diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/Dependencies/DimProvidedByRecursiveInterface.il b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/Dependencies/DimProvidedByRecursiveInterface.il new file mode 100644 index 00000000000000..c85892208989b3 --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/Dependencies/DimProvidedByRecursiveInterface.il @@ -0,0 +1,86 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +.assembly extern mscorlib { } + +.assembly 'library' { } + +.class public auto ansi abstract sealed beforefieldinit Program + extends [mscorlib]System.Object +{ + // Nested Types + .class interface nested public auto ansi abstract beforefieldinit IFoo + { + // Methods + .method public hidebysig newslot abstract virtual + instance void Method () cil managed + { + } // end of method IFoo::Method + + } // end of class IFoo + + .class interface nested public auto ansi abstract beforefieldinit IBar + implements Program/IFoo + { + // Methods + .method public final hidebysig virtual + instance void Program.IFoo.Method () cil managed + { + .override method instance void Program/IFoo::Method() + // Method begins at RVA 0x2068 + // Code size 2 (0x2) + .maxstack 8 + + IL_0000: nop + IL_0001: ret + } // end of method IBar::Program.IFoo.Method + + } // end of class IBar + + .class interface nested public auto ansi abstract beforefieldinit IBaz + implements Program/IBar + { + } // end of class IBaz + + .class nested public auto ansi beforefieldinit MyFoo + extends [mscorlib]System.Object + implements Program/IBaz + { + // Methods + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + // Method begins at RVA 0x2076 + // Code size 8 (0x8) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method MyFoo::.ctor + + } // end of class MyFoo + + + // Methods + .method public hidebysig static + void CallMethod ( + class Program/IFoo foo + ) cil managed + { + .custom instance void [mscorlib]mscorlib.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( + 01 00 01 00 00 + ) + // Method begins at RVA 0x2050 + // Code size 9 (0x9) + .maxstack 8 + + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: callvirt instance void Program/IFoo::Method() + IL_0007: nop + IL_0008: ret + } // end of method Program::CallMethod + +} // end of class Program diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/Dependencies/MultipleDimsProvidedByRecursiveInterface.il b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/Dependencies/MultipleDimsProvidedByRecursiveInterface.il new file mode 100644 index 00000000000000..4937513f285310 --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/Dependencies/MultipleDimsProvidedByRecursiveInterface.il @@ -0,0 +1,86 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +.assembly extern mscorlib { } + +.assembly 'library' { } + +.class public auto ansi abstract sealed beforefieldinit Program + extends [mscorlib]System.Object +{ + // Nested Types + .class interface nested public auto ansi abstract beforefieldinit I0 + { + // Methods + .method public hidebysig newslot abstract virtual + instance void Method () cil managed + { + } // end of method I0::Method + + } // end of class I0 + + .class interface nested public auto ansi abstract beforefieldinit I00 + implements Program/I0 + { + // Methods + .method public final hidebysig virtual + instance void Program.I0.Method () cil managed + { + .override method instance void Program/I0::Method() + // Method begins at RVA 0x2068 + // Code size 2 (0x2) + .maxstack 8 + + IL_0000: nop + IL_0001: ret + } // end of method I00::Program.I0.Method + } // end of class I00 + + .class interface nested public auto ansi abstract beforefieldinit I01 + implements Program/I0 + { + // Methods + .method public final hidebysig virtual + instance void Program.I0.Method () cil managed + { + .override method instance void Program/I0::Method() + // Method begins at RVA 0x2068 + // Code size 2 (0x2) + .maxstack 8 + + IL_0000: nop + IL_0001: ret + } // end of method I00::Program.I0.Method + } // end of class I00 + + .class interface nested public auto ansi abstract beforefieldinit I000 + implements Program/I00 + { + } // end of class I000 + + .class interface nested public auto ansi abstract beforefieldinit I010 + implements Program/I01 + { + } // end of class I000 + + .class nested public auto ansi beforefieldinit MyFoo + extends [mscorlib]System.Object + implements Program/I000, Program/I010 + { + // Methods + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + // Method begins at RVA 0x2076 + // Code size 8 (0x8) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method MyFoo::.ctor + + } // end of class MyFoo + +} // end of class Program diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/Dependencies/StaticDimProvidedByUnreferencedIfaceInHierarchy.il b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/Dependencies/StaticDimProvidedByUnreferencedIfaceInHierarchy.il new file mode 100644 index 00000000000000..949d5e6fbf4aba --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/Dependencies/StaticDimProvidedByUnreferencedIfaceInHierarchy.il @@ -0,0 +1,70 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +.assembly extern mscorlib { } + +.assembly 'library' { } + +.class public auto ansi abstract sealed beforefieldinit Program + extends [mscorlib]System.Object +{ + // Nested Types + .class interface nested public auto ansi abstract beforefieldinit IBase + { + // Methods + .method public hidebysig abstract virtual static + void Method () cil managed + { + } // end of method IBase::Method + + } // end of class IBase + + .class interface nested public auto ansi abstract beforefieldinit I2 + implements Program/IBase + { + // Methods + .method public hidebysig static + void Program.IBase.Method () cil managed + { + .override method void Program/IBase::Method() + // Method begins at RVA 0x205f + // Code size 2 (0x2) + .maxstack 8 + + IL_0000: nop + IL_0001: ret + } // end of method I2::Program.IBase.Method + + } // end of class I2 + + .class interface nested public auto ansi abstract beforefieldinit I3 + implements Program/I2 + { + } // end of class I3 + + .class interface nested public auto ansi abstract beforefieldinit I4 + implements Program/I3 + { + } // end of class I4 + + + // Methods + .method public hidebysig static + void CallMethod<(Program/IBase) T> () cil managed + { + .param constraint T, Program/IBase + .custom instance void [mscorlib]mscorlib.CompilerServices.NullableAttribute::.ctor(uint8) = ( + 01 00 01 00 00 + ) + // Method begins at RVA 0x2050 + // Code size 14 (0xe) + .maxstack 8 + + IL_0000: nop + IL_0001: constrained. !!T + IL_0007: call void Program/IBase::Method() + IL_000c: nop + IL_000d: ret + } // end of method Program::CallMethod + +} // end of class Program diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/DimProvidedByRecursiveInterface.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/DimProvidedByRecursiveInterface.cs new file mode 100644 index 00000000000000..743df7227a80c8 --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/DimProvidedByRecursiveInterface.cs @@ -0,0 +1,67 @@ + + +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.DefaultInterfaceMethods +{ + [SetupLinkerArgument ("--skip-unresolved", "true")] + [TestCaseRequirements (TestRunCharacteristics.SupportsDefaultInterfaceMethods, "Requires support for default interface methods")] + [Define ("IL_ASSEMBLY_AVAILABLE")] + [SetupCompileBefore ("library.dll", new[] { "Dependencies/DimProvidedByRecursiveInterface.il" })] + [SkipILVerify] + +#if IL_ASSEMBLY_AVAILABLE + [KeptMemberInAssembly ("library.dll", typeof(Program.IFoo), "Method()")] + [KeptTypeInAssembly ("library.dll", typeof(Program.IBar))] + [KeptMemberInAssembly ("library.dll", typeof(Program.IBar), "Program.IFoo.Method()")] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.IBar), "library.dll", typeof (Program.IFoo))] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.MyFoo), "library.dll", typeof (Program.IBaz))] + [KeptTypeInAssembly ("library.dll", typeof(Program.IBaz))] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.IBaz), "library.dll", typeof (Program.IBar))] + [KeptMemberInAssembly ("library.dll", typeof(Program), "CallMethod(Program/IFoo)")] +#endif + class DimProvidedByRecursiveInterface + { + static void Main () + { +#if IL_ASSEMBLY_AVAILABLE + Program.IFoo foo = new Program.MyFoo (); + Program.CallMethod(foo); +#endif + } + } +} + + + +// public static class Program +// { +// [Kept] +// interface IFoo +// { +// void Method(); +// } + +// [Kept] +// interface IBar : IFoo +// { +// [Kept] +// void IFoo.Method() { } +// } + +// [Kept] +// interface IBaz: IBar /* not IFoo */ +// { +// } + +// [Kept] +// [KeptInterface(typeof(IBaz))] +// class MyFoo : IBaz /* not IBar, not IFoo */ +// { } + +// static void CallMethod(IFoo foo) +// { +// foo.Method(); +// } +// } diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/MultipleDimsProvidedByRecursiveInterface.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/MultipleDimsProvidedByRecursiveInterface.cs new file mode 100644 index 00000000000000..ccaddf8b5309f4 --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/MultipleDimsProvidedByRecursiveInterface.cs @@ -0,0 +1,88 @@ + + +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.DefaultInterfaceMethods +{ + [SetupLinkerArgument ("--skip-unresolved", "true")] + [TestCaseRequirements (TestRunCharacteristics.SupportsDefaultInterfaceMethods, "Requires support for default interface methods")] + [Define ("IL_ASSEMBLY_AVAILABLE")] + [SetupCompileBefore ("library.dll", new[] { "Dependencies/MultipleDimsProvidedByRecursiveInterface.il" })] + [SkipILVerify] + +#if IL_ASSEMBLY_AVAILABLE + // Both DIMs on I01 and I00 should be kept because one is not more specific than another. + [KeptMemberInAssembly ("library.dll", typeof(Program.I0), "Method()")] + [KeptTypeInAssembly ("library.dll", typeof(Program.I00))] + [KeptMemberInAssembly ("library.dll", typeof(Program.I00), "Program.I0.Method()")] + // Bug: DIM resolution doesn't look at recursive interfaces + //[KeptMemberInAssembly ("library.dll", typeof(Program.I01), "Program.I0.Method()")] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.I00), "library.dll", typeof (Program.I0))] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.MyFoo), "library.dll", typeof (Program.I000))] + [KeptTypeInAssembly ("library.dll", typeof(Program.I000))] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.I000), "library.dll", typeof (Program.I00))] + // Bug: DIM resolution doesn't look at recursive interfaces + //[KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.MyFoo), "library.dll", typeof (Program.I010))] + //[KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.I010), "library.dll", typeof (Program.I01))] + //[KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.I01), "library.dll", typeof (Program.I0))] +#endif + class MultipleDimsProvidedByRecursiveInterface + { + static void Main () + { +#if IL_ASSEMBLY_AVAILABLE + Program.I0 foo = new Program.MyFoo (); + CallMethod(foo); +#endif + } +#if IL_ASSEMBLY_AVAILABLE + [Kept] + static void CallMethod(Program.I0 foo) + { + foo.Method(); + } +#endif + } +} + + + +// public static class Program +// { +// [Kept] +// interface I0 +// { +// void Method(); +// } + +// [Kept] +// interface I00 : I0 +// { +// [Kept] +// void I0.Method() { } +// } + +// [Kept] +// interface I000: I00 /* not I0 */ +// { +// } + +// [Kept] +// interface I01 : I0 +// { +// [Kept] +// void I0.Method() { } +// } + +// [Kept] +// interface I010: I01 /* not I0 */ +// { +// } + +// [Kept] +// [KeptInterface(typeof(I000))] +// class MyFoo : I000, I010 +// { } + +// } diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/StaticDimProvidedByUnreferencedIfaceInHierarchy.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/StaticDimProvidedByUnreferencedIfaceInHierarchy.cs new file mode 100644 index 00000000000000..2d10e78d146f35 --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/StaticDimProvidedByUnreferencedIfaceInHierarchy.cs @@ -0,0 +1,69 @@ + + +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.DefaultInterfaceMethods +{ + [SetupLinkerArgument ("--skip-unresolved", "true")] + [TestCaseRequirements (TestRunCharacteristics.SupportsDefaultInterfaceMethods, "Requires support for default interface methods")] + [Define ("IL_ASSEMBLY_AVAILABLE")] + [SetupCompileBefore ("library.dll", new[] { "Dependencies/StaticDimProvidedByUnreferencedIfaceInHierarchy.il" })] + [SkipILVerify] + +#if IL_ASSEMBLY_AVAILABLE + [KeptMemberInAssembly ("library.dll", typeof(Program), "CallMethod<#1>()")] + [KeptTypeInAssembly ("library.dll", typeof(Program.IBase))] + [KeptMemberInAssembly ("library.dll", typeof(Program.IBase), "Method()")] + [KeptTypeInAssembly ("library.dll", typeof(Program.I4))] + [KeptTypeInAssembly ("library.dll", typeof(Program.I2))] + [KeptMemberInAssembly ("library.dll", typeof(Program.I2), "Program.IBase.Method()")] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.I2), "library.dll", typeof (Program.IBase))] + [KeptTypeInAssembly ("library.dll", typeof(Program.I3))] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.I3), "library.dll", typeof (Program.I2))] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.I4), "library.dll", typeof (Program.I3))] +#endif + class StaticDimProvidedByUnreferencedIfaceInHierarchy + { + static void Main () + { +#if IL_ASSEMBLY_AVAILABLE + Program.CallMethod(); +#endif + } + } +} + + + +// public static class Program +// { +// [Kept] +// interface IBase +// { +// [Kept] +// static abstract void Method(); +// } + +// [Kept] +// [KeptInterface(typeof(IBase)] +// interface I2 : IBase +// { +// [Kept] +// static void IBase.Method() { } +// } + +// [Kept] +// [KeptInterface(typeof(I2)] +// interface I3 : I2 { } + +// [Kept] +// [KeptInterface(typeof(I3)] +// interface I4 : I3 { } + +// [Kept] +// static void CallMethod() where T : IBase +// { +// T.Method(); +// } +// } diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceVariantsGeneric.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceVariantsGeneric.cs new file mode 100644 index 00000000000000..17f5aed992ebba --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceVariantsGeneric.cs @@ -0,0 +1,50 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections; +using System.Collections.Generic; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Helpers; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces +{ + public class InterfaceVariantsGeneric + { + public static void Main () + { + G g = new C (); + g.M (1, 2.0f); + } + + [Kept] + interface G + { + [Kept] + void M (T t, U u); + } + [Kept] + public class MyT { } + [Kept] + public class MyU { } + [Kept] + [KeptInterface (typeof (G))] + [KeptInterface (typeof (G))] + [KeptInterface (typeof (G))] + [KeptMember (".ctor()")] + public class C : G, G, G + { + [Kept] + public void M (int t, float u) { } + + public void M (long t, double u) { } + + [Kept] + public void M (MyT t, MyU u) { } + + [Kept] + void G.M(long t, double u) { } + } + } +} diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/BaseProvidesInterfaceMember/GenericInterfaceWithMethodManyVariations.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/BaseProvidesInterfaceMember/GenericInterfaceWithMethodManyVariations.cs index 078f4b94c60ba6..79ad8eaaa58c2c 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/BaseProvidesInterfaceMember/GenericInterfaceWithMethodManyVariations.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/BaseProvidesInterfaceMember/GenericInterfaceWithMethodManyVariations.cs @@ -68,4 +68,4 @@ class Bar { } } -} \ No newline at end of file +} diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/Dependencies/GenericInterfaceImplementedRecursively.il b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/Dependencies/GenericInterfaceImplementedRecursively.il new file mode 100644 index 00000000000000..7ff59e54ad0658 --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/Dependencies/GenericInterfaceImplementedRecursively.il @@ -0,0 +1,45 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +.assembly extern mscorlib { } + +.assembly 'library' { } + +.class public auto ansi abstract sealed beforefieldinit Program + extends [mscorlib]System.Object +{ + // Nested Types + .class interface nested public auto ansi abstract beforefieldinit IBase`1 + { + } // end of class IBase + + .class interface nested public auto ansi abstract beforefieldinit IMiddle`1 + implements class Program/IBase`1 + { + } // end of class IMiddle + + .class interface nested public auto ansi abstract beforefieldinit IDerived`1 + implements class Program/IMiddle`1 + { + } // end of class IDerived + + .class nested public auto ansi beforefieldinit C + extends [mscorlib]System.Object + implements class Program/IDerived`1 + { + // Methods + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + // Method begins at RVA 0x2066 + // Code size 8 (0x8) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method C::.ctor + + } // end of class C +} // end of class Program diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/Dependencies/InterfaceImplementedRecursively.il b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/Dependencies/InterfaceImplementedRecursively.il new file mode 100644 index 00000000000000..c1d2943997da6f --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/Dependencies/InterfaceImplementedRecursively.il @@ -0,0 +1,67 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +.assembly extern mscorlib { } + +.assembly 'library' { } + +.class public auto ansi abstract sealed beforefieldinit Program + extends [System.Runtime]System.Object +{ + // Nested Types + .class interface nested public auto ansi abstract beforefieldinit IBase + { + } // end of class IBase + + .class interface nested public auto ansi abstract beforefieldinit IMiddle + implements Program/IBase + { + } // end of class IMiddle + + .class interface nested public auto ansi abstract beforefieldinit IDerived + implements Program/IMiddle + { + } // end of class IDerived + + .class nested public auto ansi beforefieldinit C + extends [System.Runtime]System.Object + implements Program/IDerived + { + // Methods + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + // Method begins at RVA 0x2066 + // Code size 8 (0x8) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void [System.Runtime]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method C::.ctor + + } // end of class C + + + // Methods + .method public hidebysig static + void Main () cil managed + { + // Method begins at RVA 0x2050 + // Code size 10 (0xa) + .maxstack 1 + .locals init ( + [0] class Program/IBase b, + [1] class Program/C c + ) + + IL_0000: nop + IL_0001: ldnull + IL_0002: stloc.0 + IL_0003: newobj instance void Program/C::.ctor() + IL_0008: stloc.1 + IL_0009: ret + } // end of method Program::Main + +} // end of class Program diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/Dependencies/RecursiveInterfaceTwoImplementationPaths.il b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/Dependencies/RecursiveInterfaceTwoImplementationPaths.il new file mode 100644 index 00000000000000..854b0cd4a45b20 --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/Dependencies/RecursiveInterfaceTwoImplementationPaths.il @@ -0,0 +1,62 @@ + +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +.assembly extern mscorlib { } + +.assembly 'library' { } + +.class public auto ansi abstract sealed beforefieldinit Library + extends [mscorlib]System.Object +{ + .class interface nested public auto ansi abstract beforefieldinit I0 + { + } // end of class I0 + + .class interface nested public auto ansi abstract beforefieldinit I00 + implements Library/I0 + { + } // end of class I00 + + .class interface nested public auto ansi abstract beforefieldinit I01 + implements Library/I0 + { + } // end of class I01 + + .class interface nested public auto ansi abstract beforefieldinit I000 + implements Library/I00 + { + } // end of class I000 + + .class interface nested public auto ansi abstract beforefieldinit I010 + implements Library/I01 + { + } // end of class I010 + + .class interface nested public auto ansi abstract beforefieldinit I0100 + implements Library/I010 + { + } // end of class I010 + + .class nested public auto ansi beforefieldinit MyClass + extends [mscorlib]System.Object + implements Library/I0100, + Library/I000 + { + // Methods + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + // Method begins at RVA 0x2076 + // Code size 8 (0x8) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method MyFoo::.ctor + + } // end of class MyFoo + +} // end of class Library diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/GenericInterfaceImplementedRecursively.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/GenericInterfaceImplementedRecursively.cs new file mode 100644 index 00000000000000..bb0d318cac1a1d --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/GenericInterfaceImplementedRecursively.cs @@ -0,0 +1,42 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.RecursiveInterfaces +{ + [SetupLinkerArgument ("--skip-unresolved", "true")] + [TestCaseRequirements (TestRunCharacteristics.SupportsDefaultInterfaceMethods, "Requires support for default interface methods")] + [Define ("IL_ASSEMBLY_AVAILABLE")] + [SetupCompileBefore ("library.dll", new[] { "Dependencies/GenericInterfaceImplementedRecursively.il" })] + [SkipILVerify] +#if IL_ASSEMBLY_AVAILABLE + [KeptTypeInAssembly ("library.dll", typeof(Program.IBase<>))] + [KeptTypeInAssembly ("library.dll", typeof(Program.IMiddle<>))] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.IMiddle<>), "library.dll", typeof (Program.IBase<>))] + [KeptTypeInAssembly ("library.dll", typeof(Program.IDerived<>))] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.IDerived<>), "library.dll", typeof (Program.IMiddle<>))] + [KeptTypeInAssembly ("library.dll", typeof(Program.C))] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.C), "library.dll", typeof (Program.IDerived))] +#endif + /// + /// This test case is to verify that the linker will keep all the metadata necessary for C to implement IBase when an interfaceImpl isn't directly on C. + /// + class GenericInterfaceImplementedRecursively + { + public static void Main() + { + +#if IL_ASSEMBLY_AVAILABLE + Program.IBase _ = null; + _ = new Program.C(); +#endif + } + } +} diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/InterfaceImplementedRecursively.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/InterfaceImplementedRecursively.cs new file mode 100644 index 00000000000000..89f59777c5fcd0 --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/InterfaceImplementedRecursively.cs @@ -0,0 +1,49 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.RecursiveInterfaces +{ + [SetupLinkerArgument ("--skip-unresolved", "true")] + [TestCaseRequirements (TestRunCharacteristics.SupportsDefaultInterfaceMethods, "Requires support for default interface methods")] + [Define ("IL_ASSEMBLY_AVAILABLE")] + [SetupCompileBefore ("library.dll", new[] { "Dependencies/InterfaceImplementedRecursively.il" })] + [SkipILVerify] +#if IL_ASSEMBLY_AVAILABLE + [KeptTypeInAssembly ("library.dll", typeof(Program.IBase))] + [KeptTypeInAssembly ("library.dll", typeof(Program.IMiddle))] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.IMiddle), "library.dll", typeof (Program.IBase))] + [KeptTypeInAssembly ("library.dll", typeof(Program.IDerived))] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.IDerived), "library.dll", typeof (Program.IMiddle))] + [KeptTypeInAssembly ("library.dll", typeof(Program.C))] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.C), "library.dll", typeof (Program.IDerived))] +#endif + /// + /// This test case is to verify that the linker will keep all the metadata necessary for C to implement IBase when an interfaceImpl isn't directly on C. + /// + class InterfaceImplementedRecursively + { + public static void Main() + { + +#if IL_ASSEMBLY_AVAILABLE + Program.IBase b = null; + object c = new Program.C(); + +#endif + } + } + // interface IBase {} + // interface IMiddle : IBase {} + // interface IDerived : IMiddle {} + // class C : IDerived + // { + // } +} diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/RecursiveInterfaceKept.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/RecursiveInterfaceKept.cs new file mode 100644 index 00000000000000..56e08d67be74f7 --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/RecursiveInterfaceKept.cs @@ -0,0 +1,36 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.RecursiveInterfaces +{ + /// + /// This tests that when a type implements an interface recursively (via implementations on implemented interfaces), + /// the interface implementations kept are in type declaration order according to ECMA-335 12.2 + /// + [TestCaseRequirements (TestRunCharacteristics.SupportsDefaultInterfaceMethods, "Requires support for default interface methods")] + [Define ("IL_ASSEMBLY_AVAILABLE")] + [SetupCompileBefore ("library.dll", new[] { "Dependencies/RecursiveInterfaceTwoImplementationPaths.il" })] + [SkipILVerify] +#if IL_ASSEMBLY_AVAILABLE + [KeptTypeInAssembly ("library.dll", typeof(Library.MyClass))] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Library.MyClass), "library.dll", typeof (Library.I0100))] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Library.I0100), "library.dll", typeof (Library.I010))] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Library.I010), "library.dll", typeof (Library.I01))] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Library.I01), "library.dll", typeof (Library.I0))] + [RemovedTypeInAssembly("library.dll", typeof(Library.I00))] + [RemovedTypeInAssembly("library.dll", typeof(Library.I000))] + [RemovedInterfaceOnTypeInAssembly("library.dll", typeof (Library.MyClass), "library.dll", typeof (Library.I000))] +#endif + public class RecursiveInterfaceKept + { + public static void Main() + { +#if IL_ASSEMBLY_AVAILABLE + Library.I0 _ = new Library.MyClass(); +#endif + } + } +} diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/UnreachableBlock/CompilerGeneratedCodeSubstitutions.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/UnreachableBlock/CompilerGeneratedCodeSubstitutions.cs index 5b1edc37671ace..3c59c3e446aadb 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/UnreachableBlock/CompilerGeneratedCodeSubstitutions.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/UnreachableBlock/CompilerGeneratedCodeSubstitutions.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Threading.Tasks; using Mono.Linker.Tests.Cases.Expectations.Assertions; using Mono.Linker.Tests.Cases.Expectations.Metadata; @@ -174,7 +175,8 @@ static IEnumerable TestBranchWithYieldBefore () public static void Test () { - TestBranchWithNormalCall (); + // Use the IEnumerable to mark the IEnumerable methods + foreach (var _ in TestBranchWithNormalCall ()) ; TestBranchWithYieldAfter (); TestBranchWithYieldBefore (); } diff --git a/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs b/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs index 8ed15724cc1148..467c2272c1bed4 100644 --- a/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs +++ b/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs @@ -345,7 +345,7 @@ IEnumerable VerifyInterfaces (TypeDefinition src, TypeDefinition linked) } if (expectedInterfaces.Any ()) { - yield return $"Expected interfaces were not found on {src}"; + yield return $"Expected interfaces were not found on {src}. Expected to find: \n{string.Join(Environment.NewLine, expectedInterfaces)}\n"; } } } From e24e7e5a1f6c102e3633f59d52cb7207f0a26667 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Fri, 22 Mar 2024 14:58:21 -0700 Subject: [PATCH 3/8] Undo test change, remove commented code --- .../src/linker/Linker.Steps/MarkStep.cs | 42 ------------------- .../DataFlow/FeatureCheckDataFlow.cs | 2 +- 2 files changed, 1 insertion(+), 43 deletions(-) diff --git a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs index c8602d62553a70..d99b51ab1d8fe8 100644 --- a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs +++ b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs @@ -2544,48 +2544,6 @@ bool TypeEquals (TypeReference type1, TypeReference type2) } } } - // Dictionary> waysToImplementIface = new (InflatedInterfaceComparer); - - //// Get all explicit interfaces of this type - //foreach (var directIface in type.Interfaces) { - //InterfaceImplementationNode directlyImplementedNode = new InterfaceImplementationNode (directIface, type, []); - // TypeReference inflatedDirectIface = directIface.InterfaceType.TryInflateFrom (type, context)!; - // waysToImplementIface.AddToList (inflatedDirectIface, directlyImplementedNode); - //} - - //// Add interfaces on base type - //if (type.BaseType is { } baseType && context.Resolve (baseType) is { } baseDef) { - // MapInterfacesOnType (baseDef); - // var baseInterfaces = _interfaces[baseDef]; - // foreach (var item in baseInterfaces) { - // var inflatedInterface = item.InflatedInterface.TryInflateFrom (type.BaseType, context); - // Debug.Assert (inflatedInterface is not null); - // foreach (var node in item.InterfaceImplementationNodes) { - // waysToImplementIface.AddToList (inflatedInterface, node); - // } - // } - //} - - //// Recursive interfaces next to preserve Inherit/Implement tree order - //foreach (var directIface in type.Interfaces) { - // // If we can't resolve the interface type we can't find recursive interfaces - // var ifaceDirectlyOnType = context.Resolve (directIface.InterfaceType); - // if (ifaceDirectlyOnType is null) { - // continue; - // } - // MapInterfacesOnType (ifaceDirectlyOnType); - // TypeReference inflatedDirectIface = directIface.InterfaceType.TryInflateFrom (type, context)!; - // var recursiveInterfaces = _interfaces[ifaceDirectlyOnType]; - // foreach (var recursiveInterface in recursiveInterfaces) { - // var implToRecursiveIfaceChain = new InterfaceImplementationNode (directIface, type, recursiveInterface.InterfaceImplementationNodes); - // // Inflate the generic arguments up to the terminal interfaceImpl to get the inflated interface type implemented by this type - // TypeReference inflatedRecursiveInterface = inflatedDirectIface; - // foreach (var interfaceImpl in recursiveInterface.MostDirectInterfaceImplementationPath) { - // inflatedRecursiveInterface = interfaceImpl.InterfaceType.TryInflateFrom (inflatedRecursiveInterface, context)!; - // } - // waysToImplementIface.AddToList (inflatedRecursiveInterface, implToRecursiveIfaceChain); - // } - //} protected virtual bool ShouldMarkInterfaceImplementationList (TypeDefinition type, List ifaces, TypeReference ifaceType) { diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/FeatureCheckDataFlow.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/FeatureCheckDataFlow.cs index f26a38a8043079..8c8c3baf5ab5c6 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/FeatureCheckDataFlow.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/FeatureCheckDataFlow.cs @@ -1156,7 +1156,7 @@ static void GuardedLocalFunction () public static void Test () { // Use the IEnumerable to mark the IEnumerable methods - foreach (var _ in GuardInIterator ()) ; + GuardInIterator (); StateFlowsAcrossYield (); GuardInAsync (); StateFlowsAcrossAwait (); From f3e5efb339c84feffd61ba6db656f53c5212f8cd Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Mon, 25 Mar 2024 12:10:09 -0700 Subject: [PATCH 4/8] Cache recursive interfaces --- .../src/linker/Linker.Steps/MarkStep.cs | 89 +------------------ .../illink/src/linker/Linker/Annotations.cs | 5 ++ .../illink/src/linker/Linker/TypeMapInfo.cs | 89 +++++++++++++++++++ ...terfaces.DefaultInterfaceMethodsTests.g.cs | 6 -- 4 files changed, 97 insertions(+), 92 deletions(-) diff --git a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs index d99b51ab1d8fe8..580c649909552f 100644 --- a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs +++ b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs @@ -35,6 +35,7 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Reflection.Metadata.Ecma335; using System.Reflection.Runtime.TypeParsing; using System.Text.RegularExpressions; using ILLink.Shared; @@ -2449,10 +2450,9 @@ void MarkNamedProperty (TypeDefinition type, string property_name, in Dependency void MarkInterfaceImplementations (TypeDefinition type) { - if (!type.HasInterfaces) + var ifaces = Annotations.GetRecursiveInterfaces (type); + if (ifaces is null) return; - - var ifaces = GetRecursiveInterfaceImplementations (type); foreach (var (ifaceType, impls) in ifaces) { // Only mark interface implementations of interface types that have been marked. // This enables stripping of interfaces that are never used @@ -2461,89 +2461,6 @@ void MarkInterfaceImplementations (TypeDefinition type) } } - List<(TypeReference, List)> GetRecursiveInterfaceImplementations (TypeDefinition type) - { - List<(TypeReference, List)> firstImplementationChain = new (); - - AddRecursiveInterfaces (type, [], firstImplementationChain, Context); - Debug.Assert (firstImplementationChain.All (kvp => Context.Resolve (kvp.Item1) == Context.Resolve (kvp.Item2.Last ().InterfaceType))); - - return firstImplementationChain; - - static void AddRecursiveInterfaces (TypeReference typeRef, IEnumerable pathToType, List<(TypeReference, List)> firstImplementationChain, LinkContext Context) - { - var type = Context.TryResolve (typeRef); - if (type is null) - return; - // GeVt all explicit interfaces of this type - foreach (var directIface in type.Interfaces) { - //var directlyImplementedType = Context.Resolve (directIface.InterfaceType); - var directlyImplementedType = directIface.InterfaceType.TryInflateFrom (typeRef, Context); - if (directlyImplementedType is null) { - continue; - } - if (!firstImplementationChain.Any (i => InterfaceTypeEquals(i.Item1, directlyImplementedType, Context))) { - firstImplementationChain.Add ((directlyImplementedType, pathToType.Append (directIface).ToList ())); - } - } - - // Add interfaces on base type - if (type.BaseType is { } baseType && Context.Resolve (baseType) is { } baseDef) { - AddRecursiveInterfaces (baseDef, pathToType, firstImplementationChain, Context); - } - - // Recursive interfaces next to preserve Inherit/Implement tree order - foreach (var directIface in type.Interfaces) { - // If we can't resolve the interface type we can't find recursive interfaces - var ifaceDirectlyOnType = directIface.InterfaceType.TryInflateFrom(typeRef, Context); - if (ifaceDirectlyOnType is null) { - continue; - } - AddRecursiveInterfaces (ifaceDirectlyOnType, pathToType.Append (directIface), firstImplementationChain, Context); - } - } - /// - /// Compares two TypeReferences to interface types and determines if they are equivalent references, taking into account generic arguments and element types. - /// - static bool InterfaceTypeEquals (TypeReference? type, TypeReference? other, ITryResolveMetadata resolver) - { - Debug.Assert (type is not null && other is not null); - Debug.Assert (resolver.TryResolve (type)?.IsInterface is null or true); - Debug.Assert (resolver.TryResolve (other)?.IsInterface is null or true); - return TypeEquals (type, other); - - bool TypeEquals (TypeReference type1, TypeReference type2) - { - if (type1 == type2) - return true; - - if (resolver.TryResolve (type1) != resolver.TryResolve (type2)) - return false; - - if (type1 is GenericInstanceType genericInstance1) { - if (type2 is not GenericInstanceType genericInstance2) - return false; - if (genericInstance1.HasGenericParameters != genericInstance2.HasGenericParameters) - return false; - if (genericInstance1.GenericParameters.Count != genericInstance2.GenericParameters.Count - || genericInstance2.GenericArguments.Count != genericInstance2.GenericArguments.Count) - return false; - for (var i = 0; i < genericInstance1.GenericArguments.Count; ++i) { - if (!TypeEquals (genericInstance1.GenericArguments[i], genericInstance2.GenericArguments[i])) - return false; - } - return true; - } - - if (type1 is TypeSpecification typeSpec1) { - if (type2 is not TypeSpecification typeSpec2) - return false; - return TypeEquals (typeSpec1.ElementType, typeSpec2.ElementType); - } - return type1.FullName == type2.FullName; - } - } - } protected virtual bool ShouldMarkInterfaceImplementationList (TypeDefinition type, List ifaces, TypeReference ifaceType) { diff --git a/src/tools/illink/src/linker/Linker/Annotations.cs b/src/tools/illink/src/linker/Linker/Annotations.cs index a7b3198265e812..cf74a35a7a7242 100644 --- a/src/tools/illink/src/linker/Linker/Annotations.cs +++ b/src/tools/illink/src/linker/Linker/Annotations.cs @@ -34,6 +34,7 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Reflection.Metadata.Ecma335; using ILLink.Shared.TrimAnalysis; using Mono.Cecil; using Mono.Cecil.Cil; @@ -717,5 +718,9 @@ public void EnqueueVirtualMethod (MethodDefinition method) if (FlowAnnotations.RequiresVirtualMethodDataFlowAnalysis (method) || HasLinkerAttribute (method)) VirtualMethodsWithAnnotationsToValidate.Add (method); } + public List<(TypeReference, List)>? GetRecursiveInterfaces(TypeDefinition type) + { + return TypeMapInfo.GetRecursiveInterfaces (type); + } } } diff --git a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs index 7e68b71bcb7d08..8b57c385c55367 100644 --- a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs +++ b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs @@ -116,10 +116,12 @@ public void AddDefaultInterfaceImplementation (MethodDefinition @base, Interface default_interface_implementations.AddToList (@base, new OverrideInformation (@base, defaultImplementationMethod, interfaceImplementor)); } + Dictionary)>> interfaces = new (); protected virtual void MapType (TypeDefinition type) { MapVirtualMethods (type); MapInterfaceMethodsInTypeHierarchy (type); + interfaces[type] = GetRecursiveInterfaceImplementations (type); if (!type.HasNestedTypes) return; @@ -128,6 +130,93 @@ protected virtual void MapType (TypeDefinition type) MapType (nested); } + public List<(TypeReference, List)>? GetRecursiveInterfaces(TypeDefinition type) + { + if (interfaces.TryGetValue (type, out var value)) + return value; + return null; + } + + List<(TypeReference, List)> GetRecursiveInterfaceImplementations (TypeDefinition type) + { + List<(TypeReference, List)> firstImplementationChain = new (); + + AddRecursiveInterfaces (type, [], firstImplementationChain, context); + Debug.Assert (firstImplementationChain.All (kvp => context.Resolve (kvp.Item1) == context.Resolve (kvp.Item2.Last ().InterfaceType))); + + return firstImplementationChain; + + static void AddRecursiveInterfaces (TypeReference typeRef, IEnumerable pathToType, List<(TypeReference, List)> firstImplementationChain, LinkContext Context) + { + var type = Context.TryResolve (typeRef); + if (type is null) + return; + // GeVt all explicit interfaces of this type + foreach (var directIface in type.Interfaces) { + //var directlyImplementedType = Context.Resolve (directIface.InterfaceType); + var directlyImplementedType = directIface.InterfaceType.TryInflateFrom (typeRef, Context); + if (directlyImplementedType is null) { + continue; + } + if (!firstImplementationChain.Any (i => InterfaceTypeEquals(i.Item1, directlyImplementedType, Context))) { + firstImplementationChain.Add ((directlyImplementedType, pathToType.Append (directIface).ToList ())); + } + } + + // Recursive interfaces next to preserve Inherit/Implement tree order + foreach (var directIface in type.Interfaces) { + // If we can't resolve the interface type we can't find recursive interfaces + var ifaceDirectlyOnType = directIface.InterfaceType.TryInflateFrom(typeRef, Context); + if (ifaceDirectlyOnType is null) { + continue; + } + AddRecursiveInterfaces (ifaceDirectlyOnType, pathToType.Append (directIface), firstImplementationChain, Context); + } + } + + /// + /// Compares two TypeReferences to interface types and determines if they are equivalent references, taking into account generic arguments and element types. + /// + static bool InterfaceTypeEquals (TypeReference? type, TypeReference? other, ITryResolveMetadata resolver) + { + Debug.Assert (type is not null && other is not null); + Debug.Assert (resolver.TryResolve (type)?.IsInterface is null or true); + Debug.Assert (resolver.TryResolve (other)?.IsInterface is null or true); + return TypeEquals (type, other); + + bool TypeEquals (TypeReference type1, TypeReference type2) + { + if (type1 == type2) + return true; + + if (resolver.TryResolve (type1) != resolver.TryResolve (type2)) + return false; + + if (type1 is GenericInstanceType genericInstance1) { + if (type2 is not GenericInstanceType genericInstance2) + return false; + if (genericInstance1.HasGenericParameters != genericInstance2.HasGenericParameters) + return false; + if (genericInstance1.GenericParameters.Count != genericInstance2.GenericParameters.Count + || genericInstance2.GenericArguments.Count != genericInstance2.GenericArguments.Count) + return false; + for (var i = 0; i < genericInstance1.GenericArguments.Count; ++i) { + if (!TypeEquals (genericInstance1.GenericArguments[i], genericInstance2.GenericArguments[i])) + return false; + } + return true; + } + + if (type1 is TypeSpecification typeSpec1) { + if (type2 is not TypeSpecification typeSpec2) + return false; + return TypeEquals (typeSpec1.ElementType, typeSpec2.ElementType); + } + return type1.FullName == type2.FullName; + } + } + } + void MapInterfaceMethodsInTypeHierarchy (TypeDefinition type) { if (!type.HasInterfaces) diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.Interfaces.DefaultInterfaceMethodsTests.g.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.Interfaces.DefaultInterfaceMethodsTests.g.cs index 2ed8346211e4b0..19189bfcd01705 100644 --- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.Interfaces.DefaultInterfaceMethodsTests.g.cs +++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.Interfaces.DefaultInterfaceMethodsTests.g.cs @@ -21,12 +21,6 @@ public Task DimProvidedByRecursiveInterface () return RunTest (allowMissingWarnings: true); } - [Fact] - public Task GenericDefaultInterfaceMethod () - { - return RunTest (allowMissingWarnings: true); - } - [Fact] public Task GenericDefaultInterfaceMethods () { From 2a1b394c3f5cf72420511f306d9c81465ceece38 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Mon, 25 Mar 2024 13:43:33 -0700 Subject: [PATCH 5/8] Make method internal and fix typo --- src/tools/illink/src/linker/Linker/Annotations.cs | 2 +- src/tools/illink/src/linker/Linker/TypeMapInfo.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tools/illink/src/linker/Linker/Annotations.cs b/src/tools/illink/src/linker/Linker/Annotations.cs index cf74a35a7a7242..4b15bc6c87cf33 100644 --- a/src/tools/illink/src/linker/Linker/Annotations.cs +++ b/src/tools/illink/src/linker/Linker/Annotations.cs @@ -718,7 +718,7 @@ public void EnqueueVirtualMethod (MethodDefinition method) if (FlowAnnotations.RequiresVirtualMethodDataFlowAnalysis (method) || HasLinkerAttribute (method)) VirtualMethodsWithAnnotationsToValidate.Add (method); } - public List<(TypeReference, List)>? GetRecursiveInterfaces(TypeDefinition type) + internal List<(TypeReference, List)>? GetRecursiveInterfaces(TypeDefinition type) { return TypeMapInfo.GetRecursiveInterfaces (type); } diff --git a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs index 8b57c385c55367..c037e7e8878ad7 100644 --- a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs +++ b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs @@ -130,7 +130,7 @@ protected virtual void MapType (TypeDefinition type) MapType (nested); } - public List<(TypeReference, List)>? GetRecursiveInterfaces(TypeDefinition type) + internal List<(TypeReference, List)>? GetRecursiveInterfaces(TypeDefinition type) { if (interfaces.TryGetValue (type, out var value)) return value; @@ -151,7 +151,7 @@ static void AddRecursiveInterfaces (TypeReference typeRef, IEnumerable Date: Mon, 25 Mar 2024 17:33:09 -0700 Subject: [PATCH 6/8] Apply suggestions from code review Co-authored-by: Sven Boemer --- src/tools/illink/src/linker/Linker.Steps/MarkStep.cs | 2 +- src/tools/illink/src/linker/Linker/Annotations.cs | 3 ++- src/tools/illink/src/linker/Linker/TypeMapInfo.cs | 6 +++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs index 580c649909552f..9290b157920dd2 100644 --- a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs +++ b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs @@ -3825,13 +3825,13 @@ protected virtual void MarkInstruction (Instruction instruction, MethodDefinitio } } - void MarkInterfaceImplementationList (List ifaces, MessageOrigin? origin = null, DependencyInfo? reason = null) { foreach (var iface in ifaces) { MarkInterfaceImplementation (iface, origin, reason); } } + protected internal virtual void MarkInterfaceImplementation (InterfaceImplementation iface, MessageOrigin? origin = null, DependencyInfo? reason = null) { if (Annotations.IsMarked (iface)) diff --git a/src/tools/illink/src/linker/Linker/Annotations.cs b/src/tools/illink/src/linker/Linker/Annotations.cs index 4b15bc6c87cf33..a7f77cf0efae90 100644 --- a/src/tools/illink/src/linker/Linker/Annotations.cs +++ b/src/tools/illink/src/linker/Linker/Annotations.cs @@ -718,7 +718,8 @@ public void EnqueueVirtualMethod (MethodDefinition method) if (FlowAnnotations.RequiresVirtualMethodDataFlowAnalysis (method) || HasLinkerAttribute (method)) VirtualMethodsWithAnnotationsToValidate.Add (method); } - internal List<(TypeReference, List)>? GetRecursiveInterfaces(TypeDefinition type) + + internal List<(TypeReference, List)>? GetRecursiveInterfaces (TypeDefinition type) { return TypeMapInfo.GetRecursiveInterfaces (type); } diff --git a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs index c037e7e8878ad7..26309c05bc9393 100644 --- a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs +++ b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs @@ -130,7 +130,7 @@ protected virtual void MapType (TypeDefinition type) MapType (nested); } - internal List<(TypeReference, List)>? GetRecursiveInterfaces(TypeDefinition type) + internal List<(TypeReference, List)>? GetRecursiveInterfaces (TypeDefinition type) { if (interfaces.TryGetValue (type, out var value)) return value; @@ -158,7 +158,7 @@ static void AddRecursiveInterfaces (TypeReference typeRef, IEnumerable InterfaceTypeEquals(i.Item1, directlyImplementedType, Context))) { + if (!firstImplementationChain.Any (i => InterfaceTypeEquals (i.Item1, directlyImplementedType, Context))) { firstImplementationChain.Add ((directlyImplementedType, pathToType.Append (directIface).ToList ())); } } @@ -166,7 +166,7 @@ static void AddRecursiveInterfaces (TypeReference typeRef, IEnumerable Date: Mon, 25 Mar 2024 17:35:50 -0700 Subject: [PATCH 7/8] PR feedback: rename vars, make non-null --- .../illink/src/linker/Linker/TypeMapInfo.cs | 21 +++++++++---------- .../linker/Linker/TypeReferenceExtensions.cs | 9 ++++---- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs index 26309c05bc9393..ed7a021723cf32 100644 --- a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs +++ b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs @@ -152,32 +152,31 @@ static void AddRecursiveInterfaces (TypeReference typeRef, IEnumerable InterfaceTypeEquals (i.Item1, directlyImplementedType, Context))) { - firstImplementationChain.Add ((directlyImplementedType, pathToType.Append (directIface).ToList ())); + if (!firstImplementationChain.Any (i => InterfaceTypeEquals (i.Item1, interfaceType, Context))) { + firstImplementationChain.Add ((interfaceType, pathToType.Append (iface).ToList ())); } } - // Recursive interfaces next to preserve Inherit/Implement tree order - foreach (var directIface in type.Interfaces) { + // Recursive interfaces after all direct interfaces to preserve Inherit/Implement tree order + foreach (var iface in type.Interfaces) { // If we can't resolve the interface type we can't find recursive interfaces - var ifaceDirectlyOnType = directIface.InterfaceType.TryInflateFrom (typeRef, Context); + var ifaceDirectlyOnType = iface.InterfaceType.TryInflateFrom (typeRef, Context); if (ifaceDirectlyOnType is null) { continue; } - AddRecursiveInterfaces (ifaceDirectlyOnType, pathToType.Append (directIface), firstImplementationChain, Context); + AddRecursiveInterfaces (ifaceDirectlyOnType, pathToType.Append (iface), firstImplementationChain, Context); } } /// /// Compares two TypeReferences to interface types and determines if they are equivalent references, taking into account generic arguments and element types. /// - static bool InterfaceTypeEquals (TypeReference? type, TypeReference? other, ITryResolveMetadata resolver) + static bool InterfaceTypeEquals (TypeReference type, TypeReference other, ITryResolveMetadata resolver) { Debug.Assert (type is not null && other is not null); Debug.Assert (resolver.TryResolve (type)?.IsInterface is null or true); diff --git a/src/tools/illink/src/linker/Linker/TypeReferenceExtensions.cs b/src/tools/illink/src/linker/Linker/TypeReferenceExtensions.cs index 46dbf96bd379f3..6189eae6d794cb 100644 --- a/src/tools/illink/src/linker/Linker/TypeReferenceExtensions.cs +++ b/src/tools/illink/src/linker/Linker/TypeReferenceExtensions.cs @@ -150,11 +150,12 @@ void parseArrayDimensions (ArrayType at) Debug.Assert (false); return null; } - public static TypeReference? TryInflateFrom (this TypeReference type, TypeReference maybeGenericInstanceProvider, ITryResolveMetadata resolver) + + public static TypeReference? TryInflateFrom (this TypeReference typeToInflate, TypeReference maybeGenericInstanceProvider, ITryResolveMetadata resolver) { - if (maybeGenericInstanceProvider is GenericInstanceType git) - return InflateGenericType (git, type, resolver); - return type; + if (maybeGenericInstanceProvider is GenericInstanceType genericInstanceProvider) + return InflateGenericType (genericInstanceProvider, typeToInflate, resolver); + return typeToInflate; } public static IEnumerable<(TypeReference InflatedInterface, InterfaceImplementation OriginalImpl)> GetInflatedInterfaces (this TypeReference typeRef, ITryResolveMetadata resolver) From cd9b55694ba9d7fa374e867782d78b8aecaf02bf Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Tue, 26 Mar 2024 14:59:42 -0700 Subject: [PATCH 8/8] Add Cecil internal Type and Method Reference comparers --- .../linker/Linker/MethodReferenceComparer.cs | 171 +++++++++++ .../src/linker/Linker/TypeComparisonMode.cs | 17 ++ .../illink/src/linker/Linker/TypeMapInfo.cs | 44 +-- .../Linker/TypeReferenceEqualityComparer.cs | 270 ++++++++++++++++++ 4 files changed, 459 insertions(+), 43 deletions(-) create mode 100644 src/tools/illink/src/linker/Linker/MethodReferenceComparer.cs create mode 100644 src/tools/illink/src/linker/Linker/TypeComparisonMode.cs create mode 100644 src/tools/illink/src/linker/Linker/TypeReferenceEqualityComparer.cs diff --git a/src/tools/illink/src/linker/Linker/MethodReferenceComparer.cs b/src/tools/illink/src/linker/Linker/MethodReferenceComparer.cs new file mode 100644 index 00000000000000..3f4fa06684a07a --- /dev/null +++ b/src/tools/illink/src/linker/Linker/MethodReferenceComparer.cs @@ -0,0 +1,171 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using Mono.Cecil; + +namespace Mono.Linker +{ + // Copied from https://github.com/jbevain/cecil/blob/master/Mono.Cecil/MethodReferenceComparer.cs + internal sealed class MethodReferenceComparer : EqualityComparer + { + // Initialized lazily for each thread + [ThreadStatic] + static List? xComparisonStack; + + [ThreadStatic] + static List? yComparisonStack; + + public readonly ITryResolveMetadata _resolver; + + public MethodReferenceComparer(ITryResolveMetadata resolver) + { + _resolver = resolver; + } + + public override bool Equals (MethodReference? x, MethodReference? y) + { + return AreEqual (x, y, _resolver); + } + + public override int GetHashCode (MethodReference obj) + { + return GetHashCodeFor (obj); + } + + public static bool AreEqual (MethodReference? x, MethodReference? y, ITryResolveMetadata resolver) + { + if (ReferenceEquals (x, y)) + return true; + + if (x is null ^ y is null) + return false; + + Debug.Assert (x is not null); + Debug.Assert (y is not null); + + if (x.HasThis != y.HasThis) + return false; + +#pragma warning disable RS0030 // MethodReference.HasParameters is banned - this code is copied from Cecil + if (x.HasParameters != y.HasParameters) + return false; +#pragma warning restore RS0030 + + if (x.HasGenericParameters != y.HasGenericParameters) + return false; + +#pragma warning disable RS0030 // MethodReference.HasParameters is banned - this code is copied from Cecil + if (x.Parameters.Count != y.Parameters.Count) + return false; +#pragma warning restore RS0030 + + if (x.Name != y.Name) + return false; + + if (!TypeReferenceEqualityComparer.AreEqual (x.DeclaringType, y.DeclaringType, resolver)) + return false; + + var xGeneric = x as GenericInstanceMethod; + var yGeneric = y as GenericInstanceMethod; + if (xGeneric != null || yGeneric != null) { + if (xGeneric == null || yGeneric == null) + return false; + + if (xGeneric.GenericArguments.Count != yGeneric.GenericArguments.Count) + return false; + + for (int i = 0; i < xGeneric.GenericArguments.Count; i++) + if (!TypeReferenceEqualityComparer.AreEqual (xGeneric.GenericArguments[i], yGeneric.GenericArguments[i], resolver)) + return false; + } + + var xResolved = resolver.TryResolve (x); + var yResolved = resolver.TryResolve (y); + + if (xResolved != yResolved) + return false; + + if (xResolved == null) { + // We couldn't resolve either method. In order for them to be equal, their parameter types _must_ match. But wait, there's a twist! + // There exists a situation where we might get into a recursive state: parameter type comparison might lead to comparing the same + // methods again if the parameter types are generic parameters whose owners are these methods. We guard against these by using a + // thread static list of all our comparisons carried out in the stack so far, and if we're in progress of comparing them already, + // we'll just say that they match. + + xComparisonStack ??= new List (); + + yComparisonStack ??= new List (); + + for (int i = 0; i < xComparisonStack.Count; i++) { + if (xComparisonStack[i] == x && yComparisonStack[i] == y) + return true; + } + + xComparisonStack.Add (x); + + try { + yComparisonStack.Add (y); + + try { +#pragma warning disable RS0030 // MethodReference.HasParameters is banned - this code is copied from Cecil + for (int i = 0; i < x.Parameters.Count; i++) { + if (!TypeReferenceEqualityComparer.AreEqual (x.Parameters[i].ParameterType, y.Parameters[i].ParameterType, resolver)) + return false; + } +#pragma warning restore RS0030 + } finally { + yComparisonStack.RemoveAt (yComparisonStack.Count - 1); + } + } finally { + xComparisonStack.RemoveAt (xComparisonStack.Count - 1); + } + } + + return true; + } + + public static bool AreSignaturesEqual (MethodReference x, MethodReference y, ITryResolveMetadata resolver, TypeComparisonMode comparisonMode = TypeComparisonMode.Exact) + { + if (x.HasThis != y.HasThis) + return false; + +#pragma warning disable RS0030 // MethodReference.HasParameters is banned - this code is copied from Cecil + if (x.Parameters.Count != y.Parameters.Count) + return false; +#pragma warning restore RS0030 + + if (x.GenericParameters.Count != y.GenericParameters.Count) + return false; + +#pragma warning disable RS0030 // MethodReference.HasParameters is banned - this code is copied from Cecil + for (var i = 0; i < x.Parameters.Count; i++) + if (!TypeReferenceEqualityComparer.AreEqual (x.Parameters[i].ParameterType, y.Parameters[i].ParameterType, resolver, comparisonMode)) + return false; +#pragma warning restore RS0030 + + if (!TypeReferenceEqualityComparer.AreEqual (x.ReturnType, y.ReturnType, resolver, comparisonMode)) + return false; + + return true; + } + + public static int GetHashCodeFor (MethodReference obj) + { + // a very good prime number + const int hashCodeMultiplier = 486187739; + + var genericInstanceMethod = obj as GenericInstanceMethod; + if (genericInstanceMethod != null) { + var hashCode = GetHashCodeFor (genericInstanceMethod.ElementMethod); + for (var i = 0; i < genericInstanceMethod.GenericArguments.Count; i++) + hashCode = hashCode * hashCodeMultiplier + TypeReferenceEqualityComparer.GetHashCodeFor (genericInstanceMethod.GenericArguments[i]); + return hashCode; + } + + return TypeReferenceEqualityComparer.GetHashCodeFor (obj.DeclaringType) * hashCodeMultiplier + obj.Name.GetHashCode (); + } + } +} diff --git a/src/tools/illink/src/linker/Linker/TypeComparisonMode.cs b/src/tools/illink/src/linker/Linker/TypeComparisonMode.cs new file mode 100644 index 00000000000000..e3f95bd148555e --- /dev/null +++ b/src/tools/illink/src/linker/Linker/TypeComparisonMode.cs @@ -0,0 +1,17 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Mono.Linker +{ + // Copied from https://github.com/jbevain/cecil/blob/master/Mono.Cecil/TypeComparisonMode.cs + internal enum TypeComparisonMode + { + Exact, + SignatureOnly, + + /// + /// Types can be in different assemblies, as long as the module, assembly, and type names match they will be considered equal + /// + SignatureOnlyLoose + } +} diff --git a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs index ed7a021723cf32..d2813c8982c4f0 100644 --- a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs +++ b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs @@ -157,7 +157,7 @@ static void AddRecursiveInterfaces (TypeReference typeRef, IEnumerable InterfaceTypeEquals (i.Item1, interfaceType, Context))) { + if (!firstImplementationChain.Any (i => TypeReferenceEqualityComparer.AreEqual (i.Item1, interfaceType, Context))) { firstImplementationChain.Add ((interfaceType, pathToType.Append (iface).ToList ())); } } @@ -172,48 +172,6 @@ static void AddRecursiveInterfaces (TypeReference typeRef, IEnumerable - /// Compares two TypeReferences to interface types and determines if they are equivalent references, taking into account generic arguments and element types. - /// - static bool InterfaceTypeEquals (TypeReference type, TypeReference other, ITryResolveMetadata resolver) - { - Debug.Assert (type is not null && other is not null); - Debug.Assert (resolver.TryResolve (type)?.IsInterface is null or true); - Debug.Assert (resolver.TryResolve (other)?.IsInterface is null or true); - return TypeEquals (type, other); - - bool TypeEquals (TypeReference type1, TypeReference type2) - { - if (type1 == type2) - return true; - - if (resolver.TryResolve (type1) != resolver.TryResolve (type2)) - return false; - - if (type1 is GenericInstanceType genericInstance1) { - if (type2 is not GenericInstanceType genericInstance2) - return false; - if (genericInstance1.HasGenericParameters != genericInstance2.HasGenericParameters) - return false; - if (genericInstance1.GenericParameters.Count != genericInstance2.GenericParameters.Count - || genericInstance2.GenericArguments.Count != genericInstance2.GenericArguments.Count) - return false; - for (var i = 0; i < genericInstance1.GenericArguments.Count; ++i) { - if (!TypeEquals (genericInstance1.GenericArguments[i], genericInstance2.GenericArguments[i])) - return false; - } - return true; - } - - if (type1 is TypeSpecification typeSpec1) { - if (type2 is not TypeSpecification typeSpec2) - return false; - return TypeEquals (typeSpec1.ElementType, typeSpec2.ElementType); - } - return type1.FullName == type2.FullName; - } - } } void MapInterfaceMethodsInTypeHierarchy (TypeDefinition type) diff --git a/src/tools/illink/src/linker/Linker/TypeReferenceEqualityComparer.cs b/src/tools/illink/src/linker/Linker/TypeReferenceEqualityComparer.cs new file mode 100644 index 00000000000000..ee0e0d48ba9bc1 --- /dev/null +++ b/src/tools/illink/src/linker/Linker/TypeReferenceEqualityComparer.cs @@ -0,0 +1,270 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using Mono.Cecil; + +namespace Mono.Linker +{ + // Copied from https://github.com/jbevain/cecil/blob/master/Mono.Cecil/TypeReferenceComparer.cs + internal sealed class TypeReferenceEqualityComparer : EqualityComparer + { + public readonly ITryResolveMetadata _resolver; + + public TypeReferenceEqualityComparer(ITryResolveMetadata resolver) + { + _resolver = resolver; + } + + public override bool Equals (TypeReference? x, TypeReference? y) + { + return AreEqual (x, y, _resolver); + } + + public override int GetHashCode (TypeReference obj) + { + return GetHashCodeFor (obj); + } + + public static bool AreEqual (TypeReference? a, TypeReference? b, ITryResolveMetadata resolver, TypeComparisonMode comparisonMode = TypeComparisonMode.Exact) + { + if (ReferenceEquals (a, b)) + return true; + + if (a == null || b == null) + return false; + + var aMetadataType = a.MetadataType; + var bMetadataType = b.MetadataType; + + if (aMetadataType == MetadataType.GenericInstance || bMetadataType == MetadataType.GenericInstance) { + if (aMetadataType != bMetadataType) + return false; + + return AreEqual ((GenericInstanceType) a, (GenericInstanceType) b, resolver, comparisonMode); + } + + if (aMetadataType == MetadataType.Array || bMetadataType == MetadataType.Array) { + if (aMetadataType != bMetadataType) + return false; + + var a1 = (ArrayType) a; + var b1 = (ArrayType) b; + if (a1.Rank != b1.Rank) + return false; + + return AreEqual (a1.ElementType, b1.ElementType, resolver, comparisonMode); + } + + if (aMetadataType == MetadataType.Var || bMetadataType == MetadataType.Var) { + if (aMetadataType != bMetadataType) + return false; + + return AreEqual ((GenericParameter) a, (GenericParameter) b, resolver, comparisonMode); + } + + if (aMetadataType == MetadataType.MVar || bMetadataType == MetadataType.MVar) { + if (aMetadataType != bMetadataType) + return false; + + return AreEqual ((GenericParameter) a, (GenericParameter) b, resolver, comparisonMode); + } + + if (aMetadataType == MetadataType.ByReference || bMetadataType == MetadataType.ByReference) { + if (aMetadataType != bMetadataType) + return false; + + return AreEqual (((ByReferenceType) a).ElementType, ((ByReferenceType) b).ElementType, resolver, comparisonMode); + } + + if (aMetadataType == MetadataType.Pointer || bMetadataType == MetadataType.Pointer) { + if (aMetadataType != bMetadataType) + return false; + + return AreEqual (((PointerType) a).ElementType, ((PointerType) b).ElementType, resolver, comparisonMode); + } + + if (aMetadataType == MetadataType.RequiredModifier || bMetadataType == MetadataType.RequiredModifier) { + if (aMetadataType != bMetadataType) + return false; + + var a1 = (RequiredModifierType) a; + var b1 = (RequiredModifierType) b; + + return AreEqual (a1.ModifierType, b1.ModifierType, resolver, comparisonMode) && AreEqual (a1.ElementType, b1.ElementType, resolver, comparisonMode); + } + + if (aMetadataType == MetadataType.OptionalModifier || bMetadataType == MetadataType.OptionalModifier) { + if (aMetadataType != bMetadataType) + return false; + + var a1 = (OptionalModifierType) a; + var b1 = (OptionalModifierType) b; + + return AreEqual (a1.ModifierType, b1.ModifierType, resolver, comparisonMode) && AreEqual (a1.ElementType, b1.ElementType, resolver, comparisonMode); + } + + if (aMetadataType == MetadataType.Pinned || bMetadataType == MetadataType.Pinned) { + if (aMetadataType != bMetadataType) + return false; + + return AreEqual (((PinnedType) a).ElementType, ((PinnedType) b).ElementType, resolver, comparisonMode); + } + + if (aMetadataType == MetadataType.Sentinel || bMetadataType == MetadataType.Sentinel) { + if (aMetadataType != bMetadataType) + return false; + + return AreEqual (((SentinelType) a).ElementType, ((SentinelType) b).ElementType, resolver, comparisonMode); + } + + if (!a.Name.Equals (b.Name) || !a.Namespace.Equals (b.Namespace)) + return false; + + var xDefinition = resolver.TryResolve (a); + var yDefinition = resolver.TryResolve (b); + if (xDefinition == null || yDefinition == null) + return false; + + // For loose signature the types could be in different assemblies, as long as the type names match we will consider them equal + if (comparisonMode == TypeComparisonMode.SignatureOnlyLoose) { + if (xDefinition.Module.Name != yDefinition.Module.Name) + return false; + + if (xDefinition.Module.Assembly.Name.Name != yDefinition.Module.Assembly.Name.Name) + return false; + + return xDefinition.FullName == yDefinition.FullName; + } + + return xDefinition == yDefinition; + } + + static bool AreEqual (GenericParameter a, GenericParameter b, ITryResolveMetadata resolver, TypeComparisonMode comparisonMode = TypeComparisonMode.Exact) + { + if (ReferenceEquals (a, b)) + return true; + + if (a.Position != b.Position) + return false; + + if (a.Type != b.Type) + return false; + + var aOwnerType = a.Owner as TypeReference; + if (aOwnerType != null && AreEqual (aOwnerType, b.Owner as TypeReference, resolver, comparisonMode)) + return true; + + var aOwnerMethod = a.Owner as MethodReference; + if (aOwnerMethod != null && comparisonMode != TypeComparisonMode.SignatureOnlyLoose && MethodReferenceComparer.AreEqual (aOwnerMethod, b.Owner as MethodReference, resolver)) + return true; + + return comparisonMode == TypeComparisonMode.SignatureOnly || comparisonMode == TypeComparisonMode.SignatureOnlyLoose; + } + + static bool AreEqual (GenericInstanceType a, GenericInstanceType b, ITryResolveMetadata resolver, TypeComparisonMode comparisonMode = TypeComparisonMode.Exact) + { + if (ReferenceEquals (a, b)) + return true; + + var aGenericArgumentsCount = a.GenericArguments.Count; + if (aGenericArgumentsCount != b.GenericArguments.Count) + return false; + + if (!AreEqual (a.ElementType, b.ElementType, resolver, comparisonMode)) + return false; + + for (int i = 0; i < aGenericArgumentsCount; i++) + if (!AreEqual (a.GenericArguments[i], b.GenericArguments[i], resolver, comparisonMode)) + return false; + + return true; + } + + public static int GetHashCodeFor (TypeReference obj) + { + // a very good prime number + const int hashCodeMultiplier = 486187739; + // prime numbers + const int genericInstanceTypeMultiplier = 31; + const int byReferenceMultiplier = 37; + const int pointerMultiplier = 41; + const int requiredModifierMultiplier = 43; + const int optionalModifierMultiplier = 47; + const int pinnedMultiplier = 53; + const int sentinelMultiplier = 59; + + var metadataType = obj.MetadataType; + + if (metadataType == MetadataType.GenericInstance) { + var genericInstanceType = (GenericInstanceType) obj; + var hashCode = GetHashCodeFor (genericInstanceType.ElementType) * hashCodeMultiplier + genericInstanceTypeMultiplier; + for (var i = 0; i < genericInstanceType.GenericArguments.Count; i++) + hashCode = hashCode * hashCodeMultiplier + GetHashCodeFor (genericInstanceType.GenericArguments[i]); + return hashCode; + } + + if (metadataType == MetadataType.Array) { + var arrayType = (ArrayType) obj; + return GetHashCodeFor (arrayType.ElementType) * hashCodeMultiplier + arrayType.Rank.GetHashCode (); + } + + if (metadataType == MetadataType.Var || metadataType == MetadataType.MVar) { + var genericParameter = (GenericParameter) obj; + var hashCode = genericParameter.Position.GetHashCode () * hashCodeMultiplier + ((int) metadataType).GetHashCode (); + + var ownerTypeReference = genericParameter.Owner as TypeReference; + if (ownerTypeReference != null) + return hashCode * hashCodeMultiplier + GetHashCodeFor (ownerTypeReference); + + var ownerMethodReference = genericParameter.Owner as MethodReference; + if (ownerMethodReference != null) + return hashCode * hashCodeMultiplier + MethodReferenceComparer.GetHashCodeFor (ownerMethodReference); + + throw new InvalidOperationException ("Generic parameter encountered with invalid owner"); + } + + if (metadataType == MetadataType.ByReference) { + var byReferenceType = (ByReferenceType) obj; + return GetHashCodeFor (byReferenceType.ElementType) * hashCodeMultiplier * byReferenceMultiplier; + } + + if (metadataType == MetadataType.Pointer) { + var pointerType = (PointerType) obj; + return GetHashCodeFor (pointerType.ElementType) * hashCodeMultiplier * pointerMultiplier; + } + + if (metadataType == MetadataType.RequiredModifier) { + var requiredModifierType = (RequiredModifierType) obj; + var hashCode = GetHashCodeFor (requiredModifierType.ElementType) * requiredModifierMultiplier; + hashCode = hashCode * hashCodeMultiplier + GetHashCodeFor (requiredModifierType.ModifierType); + return hashCode; + } + + if (metadataType == MetadataType.OptionalModifier) { + var optionalModifierType = (OptionalModifierType) obj; + var hashCode = GetHashCodeFor (optionalModifierType.ElementType) * optionalModifierMultiplier; + hashCode = hashCode * hashCodeMultiplier + GetHashCodeFor (optionalModifierType.ModifierType); + return hashCode; + } + + if (metadataType == MetadataType.Pinned) { + var pinnedType = (PinnedType) obj; + return GetHashCodeFor (pinnedType.ElementType) * hashCodeMultiplier * pinnedMultiplier; + } + + if (metadataType == MetadataType.Sentinel) { + var sentinelType = (SentinelType) obj; + return GetHashCodeFor (sentinelType.ElementType) * hashCodeMultiplier * sentinelMultiplier; + } + + if (metadataType == MetadataType.FunctionPointer) { + throw new NotImplementedException ("We currently don't handle function pointer types."); + } + + return obj.Namespace.GetHashCode () * hashCodeMultiplier + obj.FullName.GetHashCode (); + } + } +}