diff options
56 files changed, 1594 insertions, 64 deletions
diff --git a/src/linker/Linker.Steps/CodeRewriterStep.cs b/src/linker/Linker.Steps/CodeRewriterStep.cs index a7029936b..8d2d7c8a7 100644 --- a/src/linker/Linker.Steps/CodeRewriterStep.cs +++ b/src/linker/Linker.Steps/CodeRewriterStep.cs @@ -45,16 +45,17 @@ namespace Mono.Linker.Steps { } } - void RewriteBodyToLinkedAway (MethodDefinition method) + protected virtual void RewriteBodyToLinkedAway (MethodDefinition method) { method.ImplAttributes &= ~(MethodImplAttributes.AggressiveInlining | MethodImplAttributes.Synchronized); method.ImplAttributes |= MethodImplAttributes.NoInlining; method.Body = CreateThrowLinkedAwayBody (method); + ClearDebugInformation (method); } - void RewriteBodyToStub (MethodDefinition method) + protected virtual void RewriteBodyToStub (MethodDefinition method) { if (!method.IsIL) throw new NotImplementedException (); @@ -64,7 +65,7 @@ namespace Mono.Linker.Steps { ClearDebugInformation (method); } - void RewriteBodyToFalse (MethodDefinition method) + protected virtual void RewriteBodyToFalse (MethodDefinition method) { if (!method.IsIL) throw new NotImplementedException (); diff --git a/src/linker/Linker.Steps/MarkStep.cs b/src/linker/Linker.Steps/MarkStep.cs index d73fd3693..5b7d48ff2 100644 --- a/src/linker/Linker.Steps/MarkStep.cs +++ b/src/linker/Linker.Steps/MarkStep.cs @@ -46,6 +46,7 @@ namespace Mono.Linker.Steps { protected Queue<AttributeProviderPair> _assemblyLevelAttributes; protected Queue<AttributeProviderPair> _lateMarkedAttributes; protected List<TypeDefinition> _typesWithInterfaces; + protected List<MethodBody> _unreachableBodies; public AnnotationStore Annotations { get { return _context.Annotations; } @@ -64,6 +65,7 @@ namespace Mono.Linker.Steps { _assemblyLevelAttributes = new Queue<AttributeProviderPair> (); _lateMarkedAttributes = new Queue<AttributeProviderPair> (); _typesWithInterfaces = new List<TypeDefinition> (); + _unreachableBodies = new List<MethodBody> (); } public virtual void Process (LinkContext context) @@ -72,6 +74,7 @@ namespace Mono.Linker.Steps { Initialize (); Process (); + Complete (); } void Initialize () @@ -93,6 +96,13 @@ namespace Mono.Linker.Steps { } } + void Complete () + { + foreach (var body in _unreachableBodies) { + Annotations.SetAction (body.Method, MethodAction.ConvertToThrow); + } + } + void InitializeType (TypeDefinition type) { if (type.HasNestedTypes) { @@ -188,6 +198,7 @@ namespace Mono.Linker.Steps { ProcessQueue (); ProcessVirtualMethods (); ProcessMarkedTypesWithInterfaces (); + ProcessPendingBodies (); DoAdditionalProcessing (); } @@ -242,6 +253,17 @@ namespace Mono.Linker.Steps { } } + void ProcessPendingBodies () + { + for (int i = 0; i < _unreachableBodies.Count; i++) { + var body = _unreachableBodies [i]; + if (Annotations.IsInstantiated (body.Method.DeclaringType)) { + MarkMethodBody (body); + _unreachableBodies.RemoveAt (i--); + } + } + } + void ProcessVirtualMethod (MethodDefinition method) { var overrides = Annotations.GetOverrides (method); @@ -1919,23 +1941,28 @@ namespace Mono.Linker.Steps { break; case MethodAction.ConvertToThrow: - if (_context.MarkedKnownMembers.NotSupportedExceptionCtorString != null) - break; + MarkAndCacheNotSupportedCtorString (); + break; + } + } - var nse = BCL.FindPredefinedType ("System", "NotSupportedException", _context); - if (nse == null) - throw new NotSupportedException ("Missing predefined 'System.NotSupportedException' type"); + void MarkAndCacheNotSupportedCtorString () + { + if (_context.MarkedKnownMembers.NotSupportedExceptionCtorString != null) + return; - MarkType (nse); + var nse = BCL.FindPredefinedType ("System", "NotSupportedException", _context); + if (nse == null) + throw new NotSupportedException ("Missing predefined 'System.NotSupportedException' type"); - var nseCtor = MarkMethodIf (nse.Methods, KnownMembers.IsNotSupportedExceptionCtorString); - if (nseCtor == null) - throw new MarkException ($"Could not find constructor on '{nse.FullName}'"); + MarkType (nse); - _context.MarkedKnownMembers.NotSupportedExceptionCtorString = nseCtor; - break; - } - } + var nseCtor = MarkMethodIf (nse.Methods, KnownMembers.IsNotSupportedExceptionCtorString); + if (nseCtor == null) + throw new MarkException ($"Could not find constructor on '{nse.FullName}'"); + + _context.MarkedKnownMembers.NotSupportedExceptionCtorString = nseCtor; + } void MarkBaseMethods (MethodDefinition method) { @@ -2068,6 +2095,12 @@ namespace Mono.Linker.Steps { protected virtual void MarkMethodBody (MethodBody body) { + if (_context.IsOptimizationEnabled (CodeOptimizations.UnreachableBodies) && IsUnreachableBody (body)) { + MarkAndCacheNotSupportedCtorString (); + _unreachableBodies.Add (body); + return; + } + foreach (VariableDefinition var in body.Variables) MarkType (var.VariableType); @@ -2085,6 +2118,14 @@ namespace Mono.Linker.Steps { PostMarkMethodBody (body); } + bool IsUnreachableBody (MethodBody body) + { + return !body.Method.IsStatic + && !Annotations.IsInstantiated (body.Method.DeclaringType) + && MethodBodyScanner.IsWorthConvertingToThrow (body); + } + + partial void PostMarkMethodBody (MethodBody body); void MarkInterfacesNeededByBodyStack (MethodBody body) diff --git a/src/linker/Linker.Steps/RemoveFeaturesStep.cs b/src/linker/Linker.Steps/RemoveFeaturesStep.cs index 225472d4f..14bb889fa 100644 --- a/src/linker/Linker.Steps/RemoveFeaturesStep.cs +++ b/src/linker/Linker.Steps/RemoveFeaturesStep.cs @@ -100,7 +100,8 @@ namespace Mono.Linker.Steps continue; } - annotations.SetAction (method, MethodAction.ConvertToThrow); + if (MethodBodyScanner.IsWorthConvertingToThrow (method.Body)) + annotations.SetAction (method, MethodAction.ConvertToThrow); } } @@ -149,7 +150,7 @@ namespace Mono.Linker.Steps continue; } - if (!skip) + if (!skip && MethodBodyScanner.IsWorthConvertingToThrow (method.Body)) annotations.SetAction (method, MethodAction.ConvertToThrow); } } @@ -166,7 +167,8 @@ namespace Mono.Linker.Steps { foreach (var method in type.Methods) { - annotations.SetAction(method, MethodAction.ConvertToThrow); + if (MethodBodyScanner.IsWorthConvertingToThrow (method.Body)) + annotations.SetAction(method, MethodAction.ConvertToThrow); } } diff --git a/src/linker/Linker/BCL.cs b/src/linker/Linker/BCL.cs index 1c75a056d..9d26c060f 100644 --- a/src/linker/Linker/BCL.cs +++ b/src/linker/Linker/BCL.cs @@ -65,9 +65,14 @@ namespace Mono.Linker var cache = context.Resolver.AssemblyCache; AssemblyDefinition corlib; - if (cache.TryGetValue ("mscorlib", out corlib)) - return corlib.MainModule.GetType (ns, name); - + TypeDefinition type = null; + if (cache.TryGetValue ("mscorlib", out corlib)) { + type = corlib.MainModule.GetType (ns, name); + // The assembly could be a facade with type forwarders, in which case we don't find the type in this assembly. + if (type != null) + return type; + } + if (cache.TryGetValue ("System.Private.CoreLib", out corlib)) return corlib.MainModule.GetType (ns, name); diff --git a/src/linker/Linker/Driver.cs b/src/linker/Linker/Driver.cs index 3cca83aaf..02fc7b509 100644 --- a/src/linker/Linker/Driver.cs +++ b/src/linker/Linker/Driver.cs @@ -363,6 +363,9 @@ namespace Mono.Linker { case "overrideremoval": context.DisabledOptimizations |= CodeOptimizations.OverrideRemoval; break; + case "unreachablebodies": + context.DisabledOptimizations |= CodeOptimizations.UnreachableBodies; + break; } } } @@ -522,6 +525,7 @@ namespace Mono.Linker { Console.WriteLine (" --disable-opt <name> Disable one of the default optimizations"); Console.WriteLine (" beforefieldinit: Unused static fields are removed if there is no static ctor"); Console.WriteLine (" overrideremoval: Overrides of virtual methods on types that are never instantiated are removed"); + Console.WriteLine (" unreachablebodies: Instance methods that are marked but can never be entered are converted to throws"); Console.WriteLine (" --exclude-feature <name> Any code which has a feature <name> in linked assemblies will be removed"); Console.WriteLine (" com: Support for COM Interop"); Console.WriteLine (" etw: Event Tracing for Windows"); diff --git a/src/linker/Linker/LinkContext.cs b/src/linker/Linker/LinkContext.cs index 1925eb229..79a0d5ba8 100644 --- a/src/linker/Linker/LinkContext.cs +++ b/src/linker/Linker/LinkContext.cs @@ -401,5 +401,10 @@ namespace Mono.Linker { /// that do not get an instance constructor marked. /// </summary> OverrideRemoval = 1 << 1, + + /// <summary> + /// Option to disable delaying marking of instance methods until an instance of that type could exist + /// </summary> + UnreachableBodies = 1 << 2 } } diff --git a/src/linker/Linker/MethodBodyScanner.cs b/src/linker/Linker/MethodBodyScanner.cs index 09d672a93..c9b5d0379 100644 --- a/src/linker/Linker/MethodBodyScanner.cs +++ b/src/linker/Linker/MethodBodyScanner.cs @@ -6,6 +6,49 @@ using Mono.Cecil.Cil; namespace Mono.Linker { public static class MethodBodyScanner { + public static bool IsWorthConvertingToThrow (MethodBody body) + { + // Some bodies are cheaper size wise to leave alone than to convert to a throw + Instruction previousMeaningful = null; + int meaningfulCount = 0; + foreach (var ins in body.Instructions) { + // Handle ignoring noops because because (1) it's a valid case to ignore + // and (2) When running the tests on .net core roslyn tosses in no ops + // and that leads to a difference in test results between mcs and .net framework csc. + if (ins.OpCode.Code == Code.Nop) + continue; + + meaningfulCount++; + + if (meaningfulCount == 1 && ins.OpCode.Code == Code.Ret) + return false; + + if (meaningfulCount == 2 && ins.OpCode.Code == Code.Ret && previousMeaningful != null) { + if (previousMeaningful.OpCode.StackBehaviourPop == StackBehaviour.Pop0) { + switch (previousMeaningful.OpCode.StackBehaviourPush) { + case StackBehaviour.Pushi: + case StackBehaviour.Pushi8: + case StackBehaviour.Pushr4: + case StackBehaviour.Pushr8: + return false; + } + + switch (previousMeaningful.OpCode.Code) { + case Code.Ldnull: + return false; + } + } + } + + if (meaningfulCount >= 2) + return true; + + previousMeaningful = ins; + } + + return true; + } + public static IEnumerable<InterfaceImplementation> GetReferencedInterfaces (AnnotationStore annotations, MethodBody body) { var possibleStackTypes = AllPossibleStackTypes (body.Method); diff --git a/test/Mono.Linker.Tests.Cases/BCLFeatures/ETW/BaseRemovedEventSource.cs b/test/Mono.Linker.Tests.Cases/BCLFeatures/ETW/BaseRemovedEventSource.cs index d63d45a7c..5f3ead9a1 100644 --- a/test/Mono.Linker.Tests.Cases/BCLFeatures/ETW/BaseRemovedEventSource.cs +++ b/test/Mono.Linker.Tests.Cases/BCLFeatures/ETW/BaseRemovedEventSource.cs @@ -50,6 +50,11 @@ namespace Mono.Linker.Tests.Cases.BCLFeatures.ETW { })] protected override void OnEventCommand (EventCommandEventArgs command) { + Removed2 (); + } + + static void Removed2 () + { } [Kept] diff --git a/test/Mono.Linker.Tests.Cases/BCLFeatures/ETW/BaseRemovedEventSourceEmptyBody.cs b/test/Mono.Linker.Tests.Cases/BCLFeatures/ETW/BaseRemovedEventSourceEmptyBody.cs new file mode 100644 index 000000000..920cb7e91 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/BCLFeatures/ETW/BaseRemovedEventSourceEmptyBody.cs @@ -0,0 +1,75 @@ +using System.Diagnostics.Tracing; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.BCLFeatures.ETW { + [SetupLinkerArgument ("--exclude-feature", "etw")] + public class BaseRemovedEventSourceEmptyBody { + public static void Main () + { + var b = CustomCtorEventSourceEmptyBody.Log.IsEnabled (); + if (b) + CustomCtorEventSourceEmptyBody.Log.SomeMethod (); + } + } + + [Kept] + [KeptBaseType (typeof (EventSource))] + [KeptMember (".ctor()")] + [KeptMember (".cctor()")] + [EventSource (Name = "MyCompany")] + class CustomCtorEventSourceEmptyBody : EventSource { + public class Keywords { + public const EventKeywords Page = (EventKeywords)1; + + public int Unused; + } + + [Kept] + public static CustomCtorEventSourceEmptyBody Log = new MyEventSourceBasedOnCustomCtorEventSourceEmptyBody (1); + + [Kept] + [ExpectedInstructionSequence (new [] + { + "ldarg.0", + "call", + "ret", + })] + public CustomCtorEventSourceEmptyBody (int value) + { + Removed (); + } + + [Kept] + protected override void OnEventCommand (EventCommandEventArgs command) + { + // Not converted to throw because the body is empty + } + + [Kept] + [ExpectedInstructionSequence (new [] + { + "ldstr", + "newobj", + "throw", + })] + [Event (8)] + public void SomeMethod () + { + Removed (); + } + + public void Removed () + { + } + } + + [Kept] + [KeptBaseType (typeof (CustomCtorEventSourceEmptyBody))] + class MyEventSourceBasedOnCustomCtorEventSourceEmptyBody : CustomCtorEventSourceEmptyBody { + [Kept] + public MyEventSourceBasedOnCustomCtorEventSourceEmptyBody (int value) : base (value) + { + } + } +}
\ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/BCLFeatures/ETW/Excluded.cs b/test/Mono.Linker.Tests.Cases/BCLFeatures/ETW/Excluded.cs index 575a7dec0..20cac269b 100644 --- a/test/Mono.Linker.Tests.Cases/BCLFeatures/ETW/Excluded.cs +++ b/test/Mono.Linker.Tests.Cases/BCLFeatures/ETW/Excluded.cs @@ -41,6 +41,11 @@ namespace Mono.Linker.Tests.Cases.BCLFeatures.ETW { })] protected override void OnEventCommand (EventCommandEventArgs command) { + Removed2 (); + } + + static void Removed2 () + { } [Kept] diff --git a/test/Mono.Linker.Tests.Cases/Inheritance.AbstractClasses/NoKeptCtor/OverrideRemoval/OverrideOfAbstractIsKeptNonEmpty.cs b/test/Mono.Linker.Tests.Cases/Inheritance.AbstractClasses/NoKeptCtor/OverrideRemoval/OverrideOfAbstractIsKeptNonEmpty.cs new file mode 100644 index 000000000..de9030a41 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/Inheritance.AbstractClasses/NoKeptCtor/OverrideRemoval/OverrideOfAbstractIsKeptNonEmpty.cs @@ -0,0 +1,48 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Inheritance.AbstractClasses.NoKeptCtor.OverrideRemoval { + public class OverrideOfAbstractIsKeptNonEmpty { + public static void Main () + { + Base b = HelperToMarkFooAndRequireBase (); + b.Method (); + } + + [Kept] + static Foo HelperToMarkFooAndRequireBase () + { + return null; + } + + [Kept] + abstract class Base { + [Kept] + public abstract void Method (); + } + + [Kept] + [KeptBaseType (typeof(Base))] + abstract class Base2 : Base { + } + + [Kept] + [KeptBaseType (typeof (Base2))] + abstract class Base3 : Base2 { + } + + [Kept] + [KeptBaseType (typeof (Base3))] + class Foo : Base3 { + [Kept] + [ExpectBodyModified] + public override void Method () + { + Other (); + } + + static void Other() + { + } + } + } +}
\ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtor/UnusedTypeHasExplicitInterfacePropertyPreservedViaXml.cs b/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtor/UnusedTypeHasExplicitInterfacePropertyPreservedViaXml.cs index 191928690..c2116b702 100644 --- a/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtor/UnusedTypeHasExplicitInterfacePropertyPreservedViaXml.cs +++ b/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtor/UnusedTypeHasExplicitInterfacePropertyPreservedViaXml.cs @@ -20,8 +20,7 @@ namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.OnReferenceType.NoKeptC [KeptInterface (typeof (IFoo))] class A : IBar, IFoo { [Kept] - [KeptBackingField] - int IFoo.Foo { [Kept] get; [Kept] set; } + int IFoo.Foo { [Kept] [ExpectBodyModified] get; [Kept] [ExpectBodyModified] set; } int IBar.Bar { get; set; } } diff --git a/test/Mono.Linker.Tests.Cases/LinkXml/FeatureExclude/OnProperty.cs b/test/Mono.Linker.Tests.Cases/LinkXml/FeatureExclude/OnProperty.cs index b6e78b255..eb043fb01 100644 --- a/test/Mono.Linker.Tests.Cases/LinkXml/FeatureExclude/OnProperty.cs +++ b/test/Mono.Linker.Tests.Cases/LinkXml/FeatureExclude/OnProperty.cs @@ -1,17 +1,22 @@ using Mono.Linker.Tests.Cases.Expectations.Assertions; using Mono.Linker.Tests.Cases.Expectations.Metadata; -namespace Mono.Linker.Tests.Cases.LinkXml.FeatureExclude { - [SetupLinkerArgument ("--exclude-feature", "one")] - public class OnProperty { - public static void Main () - { - } +namespace Mono.Linker.Tests.Cases.LinkXml.FeatureExclude { + [SetupLinkerArgument ("--exclude-feature", "one")] + public class OnProperty { + public static void Main () + { + new Foo (); // Used to avoid lazy body marking + } - public int FeatureOne { get; set; } - - [Kept] - [KeptBackingField] - public int FeatureTwo {[Kept] get; [Kept] set; } - } + [Kept] + [KeptMember (".ctor()")] + class Foo { + public int FeatureOne { get; set; } + + [Kept] + [KeptBackingField] + public int FeatureTwo {[Kept] get; [Kept] set; } + } + } } diff --git a/test/Mono.Linker.Tests.Cases/LinkXml/FeatureExclude/OnProperty.xml b/test/Mono.Linker.Tests.Cases/LinkXml/FeatureExclude/OnProperty.xml index 3b2ee6fcb..95d59ecab 100644 --- a/test/Mono.Linker.Tests.Cases/LinkXml/FeatureExclude/OnProperty.xml +++ b/test/Mono.Linker.Tests.Cases/LinkXml/FeatureExclude/OnProperty.xml @@ -1,9 +1,9 @@ <linker> <assembly fullname="test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"> - <type fullname="Mono.Linker.Tests.Cases.LinkXml.FeatureExclude.OnProperty"> + <type fullname="Mono.Linker.Tests.Cases.LinkXml.FeatureExclude.OnProperty/Foo"> <property signature="System.Int32 FeatureOne" accessors="all" feature="one"/> </type> - <type fullname="Mono.Linker.Tests.Cases.LinkXml.FeatureExclude.OnProperty"> + <type fullname="Mono.Linker.Tests.Cases.LinkXml.FeatureExclude.OnProperty/Foo"> <property signature="System.Int32 FeatureTwo" accessors="all" feature="two"/> </type> </assembly> diff --git a/test/Mono.Linker.Tests.Cases/LinkXml/UnusedEventPreservedByLinkXmlIsKept.cs b/test/Mono.Linker.Tests.Cases/LinkXml/UnusedEventPreservedByLinkXmlIsKept.cs index 594b5bd87..b953fdcea 100644 --- a/test/Mono.Linker.Tests.Cases/LinkXml/UnusedEventPreservedByLinkXmlIsKept.cs +++ b/test/Mono.Linker.Tests.Cases/LinkXml/UnusedEventPreservedByLinkXmlIsKept.cs @@ -6,9 +6,11 @@ namespace Mono.Linker.Tests.Cases.LinkXml { class UnusedEventPreservedByLinkXmlIsKept { public static void Main () { + new Unused (); // Used to avoid lazy body marking } [Kept] + [KeptMember (".ctor()")] class Unused { [Kept] [KeptBackingField] diff --git a/test/Mono.Linker.Tests.Cases/LinkXml/UnusedPropertyPreservedByLinkXmlIsKept.cs b/test/Mono.Linker.Tests.Cases/LinkXml/UnusedPropertyPreservedByLinkXmlIsKept.cs index 6f21e7b55..8d3bb6372 100644 --- a/test/Mono.Linker.Tests.Cases/LinkXml/UnusedPropertyPreservedByLinkXmlIsKept.cs +++ b/test/Mono.Linker.Tests.Cases/LinkXml/UnusedPropertyPreservedByLinkXmlIsKept.cs @@ -4,9 +4,11 @@ namespace Mono.Linker.Tests.Cases.LinkXml { class UnusedPropertyPreservedByLinkXmlIsKept { public static void Main () { + new Unused (); // Used to avoid lazy body marking } [Kept] + [KeptMember (".ctor()")] class Unused { [Kept] [KeptBackingField] diff --git a/test/Mono.Linker.Tests.Cases/Mono.Linker.Tests.Cases.csproj b/test/Mono.Linker.Tests.Cases/Mono.Linker.Tests.Cases.csproj index 0c23e777b..72e832415 100644 --- a/test/Mono.Linker.Tests.Cases/Mono.Linker.Tests.Cases.csproj +++ b/test/Mono.Linker.Tests.Cases/Mono.Linker.Tests.Cases.csproj @@ -179,6 +179,7 @@ <Compile Include="Basic\UsedEventOnInterfaceIsRemovedWhenUsedFromClass.cs" /> <Compile Include="Basic\UsedPropertyIsKept.cs" /> <Compile Include="Basic\UsedStructIsKept.cs" /> + <Compile Include="BCLFeatures\ETW\BaseRemovedEventSourceEmptyBody.cs" /> <Compile Include="BCLFeatures\ETW\LocalsOfModifiedMethodAreRemoved.cs" /> <Compile Include="BCLFeatures\ETW\StubbedMethodWithExceptionHandlers.cs" /> <Compile Include="CommandLine\ResponseFilesWork.cs" /> @@ -187,6 +188,7 @@ <Compile Include="CoreLink\InstantiatedStructWithOverridesFromObject.cs" /> <Compile Include="CoreLink\NeverInstantiatedTypeWithOverridesFromObject.cs" /> <Compile Include="CoreLink\NoSecurityPlusOnlyKeepUsedRemovesAllSecurityAttributesFromCoreLibraries.cs" /> + <Compile Include="Inheritance.AbstractClasses\NoKeptCtor\OverrideRemoval\OverrideOfAbstractIsKeptNonEmpty.cs" /> <Compile Include="CoreLink\InstantiatedTypeWithOverridesFromObject.cs" /> <Compile Include="Inheritance.AbstractClasses\NoKeptCtor\OverrideRemoval\CanDisableOverrideRemoval.cs" /> <Compile Include="Inheritance.AbstractClasses\NoKeptCtor\OverrideRemoval\OverrideOfAbstractIsKept.cs" /> @@ -354,6 +356,37 @@ <Compile Include="Inheritance.VirtualMethods\UsedTypeWithOverrideOfVirtualMethodHasOverrideKept.cs" /> <Compile Include="Inheritance.VirtualMethods\VirtualMethodGetsPerservedIfBaseMethodGetsInvoked.cs" /> <Compile Include="Inheritance.VirtualMethods\VirtualMethodGetsStrippedIfImplementingMethodGetsInvokedDirectly.cs" /> + <Compile Include="UnreachableBody\CanDisableLazyBodyMarking.cs" /> + <Compile Include="UnreachableBody\Dependencies\OtherAssembly.cs" /> + <Compile Include="UnreachableBody\DoesNotApplyToCopiedAssembly.cs" /> + <Compile Include="UnreachableBody\DoesNotApplyToCopiedAssembly2.cs" /> + <Compile Include="UnreachableBody\DoesNotApplyToSavedAssembly.cs" /> + <Compile Include="UnreachableBody\DoesNotApplyToSavedAssembly2.cs" /> + <Compile Include="UnreachableBody\NotWorthConvertingEmpty.cs" /> + <Compile Include="UnreachableBody\ExplicitInstructionCheck.cs" /> + <Compile Include="UnreachableBody\InterfaceMethod.cs" /> + <Compile Include="UnreachableBody\MixOfMethods.cs" /> + <Compile Include="UnreachableBody\LinkedOtherIncludedLibrary.cs" /> + <Compile Include="UnreachableBody\LinkedOtherIncludedLibraryNoInstanceCtor.cs" /> + <Compile Include="UnreachableBody\NotWorthConvertingReturnDouble.cs" /> + <Compile Include="UnreachableBody\NotWorthConvertingReturnFalse.cs" /> + <Compile Include="UnreachableBody\NotWorthConvertingReturnFloat.cs" /> + <Compile Include="UnreachableBody\NotWorthConvertingReturnInt.cs" /> + <Compile Include="UnreachableBody\NotWorthConvertingReturnLong.cs" /> + <Compile Include="UnreachableBody\NotWorthConvertingReturnNull.cs" /> + <Compile Include="UnreachableBody\NotWorthConvertingReturnTrue.cs" /> + <Compile Include="UnreachableBody\OverrideOfAbstractAndInterfaceMethodCalledFromLocal.cs" /> + <Compile Include="UnreachableBody\OverrideOfAbstractAndInterfaceMethodCalledFromLocal2.cs" /> + <Compile Include="UnreachableBody\OverrideOfAbstractAndInterfaceMethodCalledFromLocal3.cs" /> + <Compile Include="UnreachableBody\OverrideOfAbstractAndInterfaceMethodWhenInterfaceRemoved2.cs" /> + <Compile Include="UnreachableBody\OverrideOfAbstractIsStubbed.cs" /> + <Compile Include="UnreachableBody\OverrideOfAbstractIsStubbedWithUnusedInterface.cs" /> + <Compile Include="UnreachableBody\SimpleGetter.cs" /> + <Compile Include="UnreachableBody\SimpleMethod.cs" /> + <Compile Include="UnreachableBody\OverrideOfAVirtual.cs" /> + <Compile Include="UnreachableBody\SimpleSetter.cs" /> + <Compile Include="UnreachableBody\WorksWithLinkXml.cs" /> + <Compile Include="UnreachableBody\WorksWithPreserveDependency.cs" /> <Compile Include="Libraries\CanLinkPublicApisOfLibrary.cs" /> <Compile Include="Libraries\DefaultLibraryLinkBehavior.cs" /> <Compile Include="Libraries\Dependencies\UserAssemblyActionWorks_Lib.cs" /> @@ -614,6 +647,8 @@ <Content Include="Inheritance.Interfaces\OnReferenceType\UnusedInterfaceHasMethodPreservedViaXml.xml" /> <Content Include="Inheritance.Interfaces\OnValueType\UnusedExplicitInterfaceHasMethodPreservedViaXml.xml" /> <Content Include="Inheritance.Interfaces\OnValueType\UnusedInterfaceHasMethodPreservedViaXml.xml" /> + <Content Include="UnreachableBody\Dependencies\OtherAssemblyNoInstanceCtor.il" /> + <Content Include="UnreachableBody\WorksWithLinkXml.xml" /> <Content Include="LinkXml\AssemblyWithPreserveAll.xml" /> <Content Include="LinkXml\CanPreserveTypesUsingRegex.xml" /> <Content Include="LinkXml\CanPreserveAnExportedType.xml" /> diff --git a/test/Mono.Linker.Tests.Cases/PreserveDependencies/PreserveDependencyMethod.cs b/test/Mono.Linker.Tests.Cases/PreserveDependencies/PreserveDependencyMethod.cs index fc634f0a6..fa48bc8e4 100644 --- a/test/Mono.Linker.Tests.Cases/PreserveDependencies/PreserveDependencyMethod.cs +++ b/test/Mono.Linker.Tests.Cases/PreserveDependencies/PreserveDependencyMethod.cs @@ -5,12 +5,15 @@ namespace Mono.Linker.Tests.Cases.PreserveDependencies { class PreserveDependencyMethod { public static void Main () { + new B (); // Needed to avoid lazy body marking stubbing + B.Method (); B.SameContext (); B.Broken (); B.Conditional (); } + [KeptMember (".ctor()")] class B { [Kept] @@ -25,6 +28,7 @@ namespace Mono.Linker.Tests.Cases.PreserveDependencies { [Kept] [PreserveDependency ("Dependency1()", "Mono.Linker.Tests.Cases.PreserveDependencies.C")] [PreserveDependency ("Dependency2`1 ( T[] , System.Int32 ) ", "Mono.Linker.Tests.Cases.PreserveDependencies.C")] + [PreserveDependency (".ctor()", "Mono.Linker.Tests.Cases.PreserveDependencies.C")] // To avoid lazy body marking stubbing [PreserveDependency ("field", "Mono.Linker.Tests.Cases.PreserveDependencies.C")] [PreserveDependency ("NextOne (Mono.Linker.Tests.Cases.PreserveDependencies.PreserveDependencyMethod+Nested&)", "Mono.Linker.Tests.Cases.PreserveDependencies.PreserveDependencyMethod+Nested")] [PreserveDependency ("Property", "Mono.Linker.Tests.Cases.PreserveDependencies.C")] @@ -64,6 +68,7 @@ namespace Mono.Linker.Tests.Cases.PreserveDependencies { } } + [KeptMember (".ctor()")] class C { [Kept] diff --git a/test/Mono.Linker.Tests.Cases/Reflection/EventUsedViaReflection.cs b/test/Mono.Linker.Tests.Cases/Reflection/EventUsedViaReflection.cs index 774a46489..49e1dc5a6 100644 --- a/test/Mono.Linker.Tests.Cases/Reflection/EventUsedViaReflection.cs +++ b/test/Mono.Linker.Tests.Cases/Reflection/EventUsedViaReflection.cs @@ -5,14 +5,18 @@ namespace Mono.Linker.Tests.Cases.Reflection { public class EventUsedViaReflection { public static void Main () { - var eventInfo = typeof (EventUsedViaReflection).GetEvent ("Event"); + new Foo (); // Needed to avoid lazy body marking stubbing + var eventInfo = typeof (Foo).GetEvent ("Event"); eventInfo.GetAddMethod (false); } - [Kept] - [KeptBackingField] - [KeptEventAddMethod] - [KeptEventRemoveMethod] - event EventHandler<EventArgs> Event; + [KeptMember (".ctor()")] + class Foo { + [Kept] + [KeptBackingField] + [KeptEventAddMethod] + [KeptEventRemoveMethod] + event EventHandler<EventArgs> Event; + } } } diff --git a/test/Mono.Linker.Tests.Cases/Reflection/MethodUsedViaReflectionWithDefaultBindingFlags.cs b/test/Mono.Linker.Tests.Cases/Reflection/MethodUsedViaReflectionWithDefaultBindingFlags.cs index 7a2a4ae10..b27623ac6 100644 --- a/test/Mono.Linker.Tests.Cases/Reflection/MethodUsedViaReflectionWithDefaultBindingFlags.cs +++ b/test/Mono.Linker.Tests.Cases/Reflection/MethodUsedViaReflectionWithDefaultBindingFlags.cs @@ -5,32 +5,36 @@ namespace Mono.Linker.Tests.Cases.Reflection { public class MethodUsedViaReflectionWithDefaultBindingFlags { public static void Main () { - var method = typeof (MethodUsedViaReflectionWithDefaultBindingFlags).GetMethod ("OnlyCalledViaReflection"); + new Foo (); // Needed to avoid lazy body marking stubbing + var method = typeof (Foo).GetMethod ("OnlyCalledViaReflection"); method.Invoke (null, new object[] { }); } - [Kept] - private static int OnlyCalledViaReflection () - { - return 42; - } + [KeptMember (".ctor()")] + class Foo { + [Kept] + private static int OnlyCalledViaReflection () + { + return 42; + } - [Kept] - private int OnlyCalledViaReflection (int foo) - { - return 43; - } + [Kept] + private int OnlyCalledViaReflection (int foo) + { + return 43; + } - [Kept] - public int OnlyCalledViaReflection (int foo, int bar) - { - return 44; - } + [Kept] + public int OnlyCalledViaReflection (int foo, int bar) + { + return 44; + } - [Kept] - public static int OnlyCalledViaReflection (int foo, int bar, int baz) - { - return 45; + [Kept] + public static int OnlyCalledViaReflection (int foo, int bar, int baz) + { + return 45; + } } } } diff --git a/test/Mono.Linker.Tests.Cases/Reflection/MightKeepExtraThings.cs b/test/Mono.Linker.Tests.Cases/Reflection/MightKeepExtraThings.cs index b5b6a1c52..091885e28 100644 --- a/test/Mono.Linker.Tests.Cases/Reflection/MightKeepExtraThings.cs +++ b/test/Mono.Linker.Tests.Cases/Reflection/MightKeepExtraThings.cs @@ -6,6 +6,8 @@ namespace Mono.Linker.Tests.Cases.Reflection { public class MightKeepExtraThings { public static void Main () { + new A (); // Needed to avoid lazy body marking stubbing + new B (); // Needed to avoid lazy body marking stubbing var typeA = typeof (A); var typeB = typeof (B); Console.WriteLine (typeB); // Use typeB so the C# compiler keeps it in the IL code. @@ -14,6 +16,7 @@ namespace Mono.Linker.Tests.Cases.Reflection { } [Kept] + [KeptMember (".ctor()")] public class A { [Kept] public int Foo () @@ -23,6 +26,7 @@ namespace Mono.Linker.Tests.Cases.Reflection { } [Kept] + [KeptMember (".ctor()")] public class B { [Kept] public int Foo () diff --git a/test/Mono.Linker.Tests.Cases/UnreachableBody/CanDisableLazyBodyMarking.cs b/test/Mono.Linker.Tests.Cases/UnreachableBody/CanDisableLazyBodyMarking.cs new file mode 100644 index 000000000..4f72c7798 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/UnreachableBody/CanDisableLazyBodyMarking.cs @@ -0,0 +1,32 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.UnreachableBody { + [SetupLinkerArgument ("--disable-opt", "unreachablebodies")] + public class CanDisableLazyBodyMarking { + public static void Main() + { + UsedToMarkMethod (null); + } + + [Kept] + static void UsedToMarkMethod (Foo f) + { + f.Method (); + } + + [Kept] + class Foo { + [Kept] + public void Method () + { + UsedByMethod (); + } + + [Kept] + void UsedByMethod () + { + } + } + } +}
\ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/UnreachableBody/Dependencies/OtherAssembly.cs b/test/Mono.Linker.Tests.Cases/UnreachableBody/Dependencies/OtherAssembly.cs new file mode 100644 index 000000000..8e7abcd65 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/UnreachableBody/Dependencies/OtherAssembly.cs @@ -0,0 +1,18 @@ +namespace Mono.Linker.Tests.Cases.UnreachableBody.Dependencies { + public class OtherAssembly { + public static void UnusedSanityCheck () + { + } + + public class Foo { + public void Method () + { + UsedByMethod (); + } + + void UsedByMethod () + { + } + } + } +}
\ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/UnreachableBody/Dependencies/OtherAssemblyNoInstanceCtor.il b/test/Mono.Linker.Tests.Cases/UnreachableBody/Dependencies/OtherAssemblyNoInstanceCtor.il new file mode 100644 index 000000000..fdf7516aa --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/UnreachableBody/Dependencies/OtherAssemblyNoInstanceCtor.il @@ -0,0 +1,63 @@ + +// Microsoft (R) .NET Framework IL Disassembler. Version 4.0.30319.1 +// Copyright (c) Microsoft Corporation. All rights reserved. + + + +// Metadata version: v4.0.30319 +.assembly extern mscorlib +{ +} +.assembly other +{ + .hash algorithm 0x00008004 + .ver 0:0:0:0 +} +.module other.dll + +// =============== CLASS MEMBERS DECLARATION =================== + +.class public auto ansi beforefieldinit Mono.Linker.Tests.Cases.UnreachableBody.Dependencies.OtherAssemblyNoInstanceCtor + extends [mscorlib]System.Object +{ + .class auto ansi nested public beforefieldinit Foo + extends [mscorlib]System.Object + { + .method public hidebysig instance void + Method() cil managed + { + // Code size 7 (0x7) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void Mono.Linker.Tests.Cases.UnreachableBody.Dependencies.OtherAssemblyNoInstanceCtor/Foo::UsedByMethod() + IL_0006: ret + } // end of method Foo::Method + + .method private hidebysig instance void + UsedByMethod() cil managed + { + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } // end of method Foo::UsedByMethod + + } // end of class Foo + + .method public hidebysig static void UnusedSanityCheck() cil managed + { + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 7 (0x7) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: ret + } + +}
\ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/UnreachableBody/DoesNotApplyToCopiedAssembly.cs b/test/Mono.Linker.Tests.Cases/UnreachableBody/DoesNotApplyToCopiedAssembly.cs new file mode 100644 index 000000000..49d2f6445 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/UnreachableBody/DoesNotApplyToCopiedAssembly.cs @@ -0,0 +1,23 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; +using Mono.Linker.Tests.Cases.UnreachableBody.Dependencies; + +namespace Mono.Linker.Tests.Cases.UnreachableBody { + [SetupLinkerAction ("copy", "other")] + [SetupCompileBefore ("other.dll", new [] {typeof (OtherAssembly)})] + [KeptMemberInAssembly ("other.dll", typeof (OtherAssembly.Foo), "Method()")] + [KeptMemberInAssembly ("other.dll", typeof (OtherAssembly.Foo), "UsedByMethod()")] + [KeptMemberInAssembly ("other.dll", typeof (OtherAssembly), "UnusedSanityCheck()")] + public class DoesNotApplyToCopiedAssembly { + public static void Main() + { + UsedToMarkMethod (null); + } + + [Kept] + static void UsedToMarkMethod (OtherAssembly.Foo f) + { + f.Method (); + } + } +}
\ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/UnreachableBody/DoesNotApplyToCopiedAssembly2.cs b/test/Mono.Linker.Tests.Cases/UnreachableBody/DoesNotApplyToCopiedAssembly2.cs new file mode 100644 index 000000000..27189d299 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/UnreachableBody/DoesNotApplyToCopiedAssembly2.cs @@ -0,0 +1,27 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.UnreachableBody { + [Define ("OTHER_INCLUDED")] + [SetupLinkerAction ("copy", "other")] + [SetupCompileBefore ("other.dll", new [] { "Dependencies/OtherAssemblyNoInstanceCtor.il" })] + [KeptMemberInAssembly ("other.dll", "Mono.Linker.Tests.Cases.UnreachableBody.Dependencies.OtherAssemblyNoInstanceCtor/Foo", "Method()")] + [KeptMemberInAssembly ("other.dll", "Mono.Linker.Tests.Cases.UnreachableBody.Dependencies.OtherAssemblyNoInstanceCtor/Foo", "UsedByMethod()")] + [KeptMemberInAssembly ("other.dll", "Mono.Linker.Tests.Cases.UnreachableBody.Dependencies.OtherAssemblyNoInstanceCtor", "UnusedSanityCheck()")] + public class DoesNotApplyToCopiedAssembly2 { + public static void Main () + { +#if OTHER_INCLUDED + UsedToMarkMethod (null); +#endif + } + +#if OTHER_INCLUDED + [Kept] + static void UsedToMarkMethod (Mono.Linker.Tests.Cases.UnreachableBody.Dependencies.OtherAssemblyNoInstanceCtor.Foo f) + { + f.Method (); + } +#endif + } +}
\ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/UnreachableBody/DoesNotApplyToSavedAssembly.cs b/test/Mono.Linker.Tests.Cases/UnreachableBody/DoesNotApplyToSavedAssembly.cs new file mode 100644 index 000000000..5c15f34eb --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/UnreachableBody/DoesNotApplyToSavedAssembly.cs @@ -0,0 +1,23 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; +using Mono.Linker.Tests.Cases.UnreachableBody.Dependencies; + +namespace Mono.Linker.Tests.Cases.UnreachableBody { + [SetupLinkerAction ("save", "other")] + [SetupCompileBefore ("other.dll", new [] {typeof (OtherAssembly)})] + [KeptMemberInAssembly ("other.dll", typeof (OtherAssembly.Foo), "Method()")] + [KeptMemberInAssembly ("other.dll", typeof (OtherAssembly.Foo), "UsedByMethod()")] + [KeptMemberInAssembly ("other.dll", typeof (OtherAssembly), "UnusedSanityCheck()")] + public class DoesNotApplyToSavedAssembly { + public static void Main() + { + UsedToMarkMethod (null); + } + + [Kept] + static void UsedToMarkMethod (OtherAssembly.Foo f) + { + f.Method (); + } + } +}
\ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/UnreachableBody/DoesNotApplyToSavedAssembly2.cs b/test/Mono.Linker.Tests.Cases/UnreachableBody/DoesNotApplyToSavedAssembly2.cs new file mode 100644 index 000000000..6e8fd17fb --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/UnreachableBody/DoesNotApplyToSavedAssembly2.cs @@ -0,0 +1,27 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.UnreachableBody { + [Define ("OTHER_INCLUDED")] + [SetupLinkerAction ("save", "other")] + [SetupCompileBefore ("other.dll", new [] { "Dependencies/OtherAssemblyNoInstanceCtor.il" })] + [KeptMemberInAssembly ("other.dll", "Mono.Linker.Tests.Cases.UnreachableBody.Dependencies.OtherAssemblyNoInstanceCtor/Foo", "Method()")] + [KeptMemberInAssembly ("other.dll", "Mono.Linker.Tests.Cases.UnreachableBody.Dependencies.OtherAssemblyNoInstanceCtor/Foo", "UsedByMethod()")] + [KeptMemberInAssembly ("other.dll", "Mono.Linker.Tests.Cases.UnreachableBody.Dependencies.OtherAssemblyNoInstanceCtor", "UnusedSanityCheck()")] + public class DoesNotApplyToSavedAssembly2 { + public static void Main () + { +#if OTHER_INCLUDED + UsedToMarkMethod (null); +#endif + } + +#if OTHER_INCLUDED + [Kept] + static void UsedToMarkMethod (Mono.Linker.Tests.Cases.UnreachableBody.Dependencies.OtherAssemblyNoInstanceCtor.Foo f) + { + f.Method (); + } +#endif + } +}
\ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/UnreachableBody/ExplicitInstructionCheck.cs b/test/Mono.Linker.Tests.Cases/UnreachableBody/ExplicitInstructionCheck.cs new file mode 100644 index 000000000..743496e60 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/UnreachableBody/ExplicitInstructionCheck.cs @@ -0,0 +1,35 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.UnreachableBody { + public class ExplicitInstructionCheck { + public static void Main() + { + UsedToMarkMethod (null); + } + + [Kept] + static void UsedToMarkMethod (Foo f) + { + f.Method (); + } + + [Kept] + class Foo { + [Kept] + [ExpectedInstructionSequence(new [] + { + "ldstr", + "newobj", + "throw" + })] + public void Method () + { + UsedByMethod (); + } + + void UsedByMethod () + { + } + } + } +}
\ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/UnreachableBody/InterfaceMethod.cs b/test/Mono.Linker.Tests.Cases/UnreachableBody/InterfaceMethod.cs new file mode 100644 index 000000000..e28737ad1 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/UnreachableBody/InterfaceMethod.cs @@ -0,0 +1,38 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.UnreachableBody { + public class InterfaceMethod { + public static void Main() + { + UsedToMarkMethod (null); + } + + [Kept] + static void UsedToMarkMethod (Foo f) + { + IFoo i = f; + i.Method (); + } + + [Kept] + interface IFoo { + [Kept] + void Method (); + } + + [Kept] + [KeptInterface (typeof (IFoo))] + class Foo : IFoo { + [Kept] + [ExpectBodyModified] + public void Method () + { + UsedByMethod (); + } + + void UsedByMethod () + { + } + } + } +}
\ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/UnreachableBody/LinkedOtherIncludedLibrary.cs b/test/Mono.Linker.Tests.Cases/UnreachableBody/LinkedOtherIncludedLibrary.cs new file mode 100644 index 000000000..0868122e7 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/UnreachableBody/LinkedOtherIncludedLibrary.cs @@ -0,0 +1,23 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; +using Mono.Linker.Tests.Cases.UnreachableBody.Dependencies; + +namespace Mono.Linker.Tests.Cases.UnreachableBody { + [SetupLinkerArgument ("-r", "other")] + [SetupCompileBefore ("other.dll", new [] {typeof (OtherAssembly)})] + [KeptMemberInAssembly ("other.dll", typeof (OtherAssembly.Foo), "Method()")] + [KeptMemberInAssembly ("other.dll", typeof (OtherAssembly.Foo), "UsedByMethod()")] + [KeptMemberInAssembly ("other.dll", typeof (OtherAssembly), "UnusedSanityCheck()")] + public class LinkedOtherIncludedLibrary { + public static void Main() + { + UsedToMarkMethod (null); + } + + [Kept] + static void UsedToMarkMethod (OtherAssembly.Foo f) + { + f.Method (); + } + } +}
\ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/UnreachableBody/LinkedOtherIncludedLibraryNoInstanceCtor.cs b/test/Mono.Linker.Tests.Cases/UnreachableBody/LinkedOtherIncludedLibraryNoInstanceCtor.cs new file mode 100644 index 000000000..1d4e368fb --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/UnreachableBody/LinkedOtherIncludedLibraryNoInstanceCtor.cs @@ -0,0 +1,27 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.UnreachableBody { + [Define ("OTHER_INCLUDED")] + [SetupLinkerArgument ("-r", "other")] + [SetupCompileBefore ("other.dll", new [] { "Dependencies/OtherAssemblyNoInstanceCtor.il" })] + [KeptMemberInAssembly ("other.dll", "Mono.Linker.Tests.Cases.UnreachableBody.Dependencies.OtherAssemblyNoInstanceCtor/Foo", "Method()")] + [RemovedMemberInAssembly ("other.dll", "Mono.Linker.Tests.Cases.UnreachableBody.Dependencies.OtherAssemblyNoInstanceCtor/Foo", "UsedByMethod()")] + [KeptMemberInAssembly ("other.dll", "Mono.Linker.Tests.Cases.UnreachableBody.Dependencies.OtherAssemblyNoInstanceCtor", "UnusedSanityCheck()")] + public class LinkedOtherIncludedLibraryNoInstanceCtor { + public static void Main () + { +#if OTHER_INCLUDED + UsedToMarkMethod (null); +#endif + } + +#if OTHER_INCLUDED + [Kept] + static void UsedToMarkMethod (Mono.Linker.Tests.Cases.UnreachableBody.Dependencies.OtherAssemblyNoInstanceCtor.Foo f) + { + f.Method (); + } +#endif + } +}
\ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/UnreachableBody/MixOfMethods.cs b/test/Mono.Linker.Tests.Cases/UnreachableBody/MixOfMethods.cs new file mode 100644 index 000000000..17047ad7d --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/UnreachableBody/MixOfMethods.cs @@ -0,0 +1,148 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.UnreachableBody { + public class MixOfMethods { + public static void Main () + { + UseInstanceMethods (null); + Foo.PublicStatic (); + Base2.Base2PublicStatic (); + Base.BasePublicStatic (); + } + + [Kept] + static void UseInstanceMethods (Foo f) + { + f.Method1 (); + f.Method2 (); + f.Method3 (); + + f.BaseMethod1 (); + f.BaseMethod2 (); + f.BaseMethod3 (); + + f.Base2Method1 (); + f.Base2Method2 (); + f.Base2Method3 (); + } + + [Kept] + class Base { + [Kept] + [ExpectBodyModified] + public void BaseMethod1 () + { + UsedByInstance (); + } + + [Kept] + [ExpectBodyModified] + public void BaseMethod2 () + { + UsedByInstance (); + } + + [Kept] + [ExpectBodyModified] + public void BaseMethod3 () + { + UsedByInstance (); + } + + void UsedByInstance () + { + } + + [Kept] + public static void BasePublicStatic () + { + UsedByStatic(); + } + + [Kept] + static void UsedByStatic () + { + } + } + + [Kept] + [KeptBaseType (typeof (Base))] + class Base2 : Base { + [Kept] + [ExpectBodyModified] + public void Base2Method1 () + { + UsedByInstance (); + } + + [Kept] + [ExpectBodyModified] + public void Base2Method2 () + { + UsedByInstance (); + } + + [Kept] + [ExpectBodyModified] + public void Base2Method3 () + { + UsedByInstance (); + } + + void UsedByInstance () + { + } + + [Kept] + public static void Base2PublicStatic () + { + UsedByStatic(); + } + + [Kept] + static void UsedByStatic () + { + } + } + + [Kept] + [KeptBaseType (typeof (Base2))] + class Foo : Base2 { + [Kept] + [ExpectBodyModified] + public void Method1 () + { + UsedByInstance (); + } + + [Kept] + [ExpectBodyModified] + public void Method2 () + { + UsedByInstance (); + } + + [Kept] + [ExpectBodyModified] + public void Method3 () + { + UsedByInstance (); + } + + void UsedByInstance () + { + } + + [Kept] + public static void PublicStatic () + { + UsedByStatic(); + } + + [Kept] + static void UsedByStatic () + { + } + } + } +}
\ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/UnreachableBody/NotWorthConvertingEmpty.cs b/test/Mono.Linker.Tests.Cases/UnreachableBody/NotWorthConvertingEmpty.cs new file mode 100644 index 000000000..8f60a53f8 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/UnreachableBody/NotWorthConvertingEmpty.cs @@ -0,0 +1,27 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.UnreachableBody { + /// <summary> + /// Stubbing an empty method would result in more instructions. It's more size efficient to just leave it alone + /// </summary> + public class NotWorthConvertingEmpty { + public static void Main() + { + UsedToMarkMethod (null); + } + + [Kept] + static void UsedToMarkMethod (Foo f) + { + f.Method (); + } + + [Kept] + class Foo { + [Kept] + public void Method () + { + } + } + } +}
\ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/UnreachableBody/NotWorthConvertingReturnDouble.cs b/test/Mono.Linker.Tests.Cases/UnreachableBody/NotWorthConvertingReturnDouble.cs new file mode 100644 index 000000000..e4399f9cb --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/UnreachableBody/NotWorthConvertingReturnDouble.cs @@ -0,0 +1,28 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.UnreachableBody { + [SetupCompileArgument ("/optimize+")] + public class NotWorthConvertingReturnDouble { + public static void Main () + { + UsedToMarkMethod (null); + } + + [Kept] + static void UsedToMarkMethod (Foo f) + { + f.Method (); + } + + [Kept] + class Foo + { + [Kept] + public double Method () + { + return 0.0; + } + } + } +}
\ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/UnreachableBody/NotWorthConvertingReturnFalse.cs b/test/Mono.Linker.Tests.Cases/UnreachableBody/NotWorthConvertingReturnFalse.cs new file mode 100644 index 000000000..03c80231e --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/UnreachableBody/NotWorthConvertingReturnFalse.cs @@ -0,0 +1,27 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.UnreachableBody { + [SetupCompileArgument ("/optimize+")] + public class NotWorthConvertingReturnFalse { + public static void Main () + { + UsedToMarkMethod (null); + } + + [Kept] + static void UsedToMarkMethod (Foo f) + { + f.Method (); + } + + [Kept] + class Foo { + [Kept] + public bool Method () + { + return false; + } + } + } +}
\ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/UnreachableBody/NotWorthConvertingReturnFloat.cs b/test/Mono.Linker.Tests.Cases/UnreachableBody/NotWorthConvertingReturnFloat.cs new file mode 100644 index 000000000..eac4d70f1 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/UnreachableBody/NotWorthConvertingReturnFloat.cs @@ -0,0 +1,28 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.UnreachableBody { + [SetupCompileArgument ("/optimize+")] + public class NotWorthConvertingReturnFloat { + public static void Main () + { + UsedToMarkMethod (null); + } + + [Kept] + static void UsedToMarkMethod (Foo f) + { + f.Method (); + } + + [Kept] + class Foo + { + [Kept] + public float Method () + { + return 1.0f; + } + } + } +}
\ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/UnreachableBody/NotWorthConvertingReturnInt.cs b/test/Mono.Linker.Tests.Cases/UnreachableBody/NotWorthConvertingReturnInt.cs new file mode 100644 index 000000000..678df5e80 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/UnreachableBody/NotWorthConvertingReturnInt.cs @@ -0,0 +1,90 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.UnreachableBody { + [SetupCompileArgument ("/optimize+")] + public class NotWorthConvertingReturnInt { + public static void Main () + { + UsedToMarkMethod (null); + } + + [Kept] + static void UsedToMarkMethod (Foo f) + { + f.Method (); + f.Method1 (); + f.Method2 (); + f.Method3 (); + f.Method4 (); + f.Method5 (); + f.Method6 (); + f.Method7 (); + f.Method8 (); + f.Method9 (); + } + + [Kept] + class Foo { + [Kept] + public int Method () + { + return 0; + } + + [Kept] + public int Method1 () + { + return 1; + } + + [Kept] + public int Method2 () + { + return 2; + } + + [Kept] + public int Method3 () + { + return 3; + } + + [Kept] + public int Method4 () + { + return 4; + } + + [Kept] + public int Method5 () + { + return 5; + } + + [Kept] + public int Method6 () + { + return 6; + } + + [Kept] + public int Method7 () + { + return 7; + } + + [Kept] + public int Method8 () + { + return 8; + } + + [Kept] + public int Method9 () + { + return 9; + } + } + } +}
\ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/UnreachableBody/NotWorthConvertingReturnLong.cs b/test/Mono.Linker.Tests.Cases/UnreachableBody/NotWorthConvertingReturnLong.cs new file mode 100644 index 000000000..3d21cac45 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/UnreachableBody/NotWorthConvertingReturnLong.cs @@ -0,0 +1,28 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.UnreachableBody { + [SetupCompileArgument ("/optimize+")] + public class NotWorthConvertingReturnLong { + public static void Main () + { + UsedToMarkMethod (null); + } + + [Kept] + static void UsedToMarkMethod (Foo f) + { + f.Method (); + } + + [Kept] + class Foo + { + [Kept] + public long Method () + { + return 9223372036854775807; + } + } + } +}
\ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/UnreachableBody/NotWorthConvertingReturnNull.cs b/test/Mono.Linker.Tests.Cases/UnreachableBody/NotWorthConvertingReturnNull.cs new file mode 100644 index 000000000..13d3234a9 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/UnreachableBody/NotWorthConvertingReturnNull.cs @@ -0,0 +1,27 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.UnreachableBody { + [SetupCompileArgument ("/optimize+")] + public class NotWorthConvertingReturnNull { + public static void Main () + { + UsedToMarkMethod (null); + } + + [Kept] + static void UsedToMarkMethod (Foo f) + { + f.Method (); + } + + [Kept] + class Foo { + [Kept] + public object Method () + { + return null; + } + } + } +}
\ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/UnreachableBody/NotWorthConvertingReturnTrue.cs b/test/Mono.Linker.Tests.Cases/UnreachableBody/NotWorthConvertingReturnTrue.cs new file mode 100644 index 000000000..87b11b5dc --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/UnreachableBody/NotWorthConvertingReturnTrue.cs @@ -0,0 +1,27 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.UnreachableBody { + [SetupCompileArgument ("/optimize+")] + public class NotWorthConvertingReturnTrue { + public static void Main () + { + UsedToMarkMethod (null); + } + + [Kept] + static void UsedToMarkMethod (Foo f) + { + f.Method (); + } + + [Kept] + class Foo { + [Kept] + public bool Method () + { + return true; + } + } + } +}
\ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/UnreachableBody/OverrideOfAVirtual.cs b/test/Mono.Linker.Tests.Cases/UnreachableBody/OverrideOfAVirtual.cs new file mode 100644 index 000000000..696904219 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/UnreachableBody/OverrideOfAVirtual.cs @@ -0,0 +1,44 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.UnreachableBody { + public class OverrideOfAVirtual { + public static void Main() + { + UsedToMarkMethod (null); + } + + [Kept] + static void UsedToMarkMethod (Foo f) + { + f.Method (); + } + + [Kept] + class Base { + [Kept] + [ExpectBodyModified] + public virtual void Method () + { + UsedByMethod(); + } + + void UsedByMethod () + { + } + } + + [Kept] + [KeptBaseType (typeof (Base))] + class Foo : Base { + // A callvirt to Base.Method() appears in the IL so this override can be removed entirely + public override void Method () + { + UsedByMethod (); + } + + void UsedByMethod () + { + } + } + } +}
\ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/UnreachableBody/OverrideOfAbstractAndInterfaceMethodCalledFromLocal.cs b/test/Mono.Linker.Tests.Cases/UnreachableBody/OverrideOfAbstractAndInterfaceMethodCalledFromLocal.cs new file mode 100644 index 000000000..c3785889f --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/UnreachableBody/OverrideOfAbstractAndInterfaceMethodCalledFromLocal.cs @@ -0,0 +1,46 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.UnreachableBody { + public class OverrideOfAbstractAndInterfaceMethodCalledFromLocal { + public static void Main () + { + Foo b = HelperToMarkFooAndRequireBase (); + IBar i = b; + i.Method (); + } + + [Kept] + static Foo HelperToMarkFooAndRequireBase () + { + return null; + } + + [Kept] + abstract class Base { + [Kept] // FIXME : Technically this can be removed + public abstract void Method (); + } + + [Kept] + [KeptBaseType (typeof (Base))] + [KeptInterface (typeof (IBar))] + class Foo : Base, IBar { + [Kept] + [ExpectBodyModified] + public override void Method () + { + UsedByOverride (); + } + + void UsedByOverride () + { + } + } + + [Kept] + interface IBar { + [Kept] + void Method (); + } + } +}
\ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/UnreachableBody/OverrideOfAbstractAndInterfaceMethodCalledFromLocal2.cs b/test/Mono.Linker.Tests.Cases/UnreachableBody/OverrideOfAbstractAndInterfaceMethodCalledFromLocal2.cs new file mode 100644 index 000000000..338efabfa --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/UnreachableBody/OverrideOfAbstractAndInterfaceMethodCalledFromLocal2.cs @@ -0,0 +1,53 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.UnreachableBody { + public class OverrideOfAbstractAndInterfaceMethodCalledFromLocal2 { + public static void Main () + { + Foo f = HelperToMarkFooAndRequireBase (); + f.Method (); + IBar i = GetAnIBar (); + i.Method (); + } + + [Kept] + static Foo HelperToMarkFooAndRequireBase () + { + return null; + } + + [Kept] + static IBar GetAnIBar () + { + return null; + } + + [Kept] + abstract class Base { + [Kept] // FIXME : Technically this can be removed + public abstract void Method (); + } + + [Kept] + [KeptBaseType (typeof (Base))] + [KeptInterface (typeof (IBar))] + class Foo : Base, IBar { + [Kept] + [ExpectBodyModified] + public override void Method () + { + UsedByOverride (); + } + + void UsedByOverride () + { + } + } + + [Kept] + interface IBar { + [Kept] + void Method(); + } + } +}
\ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/UnreachableBody/OverrideOfAbstractAndInterfaceMethodCalledFromLocal3.cs b/test/Mono.Linker.Tests.Cases/UnreachableBody/OverrideOfAbstractAndInterfaceMethodCalledFromLocal3.cs new file mode 100644 index 000000000..f85059918 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/UnreachableBody/OverrideOfAbstractAndInterfaceMethodCalledFromLocal3.cs @@ -0,0 +1,40 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.UnreachableBody { + public class OverrideOfAbstractAndInterfaceMethodCalledFromLocal3 { + public static void Main () + { + Foo b = null; + IBar i = b; + i.Method (); + } + + [Kept] + abstract class Base { + [Kept] // FIXME : Technically this can be removed + public abstract void Method (); + } + + [Kept] + [KeptBaseType (typeof (Base))] + [KeptInterface (typeof (IBar))] + class Foo : Base, IBar { + [Kept] + [ExpectBodyModified] + public override void Method () + { + UsedByOverride (); + } + + void UsedByOverride () + { + } + } + + [Kept] + interface IBar { + [Kept] + void Method (); + } + } +}
\ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/UnreachableBody/OverrideOfAbstractAndInterfaceMethodWhenInterfaceRemoved2.cs b/test/Mono.Linker.Tests.Cases/UnreachableBody/OverrideOfAbstractAndInterfaceMethodWhenInterfaceRemoved2.cs new file mode 100644 index 000000000..4a2832402 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/UnreachableBody/OverrideOfAbstractAndInterfaceMethodWhenInterfaceRemoved2.cs @@ -0,0 +1,58 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.UnreachableBody { + public class OverrideOfAbstractAndInterfaceMethodWhenInterfaceRemoved2 { + public static void Main () + { + Foo f = HelperToMarkFooAndRequireBase (); + f.Method (); + // Use IBar in another method so that IBar can be removed from Foo + HelperToMarkIBar (); + } + + [Kept] + static Foo HelperToMarkFooAndRequireBase () + { + return null; + } + + [Kept] + static IBar GetAnIBar () + { + return null; + } + + [Kept] + static void HelperToMarkIBar() + { + GetAnIBar().Method(); + } + + [Kept] + abstract class Base { + [Kept] + public abstract void Method (); + } + + [Kept] + [KeptBaseType (typeof (Base))] + class Foo : Base, IBar { + [Kept] + [ExpectBodyModified] + public override void Method () + { + UsedByOverride (); + } + + void UsedByOverride () + { + } + } + + [Kept] + interface IBar { + [Kept] + void Method(); + } + } +}
\ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/UnreachableBody/OverrideOfAbstractIsStubbed.cs b/test/Mono.Linker.Tests.Cases/UnreachableBody/OverrideOfAbstractIsStubbed.cs new file mode 100644 index 000000000..3301b4b6a --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/UnreachableBody/OverrideOfAbstractIsStubbed.cs @@ -0,0 +1,39 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.UnreachableBody { + public class OverrideOfAbstractIsStubbed { + public static void Main () + { + Base b = HelperToMarkFooAndRequireBase (); + b.Method (); + } + + [Kept] + static Foo HelperToMarkFooAndRequireBase () + { + return null; + } + + [Kept] + abstract class Base + { + [Kept] + public abstract void Method (); + } + + [Kept] + [KeptBaseType (typeof (Base))] + class Foo : Base { + [Kept] + [ExpectBodyModified] + public override void Method () + { + UsedByOverride (); + } + + void UsedByOverride () + { + } + } + } +}
\ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/UnreachableBody/OverrideOfAbstractIsStubbedWithUnusedInterface.cs b/test/Mono.Linker.Tests.Cases/UnreachableBody/OverrideOfAbstractIsStubbedWithUnusedInterface.cs new file mode 100644 index 000000000..11087de08 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/UnreachableBody/OverrideOfAbstractIsStubbedWithUnusedInterface.cs @@ -0,0 +1,44 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.UnreachableBody { + public class OverrideOfAbstractIsStubbedWithUnusedInterface { + public static void Main () + { + Base b = HelperToMarkFooAndRequireBase (); + b.Method (); + } + + [Kept] + static Foo HelperToMarkFooAndRequireBase () + { + return null; + } + + [Kept] + abstract class Base + { + [Kept] + public abstract void Method (); + } + + [Kept] + [KeptBaseType (typeof (Base))] + class Foo : Base, IBar { + [Kept] + [ExpectBodyModified] + public override void Method () + { + UsedByOverride (); + } + + void UsedByOverride () + { + } + } + + interface IBar + { + void Method(); + } + } +}
\ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/UnreachableBody/SimpleGetter.cs b/test/Mono.Linker.Tests.Cases/UnreachableBody/SimpleGetter.cs new file mode 100644 index 000000000..1c93c6a8f --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/UnreachableBody/SimpleGetter.cs @@ -0,0 +1,22 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.UnreachableBody { + public class SimpleGetter { + public static void Main() + { + UsedToMarkMethod (null); + } + + [Kept] + static void UsedToMarkMethod (Foo f) + { + var tmp = f.Property; + } + + [Kept] + class Foo { + [Kept] + public string Property { [Kept] [ExpectBodyModified] get; set; } + } + } +}
\ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/UnreachableBody/SimpleMethod.cs b/test/Mono.Linker.Tests.Cases/UnreachableBody/SimpleMethod.cs new file mode 100644 index 000000000..f69f1f5ab --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/UnreachableBody/SimpleMethod.cs @@ -0,0 +1,30 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.UnreachableBody { + public class SimpleMethod { + public static void Main() + { + UsedToMarkMethod (null); + } + + [Kept] + static void UsedToMarkMethod (Foo f) + { + f.Method (); + } + + [Kept] + class Foo { + [Kept] + [ExpectBodyModified] + public void Method () + { + UsedByMethod (); + } + + void UsedByMethod () + { + } + } + } +}
\ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/UnreachableBody/SimpleSetter.cs b/test/Mono.Linker.Tests.Cases/UnreachableBody/SimpleSetter.cs new file mode 100644 index 000000000..54c22d65c --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/UnreachableBody/SimpleSetter.cs @@ -0,0 +1,22 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.UnreachableBody { + public class SimpleSetter { + public static void Main() + { + UsedToMarkMethod (null); + } + + [Kept] + static void UsedToMarkMethod (Foo f) + { + f.Property = string.Empty; + } + + [Kept] + class Foo { + [Kept] + public string Property { get; [Kept] [ExpectBodyModified] set; } + } + } +}
\ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/UnreachableBody/WorksWithLinkXml.cs b/test/Mono.Linker.Tests.Cases/UnreachableBody/WorksWithLinkXml.cs new file mode 100644 index 000000000..a366a50b0 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/UnreachableBody/WorksWithLinkXml.cs @@ -0,0 +1,23 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.UnreachableBody { + public class WorksWithLinkXml { + public static void Main() + { + } + + [Kept] + class Foo { + [Kept] + [ExpectBodyModified] + public void InstanceMethod () + { + UsedByMethod (); + } + + void UsedByMethod () + { + } + } + } +}
\ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/UnreachableBody/WorksWithLinkXml.xml b/test/Mono.Linker.Tests.Cases/UnreachableBody/WorksWithLinkXml.xml new file mode 100644 index 000000000..1e89edf42 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/UnreachableBody/WorksWithLinkXml.xml @@ -0,0 +1,7 @@ +<linker> + <assembly fullname="test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"> + <type fullname="Mono.Linker.Tests.Cases.UnreachableBody.WorksWithLinkXml/Foo"> + <method signature="System.Void InstanceMethod()"/> + </type> + </assembly> +</linker> diff --git a/test/Mono.Linker.Tests.Cases/UnreachableBody/WorksWithPreserveDependency.cs b/test/Mono.Linker.Tests.Cases/UnreachableBody/WorksWithPreserveDependency.cs new file mode 100644 index 000000000..3a6ed1d27 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/UnreachableBody/WorksWithPreserveDependency.cs @@ -0,0 +1,31 @@ +using System.Runtime.CompilerServices; +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.UnreachableBody { + public class WorksWithPreserveDependency { + public static void Main() + { + Foo.StaticMethod (); + } + + [Kept] + class Foo { + [Kept] + [PreserveDependency ("InstanceMethod()")] + public static void StaticMethod () + { + } + + [Kept] + [ExpectBodyModified] + public void InstanceMethod () + { + UsedByMethod (); + } + + void UsedByMethod () + { + } + } + } +}
\ No newline at end of file diff --git a/test/Mono.Linker.Tests/TestCases/TestDatabase.cs b/test/Mono.Linker.Tests/TestCases/TestDatabase.cs index e5e9085f2..17e380783 100644 --- a/test/Mono.Linker.Tests/TestCases/TestDatabase.cs +++ b/test/Mono.Linker.Tests/TestCases/TestDatabase.cs @@ -125,6 +125,11 @@ namespace Mono.Linker.Tests.TestCases { return NUnitCasesBySuiteName ("CommandLine"); } + + public static IEnumerable<TestCaseData> UnreachableBodyTests () + { + return NUnitCasesBySuiteName ("UnreachableBody"); + } public static TestCaseCollector CreateCollector () { diff --git a/test/Mono.Linker.Tests/TestCases/TestSuites.cs b/test/Mono.Linker.Tests/TestCases/TestSuites.cs index 994448f68..210442e28 100644 --- a/test/Mono.Linker.Tests/TestCases/TestSuites.cs +++ b/test/Mono.Linker.Tests/TestCases/TestSuites.cs @@ -144,6 +144,12 @@ namespace Mono.Linker.Tests.TestCases Run (testCase); } + [TestCaseSource (typeof (TestDatabase), nameof (TestDatabase.UnreachableBodyTests))] + public void UnreachableBodyTests (TestCase testCase) + { + Run (testCase); + } + protected virtual void Run (TestCase testCase) { var runner = new TestRunner (new ObjectFactory ()); |