diff options
26 files changed, 807 insertions, 4 deletions
diff --git a/linker/Linker.Steps/MarkStep.cs b/linker/Linker.Steps/MarkStep.cs index fb06bc70c..a42c5facd 100644 --- a/linker/Linker.Steps/MarkStep.cs +++ b/linker/Linker.Steps/MarkStep.cs @@ -266,7 +266,7 @@ namespace Mono.Linker.Steps { // We don't need to mark overrides until it is possible that the type could be instantiated // Note : The base type is interface check should be removed once we have base type sweeping - if (!isInstantiated && @base.DeclaringType.IsInterface) + if (@base.DeclaringType.IsInterface && !isInstantiated && !IsInterfaceImplementationMarked (method.DeclaringType, @base.DeclaringType)) return; if (!isInstantiated && !@base.IsAbstract) @@ -276,6 +276,11 @@ namespace Mono.Linker.Steps { ProcessVirtualMethod (method); } + bool IsInterfaceImplementationMarked (TypeDefinition type, TypeDefinition interfaceType) + { + return type.HasInterface (@interfaceType, out InterfaceImplementation implementation) && Annotations.IsMarked (implementation); + } + void MarkMarshalSpec (IMarshalInfoProvider spec) { if (!spec.HasMarshalInfo) @@ -2004,6 +2009,8 @@ namespace Mono.Linker.Steps { foreach (Instruction instruction in body.Instructions) MarkInstruction (instruction); + MarkInterfacesNeededByBodyStack (body); + MarkThingsUsedViaReflection (body); PostMarkMethodBody (body); @@ -2011,6 +2018,19 @@ namespace Mono.Linker.Steps { partial void PostMarkMethodBody (MethodBody body); + void MarkInterfacesNeededByBodyStack (MethodBody body) + { + // If a type could be on the stack in the body and an interface it implements could be on the stack on the body + // then we need to mark that interface implementation. When this occurs it is not safe to remove the interface implementation from the type + // even if the type is never instantiated + var implementations = MethodBodyScanner.GetReferencedInterfaces (_context.Annotations, body); + if (implementations == null) + return; + + foreach (var implementation in implementations) + MarkInterfaceImplementation (implementation); + } + protected virtual void MarkThingsUsedViaReflection (MethodBody body) { MarkSomethingUsedViaReflection ("GetConstructor", MarkConstructorsUsedViaReflection, body.Instructions); diff --git a/linker/Linker.Steps/TypeMapStep.cs b/linker/Linker.Steps/TypeMapStep.cs index 44e882330..4189e65a8 100644 --- a/linker/Linker.Steps/TypeMapStep.cs +++ b/linker/Linker.Steps/TypeMapStep.cs @@ -44,6 +44,7 @@ namespace Mono.Linker.Steps { { MapVirtualMethods (type); MapInterfaceMethodsInTypeHierarchy (type); + MapBaseTypeHierarchy (type); if (!type.HasNestedTypes) return; @@ -123,6 +124,30 @@ namespace Mono.Linker.Steps { } } + void MapBaseTypeHierarchy (TypeDefinition type) + { + if (!type.IsClass) + return; + + var bases = new List<TypeDefinition> (); + var current = type.BaseType; + + while (current != null) { + var resolved = current.Resolve (); + if (resolved == null) + break; + + // Exclude Object. That's implied and adding it to the list will just lead to lots of extra unnecessary processing + if (resolved.BaseType == null) + break; + + bases.Add (resolved); + current = resolved.BaseType; + } + + Annotations.SetClassHierarchy (type, bases); + } + void AnnotateMethods (MethodDefinition @base, MethodDefinition @override) { Annotations.AddBaseMethod (@override, @base); diff --git a/linker/Linker/Annotations.cs b/linker/Linker/Annotations.cs index 80b90e0b5..488ce9cc4 100644 --- a/linker/Linker/Annotations.cs +++ b/linker/Linker/Annotations.cs @@ -48,6 +48,7 @@ namespace Mono.Linker { protected readonly Dictionary<MethodDefinition, List<MethodDefinition>> override_methods = new Dictionary<MethodDefinition, List<MethodDefinition>> (); protected readonly Dictionary<MethodDefinition, List<MethodDefinition>> base_methods = new Dictionary<MethodDefinition, List<MethodDefinition>> (); protected readonly Dictionary<AssemblyDefinition, ISymbolReader> symbol_readers = new Dictionary<AssemblyDefinition, ISymbolReader> (); + protected readonly Dictionary<TypeDefinition, List<TypeDefinition>> class_type_base_hierarchy = new Dictionary<TypeDefinition, List<TypeDefinition>> (); protected readonly Dictionary<object, Dictionary<IMetadataTokenProvider, object>> custom_annotations = new Dictionary<object, Dictionary<IMetadataTokenProvider, object>> (); protected readonly Dictionary<AssemblyDefinition, HashSet<string>> resources_to_remove = new Dictionary<AssemblyDefinition, HashSet<string>> (); @@ -355,5 +356,17 @@ namespace Mono.Linker { return marked_types_with_cctor.Add (type); } + public void SetClassHierarchy (TypeDefinition type, List<TypeDefinition> bases) + { + class_type_base_hierarchy [type] = bases; + } + + public List<TypeDefinition> GetClassHierarchy (TypeDefinition type) + { + if (class_type_base_hierarchy.TryGetValue (type, out List<TypeDefinition> bases)) + return bases; + + return null; + } } } diff --git a/linker/Linker/MethodBodyScanner.cs b/linker/Linker/MethodBodyScanner.cs new file mode 100644 index 000000000..f875ad162 --- /dev/null +++ b/linker/Linker/MethodBodyScanner.cs @@ -0,0 +1,119 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Mono.Cecil; +using Mono.Cecil.Cil; + +namespace Mono.Linker { + public static class MethodBodyScanner { + public static IEnumerable<InterfaceImplementation> GetReferencedInterfaces (AnnotationStore annotations, MethodBody body) + { + var possibleStackTypes = AllPossibleStackTypes (body.Method); + if (possibleStackTypes.Count == 0) + return null; + + var interfaceTypes = possibleStackTypes.Where (t => t.IsInterface).ToArray (); + if (interfaceTypes.Length == 0) + return null; + + var interfaceImplementations = new HashSet<InterfaceImplementation> (); + + // If a type could be on the stack in the body and an interface it implements could be on the stack on the body + // then we need to mark that interface implementation. When this occurs it is not safe to remove the interface implementation from the type + // even if the type is never instantiated + foreach (var type in possibleStackTypes) { + // We only sweep interfaces on classes so that's why we only care about classes + if (!type.IsClass) + continue; + + AddMatchingInterfaces (interfaceImplementations, type, interfaceTypes); + var bases = annotations.GetClassHierarchy (type); + foreach (var @base in bases) { + AddMatchingInterfaces (interfaceImplementations, @base, interfaceTypes); + } + } + + return interfaceImplementations; + } + + static HashSet<TypeDefinition> AllPossibleStackTypes (MethodDefinition method) + { + if (!method.HasBody) + throw new ArgumentException(); + + var body = method.Body; + var types = new HashSet<TypeDefinition> (); + + foreach (VariableDefinition var in body.Variables) + AddIfResolved (types, var.VariableType); + + foreach (ExceptionHandler eh in body.ExceptionHandlers) { + if (eh.HandlerType == ExceptionHandlerType.Catch) { + AddIfResolved (types, eh.CatchType); + } + } + + foreach (Instruction instruction in body.Instructions) { + if (instruction.Operand is FieldReference fieldReference) { + AddIfResolved (types, fieldReference.Resolve ()?.FieldType); + } else if (instruction.Operand is MethodReference methodReference) { + if (methodReference is GenericInstanceMethod genericInstanceMethod) + AddFromGenericInstance (types, genericInstanceMethod); + + if (methodReference.DeclaringType is GenericInstanceType genericInstanceType) + AddFromGenericInstance (types, genericInstanceType); + + var resolvedMethod = methodReference.Resolve (); + if (resolvedMethod != null) { + if (resolvedMethod.HasParameters) { + foreach (var param in resolvedMethod.Parameters) + AddIfResolved (types, param.ParameterType); + } + + AddFromGenericParameterProvider (types, resolvedMethod); + AddFromGenericParameterProvider (types, resolvedMethod.DeclaringType); + AddIfResolved (types, resolvedMethod.ReturnType); + } + } + } + + return types; + } + + static void AddMatchingInterfaces (HashSet<InterfaceImplementation> results, TypeDefinition type, TypeDefinition [] interfaceTypes) + { + foreach (var interfaceType in interfaceTypes) { + if (type.HasInterface (interfaceType, out InterfaceImplementation implementation)) + results.Add (implementation); + } + } + + static void AddFromGenericInstance (HashSet<TypeDefinition> set, IGenericInstance instance) + { + if (!instance.HasGenericArguments) + return; + + foreach (var genericArgument in instance.GenericArguments) + AddIfResolved (set, genericArgument); + } + + static void AddFromGenericParameterProvider (HashSet<TypeDefinition> set, IGenericParameterProvider provider) + { + if (!provider.HasGenericParameters) + return; + + foreach (var genericParameter in provider.GenericParameters) { + foreach (var constraint in genericParameter.Constraints) + AddIfResolved (set, constraint); + } + } + + static void AddIfResolved (HashSet<TypeDefinition> set, TypeReference item) + { + var resolved = item.Resolve (); + if (resolved == null) + return; + set.Add (resolved); + } + } +}
\ No newline at end of file diff --git a/linker/Linker/TypeDefinitionExtensions.cs b/linker/Linker/TypeDefinitionExtensions.cs new file mode 100644 index 000000000..f94e340c3 --- /dev/null +++ b/linker/Linker/TypeDefinitionExtensions.cs @@ -0,0 +1,21 @@ +using Mono.Cecil; + +namespace Mono.Linker { + public static class TypeDefinitionExtensions { + public static bool HasInterface (this TypeDefinition type, TypeDefinition interfaceType, out InterfaceImplementation implementation) + { + implementation = null; + if (!type.HasInterfaces) + return false; + + foreach (var iface in type.Interfaces) { + if (iface.InterfaceType.Resolve () == interfaceType) { + implementation = iface; + return true; + } + } + + return false; + } + } +}
\ No newline at end of file diff --git a/linker/Mono.Linker.csproj b/linker/Mono.Linker.csproj index cd547ca51..2b82f3838 100644 --- a/linker/Mono.Linker.csproj +++ b/linker/Mono.Linker.csproj @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="utf-8"?> +<?xml version="1.0" encoding="utf-8"?> <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="14.0"> <!-- TODO: Remove this workaround once supported. @@ -65,6 +65,7 @@ <Compile Include="Linker.Steps\RegenerateGuidStep.cs" /> <Compile Include="Linker.Steps\LoadI18nAssemblies.cs" /> <Compile Include="Linker.Steps\RemoveSecurityStep.cs" /> + <Compile Include="Linker\MethodBodyScanner.cs" /> <Compile Include="Linker\IXApiVisitor.cs" /> <Compile Include="Linker\I18nAssemblies.cs" /> <Compile Include="Linker.Steps\IStep.cs" /> @@ -91,6 +92,7 @@ <Compile Include="Linker\MethodAction.cs" /> <Compile Include="Linker\MethodReferenceExtensions.cs" /> <Compile Include="Linker\Pipeline.cs" /> + <Compile Include="Linker\TypeDefinitionExtensions.cs" /> <Compile Include="Linker\TypePreserve.cs" /> <Compile Include="Linker\TypeReferenceExtensions.cs" /> <Compile Include="Linker\TypeNameParser.cs" /> @@ -133,4 +135,4 @@ <Name>Mono.Cecil.Pdb</Name> </ProjectReference> </ItemGroup> -</Project> +</Project>
\ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Inheritance.AbstractClasses/NoKeptCtor/OverrideRemoval/OverrideThatAlsoFulfilsInterface.cs b/linker/Tests/Mono.Linker.Tests.Cases/Inheritance.AbstractClasses/NoKeptCtor/OverrideRemoval/OverrideThatAlsoFulfilsInterface.cs index c909a70e6..428310dad 100644 --- a/linker/Tests/Mono.Linker.Tests.Cases/Inheritance.AbstractClasses/NoKeptCtor/OverrideRemoval/OverrideThatAlsoFulfilsInterface.cs +++ b/linker/Tests/Mono.Linker.Tests.Cases/Inheritance.AbstractClasses/NoKeptCtor/OverrideRemoval/OverrideThatAlsoFulfilsInterface.cs @@ -6,7 +6,13 @@ namespace Mono.Linker.Tests.Cases.Inheritance.AbstractClasses.NoKeptCtor.Overrid { Base b = HelperToMarkFooAndRequireBase (); b.Method (); - + + MethodToUseTheInterface (); + } + + [Kept] + static void MethodToUseTheInterface () + { // Now use the interface method so that it is kept IFoo f = new Bar (); f.Method (); diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtor/GenericType.cs b/linker/Tests/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtor/GenericType.cs new file mode 100644 index 000000000..b6dff2fde --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtor/GenericType.cs @@ -0,0 +1,22 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.OnReferenceType.NoKeptCtor { + public class GenericType { + public static void Main () + { + object o = new Bar<Foo> (); + } + + [Kept] + [KeptMember(".ctor()")] + class Bar<T> { + } + + [Kept] + class Foo : IFoo { + } + + interface IFoo { + } + } +}
\ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtor/GenericWithConstraintDoesNotCauseOtherTypesToKeepInterface.cs b/linker/Tests/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtor/GenericWithConstraintDoesNotCauseOtherTypesToKeepInterface.cs new file mode 100644 index 000000000..7255cac2c --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtor/GenericWithConstraintDoesNotCauseOtherTypesToKeepInterface.cs @@ -0,0 +1,41 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.OnReferenceType.NoKeptCtor { + public class GenericWithConstraintDoesNotCauseOtherTypesToKeepInterface { + public static void Main () + { + Foo f = null; + Helper (f); + OtherMethodToDoStuff (); + } + + [Kept] + static void Helper<T> (T f) where T : IFoo + { + } + + [Kept] + static void OtherMethodToDoStuff () + { + HelperToUseFoo2 (null); + } + + [Kept] + static void HelperToUseFoo2 (Foo2 f) + { + } + + [Kept] + [KeptInterface (typeof (IFoo))] + class Foo : IFoo { + } + + [Kept] + class Foo2 : IFoo { + } + + [Kept] + interface IFoo { + } + } +}
\ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtor/LocalDowncastDoesNotCuaseOtherTypesToKeepInterface.cs b/linker/Tests/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtor/LocalDowncastDoesNotCuaseOtherTypesToKeepInterface.cs new file mode 100644 index 000000000..00ad89fd2 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtor/LocalDowncastDoesNotCuaseOtherTypesToKeepInterface.cs @@ -0,0 +1,47 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.OnReferenceType.NoKeptCtor { + public class LocalDowncastDoesNotCuaseOtherTypesToKeepInterface { + public static void Main () + { + Foo f = null; + IFoo i = f; + i.Method (); + DoOtherStuff (); + } + + [Kept] + static void DoOtherStuff () + { + HelperToUseBar (null); + } + + [Kept] + static void HelperToUseBar (Bar arg) + { + } + + [Kept] + [KeptInterface (typeof (IFoo))] + class Foo : IFoo { + [Kept] // TODO : It should be safe to stub this. It can't actually be called because no instance of Foo ever exists + public void Method () + { + } + } + + [Kept] + interface IFoo { + [Kept] + void Method (); + } + + [Kept] + class Bar : IFoo + { + public void Method () + { + } + } + } +}
\ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtor/ObjectHardCastedToInterface.cs b/linker/Tests/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtor/ObjectHardCastedToInterface.cs new file mode 100644 index 000000000..84b9b6c16 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtor/ObjectHardCastedToInterface.cs @@ -0,0 +1,40 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.OnReferenceType.NoKeptCtor { + public class ObjectHardCastedToInterface { + public static void Main () + { + object o = GetAnObject (); + IFoo i = (IFoo)o; + UseAnIFoo (i); + + // Here to mark Foo so that we can verify the interface is removed + Foo.Helper (); + } + + [Kept] + static object GetAnObject () + { + return null; + } + + [Kept] + static void UseAnIFoo (IFoo arg) + { + } + + [Kept] + class Foo : IFoo + { + [Kept] + public static void Helper () + { + } + } + + [Kept] + interface IFoo + { + } + } +}
\ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/ArrayPassedAsParameter.cs b/linker/Tests/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/ArrayPassedAsParameter.cs new file mode 100644 index 000000000..0a9aacc0c --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/ArrayPassedAsParameter.cs @@ -0,0 +1,25 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.OnReferenceType.NoKeptCtorButInterfaceNeeded { + public class LocalArrayPassedAsParameter { + public static void Main () + { + Foo [] arr = null; + Helper (arr); + } + + [Kept] + static void Helper (IFoo[] f) + { + } + + [Kept] + [KeptInterface (typeof (IFoo))] + class Foo : IFoo { + } + + [Kept] + interface IFoo { + } + } +}
\ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/ArrayWithIndexAssignedToReturnValue.cs b/linker/Tests/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/ArrayWithIndexAssignedToReturnValue.cs new file mode 100644 index 000000000..179b7577b --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/ArrayWithIndexAssignedToReturnValue.cs @@ -0,0 +1,26 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.OnReferenceType.NoKeptCtorButInterfaceNeeded { + public class ArrayWithIndexAssignedToReturnValue { + public static void Main () + { + IFoo [] arr = new IFoo [5]; + arr [0] = GetAFoo (); + } + + [Kept] + static Foo GetAFoo () + { + return null; + } + + [Kept] + [KeptInterface (typeof (IFoo))] + class Foo : IFoo { + } + + [Kept] + interface IFoo { + } + } +}
\ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/FieldDowncastedToInterface.cs b/linker/Tests/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/FieldDowncastedToInterface.cs new file mode 100644 index 000000000..84ff1a1c7 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/FieldDowncastedToInterface.cs @@ -0,0 +1,32 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.OnReferenceType.NoKeptCtorButInterfaceNeeded { + public class FieldDowncastedToInterface { + [Kept] + private static Foo f; + [Kept] + private static IFoo i; + + public static void Main () + { + f = null; + i = f; + i.Method (); + } + + [Kept] + [KeptInterface (typeof (IFoo))] + class Foo : IFoo { + [Kept] // TODO : It should be safe to stub this. It can't actually be called because no instance of Foo ever exists + public void Method () + { + } + } + + [Kept] + interface IFoo { + [Kept] + void Method (); + } + } +}
\ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/GenericType.cs b/linker/Tests/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/GenericType.cs new file mode 100644 index 000000000..7e8e8ffa9 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/GenericType.cs @@ -0,0 +1,30 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.OnReferenceType.NoKeptCtorButInterfaceNeeded { + public class GenericType { + public static void Main () + { + Foo f = null; + Bar<IFoo> o = new Bar<IFoo> (); + o.Method (f); + } + + [Kept] + [KeptMember(".ctor()")] + class Bar<T> { + [Kept] + public void Method (T arg) + { + } + } + + [Kept] + [KeptInterface (typeof (IFoo))] + class Foo : IFoo { + } + + [Kept] + interface IFoo { + } + } +}
\ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/GenericTypeWithConstraint.cs b/linker/Tests/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/GenericTypeWithConstraint.cs new file mode 100644 index 000000000..48051732a --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/GenericTypeWithConstraint.cs @@ -0,0 +1,24 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.OnReferenceType.NoKeptCtorButInterfaceNeeded { + public class GenericTypeWithConstraint { + public static void Main () + { + object o = new Bar<Foo> (); + } + + [Kept] + [KeptMember(".ctor()")] + class Bar<T> where T : IFoo { + } + + [Kept] + [KeptInterface (typeof (IFoo))] + class Foo : IFoo { + } + + [Kept] + interface IFoo { + } + } +}
\ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/GenericTypeWithConstraint2.cs b/linker/Tests/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/GenericTypeWithConstraint2.cs new file mode 100644 index 000000000..0fb889ae8 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/GenericTypeWithConstraint2.cs @@ -0,0 +1,28 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.OnReferenceType.NoKeptCtorButInterfaceNeeded { + public class GenericTypeWithConstraint2 { + public static void Main () + { + Foo f = null; + Bar<Foo>.Helper (f); + } + + [Kept] + static class Bar<T> where T : IFoo { + [Kept] + public static void Helper (T arg) + { + } + } + + [Kept] + [KeptInterface (typeof (IFoo))] + class Foo : IFoo { + } + + [Kept] + interface IFoo { + } + } +}
\ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/GenericTypeWithConstraint3.cs b/linker/Tests/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/GenericTypeWithConstraint3.cs new file mode 100644 index 000000000..bc170287e --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/GenericTypeWithConstraint3.cs @@ -0,0 +1,58 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.OnReferenceType.NoKeptCtorButInterfaceNeeded { + public class GenericTypeWithConstraint3 { + public static void Main () + { + Foo f = null; + Bar b = null; + Bar<BaseFoo2, BaseBar2>.Helper (f, b); + } + + [Kept] + static class Bar<T, K> where T : IFoo where K : IBar { + [Kept] + public static void Helper (T arg, K arg2) + { + } + } + + [Kept] + [KeptInterface (typeof (IFoo))] + abstract class BaseFoo : IFoo { + } + + [Kept] + [KeptBaseType (typeof (BaseFoo))] + abstract class BaseFoo2 : BaseFoo { + } + + [Kept] + [KeptInterface (typeof (IBar))] + abstract class BaseBar : IBar { + } + + [Kept] + [KeptBaseType (typeof (BaseBar))] + abstract class BaseBar2 : BaseBar { + } + + [Kept] + [KeptBaseType (typeof (BaseFoo2))] + class Foo : BaseFoo2 { + } + + [Kept] + [KeptBaseType (typeof (BaseBar2))] + class Bar : BaseBar2 { + } + + [Kept] + interface IFoo { + } + + [Kept] + interface IBar { + } + } +}
\ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/InterfaceOnMultipleBases.cs b/linker/Tests/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/InterfaceOnMultipleBases.cs new file mode 100644 index 000000000..6b303e24f --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/InterfaceOnMultipleBases.cs @@ -0,0 +1,42 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.OnReferenceType.NoKeptCtorButInterfaceNeeded { + public class InterfaceOnMultipleBases { + public static void Main () + { + Foo f = null; + Helper (f); + } + + [Kept] + static void Helper (IFoo f) + { + } + + [Kept] + [KeptInterface (typeof (IFoo))] + class Base : IFoo { + } + + [Kept] + [KeptBaseType (typeof (Base))] + [KeptInterface (typeof (IFoo))] + class Base2 : Base, IFoo { + } + + [Kept] + [KeptBaseType (typeof (Base2))] + class Base3 : Base2 { + } + + [Kept] + [KeptBaseType (typeof (Base3))] + [KeptInterface (typeof (IFoo))] + class Foo : Base3, IFoo { + } + + [Kept] + interface IFoo { + } + } +}
\ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/LocalDowncastedToInterface.cs b/linker/Tests/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/LocalDowncastedToInterface.cs new file mode 100644 index 000000000..dc63d99a3 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/LocalDowncastedToInterface.cs @@ -0,0 +1,27 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.OnReferenceType.NoKeptCtorButInterfaceNeeded { + public class LocalDowncastedToInterface { + public static void Main () + { + Foo f = null; + IFoo i = f; + i.Method (); + } + + [Kept] + [KeptInterface (typeof (IFoo))] + class Foo : IFoo { + [Kept] // TODO : It should be safe to stub this. It can't actually be called because no instance of Foo ever exists + public void Method () + { + } + } + + [Kept] + interface IFoo { + [Kept] + void Method (); + } + } +}
\ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/LocalPassedAsParameter.cs b/linker/Tests/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/LocalPassedAsParameter.cs new file mode 100644 index 000000000..5338cc6ba --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/LocalPassedAsParameter.cs @@ -0,0 +1,25 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.OnReferenceType.NoKeptCtorButInterfaceNeeded { + public class LocalPassedAsParameter { + public static void Main () + { + Foo f = null; + Helper (f); + } + + [Kept] + static void Helper (IFoo f) + { + } + + [Kept] + [KeptInterface (typeof (IFoo))] + class Foo : IFoo { + } + + [Kept] + interface IFoo { + } + } +}
\ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/LocalPassedAsParameterToGeneric.cs b/linker/Tests/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/LocalPassedAsParameterToGeneric.cs new file mode 100644 index 000000000..475ac098a --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/LocalPassedAsParameterToGeneric.cs @@ -0,0 +1,25 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.OnReferenceType.NoKeptCtorButInterfaceNeeded { + public class LocalPassedAsParameterToGeneric { + public static void Main () + { + Foo f = null; + Helper<IFoo>(f); + } + + [Kept] + static void Helper <T> (T f) + { + } + + [Kept] + [KeptInterface (typeof (IFoo))] + class Foo : IFoo { + } + + [Kept] + interface IFoo { + } + } +}
\ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/LocalPassedAsParameterToGenericWithConstraint.cs b/linker/Tests/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/LocalPassedAsParameterToGenericWithConstraint.cs new file mode 100644 index 000000000..e00229264 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/LocalPassedAsParameterToGenericWithConstraint.cs @@ -0,0 +1,25 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.OnReferenceType.NoKeptCtorButInterfaceNeeded { + public class LocalPassedAsParameterToGenericWithConstraint { + public static void Main () + { + Foo f = null; + Helper (f); + } + + [Kept] + static void Helper<T> (T f) where T : IFoo + { + } + + [Kept] + [KeptInterface (typeof (IFoo))] + class Foo : IFoo { + } + + [Kept] + interface IFoo { + } + } +}
\ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/LocalPassedAsParameterToGenericWithConstraint2.cs b/linker/Tests/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/LocalPassedAsParameterToGenericWithConstraint2.cs new file mode 100644 index 000000000..cecf742e4 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/LocalPassedAsParameterToGenericWithConstraint2.cs @@ -0,0 +1,32 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.OnReferenceType.NoKeptCtorButInterfaceNeeded { + public class LocalPassedAsParameterToGenericWithConstraint2 { + public static void Main () + { + Foo f = null; + Foo2 f2 = null; + Helper (f, f2); + } + + [Kept] + static void Helper<T> (T f, Foo2 arg2) where T : IFoo + { + } + + [Kept] + [KeptInterface (typeof (IFoo))] + class Foo : IFoo { + } + + [Kept] + [KeptInterface (typeof (IFoo))] // technically this can be removed, but it would require more complex knowledge of the stack to do so + class Foo2 : IFoo { + } + + + [Kept] + interface IFoo { + } + } +}
\ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/ReturnValueDowncastedToInterface.cs b/linker/Tests/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/ReturnValueDowncastedToInterface.cs new file mode 100644 index 000000000..0aef967a5 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/ReturnValueDowncastedToInterface.cs @@ -0,0 +1,30 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.OnReferenceType.NoKeptCtorButInterfaceNeeded { + public class ReturnValueDowncastedToInterface { + public static void Main () + { + UseAnIFoo (GetAFoo ()); + } + + [Kept] + static Foo GetAFoo () + { + return null; + } + + [Kept] + static void UseAnIFoo (IFoo arg) + { + } + + [Kept] + [KeptInterface (typeof (IFoo))] + class Foo : IFoo { + } + + [Kept] + interface IFoo { + } + } +}
\ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Mono.Linker.Tests.Cases.csproj b/linker/Tests/Mono.Linker.Tests.Cases/Mono.Linker.Tests.Cases.csproj index 70d0a755d..27c913108 100644 --- a/linker/Tests/Mono.Linker.Tests.Cases/Mono.Linker.Tests.Cases.csproj +++ b/linker/Tests/Mono.Linker.Tests.Cases/Mono.Linker.Tests.Cases.csproj @@ -220,13 +220,31 @@ <Compile Include="Inheritance.Interfaces\OnReferenceType\NoInstanceCtor\NoInstanceCtorAndTypePreserveMethods.cs" /> <Compile Include="Inheritance.Interfaces\OnReferenceType\NoInstanceCtor\NoInstanceCtorAndTypePreserveMethodsWithInterfacesMarked.cs" /> <Compile Include="Inheritance.Interfaces\OnReferenceType\NoInstanceCtor\NoInstanceCtorAndTypePreserveNone.cs" /> + <Compile Include="Inheritance.Interfaces\OnReferenceType\NoKeptCtorButInterfaceNeeded\ArrayWithIndexAssignedToReturnValue.cs" /> + <Compile Include="Inheritance.Interfaces\OnReferenceType\NoKeptCtorButInterfaceNeeded\ArrayPassedAsParameter.cs" /> + <Compile Include="Inheritance.Interfaces\OnReferenceType\NoKeptCtorButInterfaceNeeded\FieldDowncastedToInterface.cs" /> + <Compile Include="Inheritance.Interfaces\OnReferenceType\NoKeptCtorButInterfaceNeeded\GenericType.cs" /> + <Compile Include="Inheritance.Interfaces\OnReferenceType\NoKeptCtorButInterfaceNeeded\GenericTypeWithConstraint2.cs" /> + <Compile Include="Inheritance.Interfaces\OnReferenceType\NoKeptCtorButInterfaceNeeded\GenericTypeWithConstraint3.cs" /> + <Compile Include="Inheritance.Interfaces\OnReferenceType\NoKeptCtorButInterfaceNeeded\InterfaceOnMultipleBases.cs" /> + <Compile Include="Inheritance.Interfaces\OnReferenceType\NoKeptCtorButInterfaceNeeded\LocalDowncastedToInterface.cs" /> + <Compile Include="Inheritance.Interfaces\OnReferenceType\NoKeptCtorButInterfaceNeeded\LocalPassedAsParameter.cs" /> + <Compile Include="Inheritance.Interfaces\OnReferenceType\NoKeptCtorButInterfaceNeeded\LocalPassedAsParameterToGeneric.cs" /> + <Compile Include="Inheritance.Interfaces\OnReferenceType\NoKeptCtorButInterfaceNeeded\LocalPassedAsParameterToGenericWithConstraint.cs" /> + <Compile Include="Inheritance.Interfaces\OnReferenceType\NoKeptCtorButInterfaceNeeded\LocalPassedAsParameterToGenericWithConstraint2.cs" /> + <Compile Include="Inheritance.Interfaces\OnReferenceType\NoKeptCtorButInterfaceNeeded\ReturnValueDowncastedToInterface.cs" /> + <Compile Include="Inheritance.Interfaces\OnReferenceType\NoKeptCtorButInterfaceNeeded\GenericTypeWithConstraint.cs" /> <Compile Include="Inheritance.Interfaces\OnReferenceType\NoKeptCtor\ComInterfaceTypeRemovedWhenOnlyUsedByClassWithOnlyStaticMethod.cs" /> <Compile Include="Inheritance.Interfaces\OnReferenceType\NoKeptCtor\ExplicitInterfaceCanBeRemovedFromClassWithOnlyStaticMethodUsed.cs" /> + <Compile Include="Inheritance.Interfaces\OnReferenceType\NoKeptCtor\GenericType.cs" /> <Compile Include="Inheritance.Interfaces\OnReferenceType\NoKeptCtor\InterfaceCanBeRemovedFromClassWithOnlyStaticMethodUsed.cs" /> <Compile Include="Inheritance.Interfaces\OnReferenceType\NoKeptCtor\InterfaceCanBeRemovedFromClassWithOnlyStaticMethodUsedWithCctor.cs" /> <Compile Include="Inheritance.Interfaces\OnReferenceType\NoKeptCtor\InterfaceFromCopiedAssemblyCanBeRemoved.cs" /> <Compile Include="Inheritance.Interfaces\OnReferenceType\NoKeptCtor\InterfaceTypeRemovedWhenOnlyUsedByClassWithOnlyStaticMethod.cs" /> <Compile Include="Inheritance.Interfaces\OnReferenceType\NoKeptCtor\InterfaceTypeRemovedWhenOnlyUsedByClassWithOnlyStaticMethodMultiple.cs" /> + <Compile Include="Inheritance.Interfaces\OnReferenceType\NoKeptCtor\LocalDowncastDoesNotCuaseOtherTypesToKeepInterface.cs" /> + <Compile Include="Inheritance.Interfaces\OnReferenceType\NoKeptCtor\GenericWithConstraintDoesNotCauseOtherTypesToKeepInterface.cs" /> + <Compile Include="Inheritance.Interfaces\OnReferenceType\NoKeptCtor\ObjectHardCastedToInterface.cs" /> <Compile Include="Inheritance.Interfaces\OnReferenceType\NoKeptCtor\PreserveDependencyPreservesInterfaceMethod.cs" /> <Compile Include="Inheritance.Interfaces\OnReferenceType\NoKeptCtor\UnusedTypeWithPreserveFields.cs" /> <Compile Include="Inheritance.Interfaces\OnReferenceType\NoKeptCtor\UnusedTypeWithPreserveMethods.cs" /> |