Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/linker.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJackson Schuster <36744439+jtschuster@users.noreply.github.com>2022-10-28 01:19:52 +0300
committerGitHub <noreply@github.com>2022-10-28 01:19:52 +0300
commit83068876a4d126026dde79158360364321ebd7c9 (patch)
tree9193dc7b873d5e4d4e19f0d830a0430d06ed7893
parentef10f6dbaa8aae99804e3deaaba6a276a5668285 (diff)
Check for marking virtual method due to base only when state changes (#3073)
Instead of checking every virtual method to see if it should be kept due to a base method every iteration of the MarkStep pipeline, check each method only when its relevant state has changed. Co-authored-by: Sven Boemer <sbomer@gmail.com>
-rw-r--r--src/linker/Linker.Steps/MarkStep.cs218
-rw-r--r--src/linker/Linker.Steps/SealerStep.cs11
-rw-r--r--src/linker/Linker.Steps/ValidateVirtualMethodAnnotationsStep.cs10
-rw-r--r--src/linker/Linker/Annotations.cs12
-rw-r--r--src/linker/Linker/TypeMapInfo.cs26
-rw-r--r--test/Mono.Linker.Tests.Cases/Attributes/TypeWithDynamicInterfaceCastableImplementationAttributeIsKept.cs4
-rw-r--r--test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceVariants.cs23
-rw-r--r--test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/StaticInterfaceMethods/UnusedInterfacesInPreservedScope.cs7
8 files changed, 160 insertions, 151 deletions
diff --git a/src/linker/Linker.Steps/MarkStep.cs b/src/linker/Linker.Steps/MarkStep.cs
index 71b6e48d4..e8c29e29e 100644
--- a/src/linker/Linker.Steps/MarkStep.cs
+++ b/src/linker/Linker.Steps/MarkStep.cs
@@ -607,12 +607,12 @@ namespace Mono.Linker.Steps
}
// OverrideInformation for interfaces in PreservedScope aren't added yet
foreach (var method in type.Methods) {
- var bases = Annotations.GetBaseMethods (method);
- if (bases is null)
+ var baseOverrideInformations = Annotations.GetBaseMethods (method);
+ if (baseOverrideInformations is null)
continue;
- foreach (var @base in bases) {
- if (@base.DeclaringType is not null && @base.DeclaringType.IsInterface && IgnoreScope (@base.DeclaringType.Scope))
- _interfaceOverrides.Add ((new OverrideInformation (@base, method, Context), ScopeStack.CurrentScope));
+ foreach (var ov in baseOverrideInformations) {
+ if (ov.Base.DeclaringType is not null && ov.Base.DeclaringType.IsInterface && IgnoreScope (ov.Base.DeclaringType.Scope))
+ _interfaceOverrides.Add ((ov, ScopeStack.CurrentScope));
}
}
}
@@ -707,12 +707,6 @@ namespace Mono.Linker.Steps
{
Annotations.EnqueueVirtualMethod (method);
- var overrides = Annotations.GetOverrides (method);
- if (overrides != null) {
- foreach (OverrideInformation @override in overrides)
- ProcessOverride (@override);
- }
-
var defaultImplementations = Annotations.GetDefaultInterfaceImplementations (method);
if (defaultImplementations != null) {
foreach (var defaultImplementationInfo in defaultImplementations) {
@@ -721,43 +715,63 @@ namespace Mono.Linker.Steps
}
}
- void ProcessOverride (OverrideInformation overrideInformation)
+ /// <summary>
+ /// Returns true if the Override in <paramref name="overrideInformation"/> should be marked because it is needed by the base method.
+ /// Does not take into account if the base method is in a preserved scope.
+ /// Assumes the base method is marked.
+ /// </summary>
+ // TODO: Move interface method marking logic here https://github.com/dotnet/linker/issues/3090
+ bool ShouldMarkOverrideForBase (OverrideInformation overrideInformation)
{
- var method = overrideInformation.Override;
- var @base = overrideInformation.Base;
- if (!Annotations.IsMarked (method.DeclaringType))
- return;
-
- if (Annotations.IsProcessed (method))
- return;
-
- if (Annotations.IsMarked (method))
- return;
-
- var isInstantiated = Annotations.IsInstantiated (method.DeclaringType);
-
- // Handle interface methods once we know more about whether the type is instantiated or relevant to variant casting
if (overrideInformation.IsOverrideOfInterfaceMember) {
_interfaceOverrides.Add ((overrideInformation, ScopeStack.CurrentScope));
- return;
+ return false;
}
- // Interface static veitual methods will be abstract and will also by pass this check to get marked
- if (!isInstantiated && !@base.IsAbstract && Context.IsOptimizationEnabled (CodeOptimizations.OverrideRemoval, method))
- return;
+ if (!Context.IsOptimizationEnabled (CodeOptimizations.OverrideRemoval, overrideInformation.Override))
+ return true;
+
+ // Methods on instantiated types that override a ov.Override from a base type (not an interface) should be marked
+ // Interface ov.Overrides should only be marked if the interfaceImplementation is marked, which is handled below
+ if (Annotations.IsInstantiated (overrideInformation.Override.DeclaringType))
+ return true;
+
+ // Direct overrides of marked abstract ov.Overrides must be marked or we get invalid IL.
+ // Overrides further in the hierarchy will override the direct override (which will be implemented by the above rule), so we don't need to worry about invalid IL.
+ if (overrideInformation.Base.IsAbstract)
+ return true;
+
+ return false;
+ }
- // Only track instantiations if override removal is enabled and the type is instantiated.
- // If it's disabled, all overrides are kept, so there's no instantiation site to blame.
- if (Context.IsOptimizationEnabled (CodeOptimizations.OverrideRemoval, method) && isInstantiated) {
- MarkMethod (method, new DependencyInfo (DependencyKind.OverrideOnInstantiatedType, method.DeclaringType), ScopeStack.CurrentScope.Origin);
+ /// <summary>
+ /// Marks the Override of <paramref name="overrideInformation"/> with the correct reason. Should be called when <see cref="ShouldMarkOverrideForBase(OverrideInformation, bool)"/> returns true.
+ /// </summary>
+ // TODO: Take into account a base method in preserved scope
+ void MarkOverrideForBaseMethod (OverrideInformation overrideInformation)
+ {
+ if (Context.IsOptimizationEnabled (CodeOptimizations.OverrideRemoval, overrideInformation.Override) && Annotations.IsInstantiated (overrideInformation.Override.DeclaringType)) {
+ MarkMethod (overrideInformation.Override, new DependencyInfo (DependencyKind.OverrideOnInstantiatedType, overrideInformation.Override.DeclaringType), ScopeStack.CurrentScope.Origin);
} else {
// If the optimization is disabled or it's an abstract type, we just mark it as a normal override.
- Debug.Assert (!Context.IsOptimizationEnabled (CodeOptimizations.OverrideRemoval, method) || @base.IsAbstract);
- MarkMethod (method, new DependencyInfo (DependencyKind.Override, @base), ScopeStack.CurrentScope.Origin);
+ Debug.Assert (!Context.IsOptimizationEnabled (CodeOptimizations.OverrideRemoval, overrideInformation.Override) || overrideInformation.Base.IsAbstract);
+ MarkMethod (overrideInformation.Override, new DependencyInfo (DependencyKind.Override, overrideInformation.Base), ScopeStack.CurrentScope.Origin);
}
+ }
- if (method.IsVirtual)
- ProcessVirtualMethod (method);
+ void MarkMethodIfNeededByBaseMethod (MethodDefinition method)
+ {
+ Debug.Assert (Annotations.IsMarked (method.DeclaringType));
+
+ var bases = Annotations.GetBaseMethods (method);
+ if (bases is null)
+ return;
+
+ var markedBaseMethods = bases.Where (ov => Annotations.IsMarked (ov.Base) || IgnoreScope (ov.Base.DeclaringType.Scope));
+ foreach (var ov in markedBaseMethods) {
+ if (ShouldMarkOverrideForBase (ov))
+ MarkOverrideForBaseMethod (ov);
+ }
}
/// <summary>
@@ -2026,6 +2040,10 @@ namespace Mono.Linker.Steps
_typesWithInterfaces.Add ((type, ScopeStack.CurrentScope));
if (type.HasMethods) {
+ // TODO: MarkMethodIfNeededByBaseMethod should include logic for IsMethodNeededBytTypeDueToPreservedScope
+ foreach (var method in type.Methods) {
+ MarkMethodIfNeededByBaseMethod (method);
+ }
// For methods that must be preserved, blame the declaring type.
MarkMethodsIf (type.Methods, IsMethodNeededByTypeDueToPreservedScope, new DependencyInfo (DependencyKind.VirtualNeededDueToPreservedScope, type), ScopeStack.CurrentScope.Origin);
if (ShouldMarkTypeStaticConstructor (type) && reason.Kind != DependencyKind.TriggersCctorForCalledMethod) {
@@ -2333,6 +2351,28 @@ namespace Mono.Linker.Steps
if (ShouldMarkInterfaceImplementation (type, iface))
MarkInterfaceImplementation (iface, new MessageOrigin (type));
}
+
+ bool ShouldMarkInterfaceImplementation (TypeDefinition type, InterfaceImplementation iface)
+ {
+ if (Annotations.IsMarked (iface))
+ return false;
+
+ if (!Context.IsOptimizationEnabled (CodeOptimizations.UnusedInterfaces, type))
+ return true;
+
+ if (Context.Resolve (iface.InterfaceType) is not TypeDefinition resolvedInterfaceType)
+ return false;
+
+ if (Annotations.IsMarked (resolvedInterfaceType))
+ return true;
+
+ // It's hard to know if a com or windows runtime interface will be needed from managed code alone,
+ // so as a precaution we will mark these interfaces once the type is instantiated
+ if (resolvedInterfaceType.IsImport || resolvedInterfaceType.IsWindowsRuntime)
+ return true;
+
+ return IsFullyPreserved (type);
+ }
}
void MarkGenericParameterProvider (IGenericParameterProvider provider)
@@ -2377,19 +2417,19 @@ namespace Mono.Linker.Steps
if (base_list == null)
return false;
- foreach (MethodDefinition @base in base_list) {
+ foreach (OverrideInformation ov in base_list) {
// Skip interface methods, they will be captured later by IsInterfaceImplementationMethodNeededByTypeDueToInterface
- if (@base.DeclaringType.IsInterface)
+ if (ov.Base.DeclaringType.IsInterface)
continue;
- if (!IgnoreScope (@base.DeclaringType.Scope) && !IsMethodNeededByTypeDueToPreservedScope (@base))
+ if (!IgnoreScope (ov.Base.DeclaringType.Scope) && !IsMethodNeededByTypeDueToPreservedScope (ov.Base))
continue;
// If the type is marked, we need to keep overrides of abstract members defined in assemblies
// that are copied to keep the IL valid.
// However, if the base method is a non-abstract virtual (has an implementation on the base type), then we don't need to keep the override
// until the type could be instantiated
- if (!@base.IsAbstract)
+ if (!ov.Base.IsAbstract)
continue;
return true;
@@ -2442,35 +2482,6 @@ namespace Mono.Linker.Steps
return Annotations.IsInstantiated (method.DeclaringType);
}
- /// <summary>
- /// Returns true if any of the base methods of <paramref name="method" /> is defined in an assembly that is not trimmed (i.e. action!=trim).
- /// This is meant to be used on methods from a type that is known to be instantiated.
- /// </summary>
- /// <remarks>
- /// This is very similar to <see cref="IsMethodNeededByTypeDueToPreservedScope (MethodDefinition)"/>,
- /// but will mark methods from an interface defined in a non-link assembly regardless of the optimization, and does not handle static interface methods.
- /// </remarks>
- bool IsMethodNeededByInstantiatedTypeDueToPreservedScope (MethodDefinition method)
- {
- // Any static interface methods are captured by <see cref="IsVirtualNeededByTypeDueToPreservedScope">, which should be called on all relevant methods so no need to check again here.
- if (!method.IsVirtual)
- return false;
-
- var base_list = Annotations.GetBaseMethods (method);
- if (base_list == null)
- return false;
-
- foreach (MethodDefinition @base in base_list) {
- if (IgnoreScope (@base.DeclaringType.Scope))
- return true;
-
- if (IsMethodNeededByTypeDueToPreservedScope (@base))
- return true;
- }
-
- return false;
- }
-
static bool IsSpecialSerializationConstructor (MethodDefinition method)
{
if (!method.IsInstanceConstructor ())
@@ -3184,6 +3195,13 @@ namespace Mono.Linker.Steps
MarkBaseMethods (method);
+ if (Annotations.GetOverrides (method) is IEnumerable<OverrideInformation> overrides) {
+ foreach (var @override in overrides) {
+ if (ShouldMarkOverrideForBase (@override))
+ MarkOverrideForBaseMethod (@override);
+ }
+ }
+
MarkType (method.ReturnType, new DependencyInfo (DependencyKind.ReturnType, method));
MarkCustomAttributes (method.MethodReturnType, new DependencyInfo (DependencyKind.ReturnTypeAttribute, method));
MarkMarshalSpec (method.MethodReturnType, new DependencyInfo (DependencyKind.ReturnTypeMarshalSpec, method));
@@ -3239,29 +3257,16 @@ namespace Mono.Linker.Steps
MarkInterfaceImplementations (type);
- foreach (var method in GetRequiredMethodsForInstantiatedType (type))
- MarkMethod (method, new DependencyInfo (DependencyKind.MethodForInstantiatedType, type), ScopeStack.CurrentScope.Origin);
+ // Requires interface implementations to be marked first
+ foreach (var method in type.Methods) {
+ MarkMethodIfNeededByBaseMethod (method);
+ }
MarkImplicitlyUsedFields (type);
DoAdditionalInstantiatedTypeProcessing (type);
}
- /// <summary>
- /// Collect methods that must be marked once a type is determined to be instantiated.
- ///
- /// This method is virtual in order to give derived mark steps an opportunity to modify the collection of methods that are needed
- /// </summary>
- /// <param name="type"></param>
- /// <returns></returns>
- protected virtual IEnumerable<MethodDefinition> GetRequiredMethodsForInstantiatedType (TypeDefinition type)
- {
- foreach (var method in type.Methods) {
- if (IsMethodNeededByInstantiatedTypeDueToPreservedScope (method))
- yield return method;
- }
- }
-
void MarkExplicitInterfaceImplementation (MethodDefinition method, MethodReference ov)
{
if (Context.Resolve (ov) is not MethodDefinition resolvedOverride)
@@ -3355,18 +3360,18 @@ namespace Mono.Linker.Steps
if (base_methods == null)
return;
- foreach (MethodDefinition base_method in base_methods) {
+ foreach (OverrideInformation ov in base_methods) {
// We should add all interface base methods to _virtual_methods for virtual override annotation validation
// Interfaces from preserved scope will be missed if we don't add them here
// This will produce warnings for all interface methods and virtual methods regardless of whether the interface, interface implementation, or interface method is kept or not.
- if (base_method.DeclaringType.IsInterface && !method.DeclaringType.IsInterface) {
+ if (ov.Base.DeclaringType.IsInterface && !method.DeclaringType.IsInterface) {
// These are all virtual, no need to check IsVirtual before adding to list
- _virtual_methods.Add ((base_method, ScopeStack.CurrentScope));
+ _virtual_methods.Add ((ov.Base, ScopeStack.CurrentScope));
continue;
}
- MarkMethod (base_method, new DependencyInfo (DependencyKind.BaseMethod, method), ScopeStack.CurrentScope.Origin);
- MarkBaseMethods (base_method);
+ MarkMethod (ov.Base, new DependencyInfo (DependencyKind.BaseMethod, method), ScopeStack.CurrentScope.Origin);
+ MarkBaseMethods (ov.Base);
}
}
@@ -3663,8 +3668,9 @@ namespace Mono.Linker.Steps
var operand = (TypeReference) instruction.Operand;
switch (instruction.OpCode.Code) {
case Code.Newarr:
- if (Context.TryResolve (operand) is TypeDefinition typeDefinition)
+ if (Context.TryResolve (operand) is TypeDefinition typeDefinition) {
Annotations.MarkRelevantToVariantCasting (typeDefinition);
+ }
break;
case Code.Isinst:
if (operand is TypeSpecification || operand is GenericParameter)
@@ -3694,33 +3700,12 @@ namespace Mono.Linker.Steps
}
}
- protected virtual bool ShouldMarkInterfaceImplementation (TypeDefinition type, InterfaceImplementation iface)
- {
- if (Annotations.IsMarked (iface))
- return false;
-
- if (!Context.IsOptimizationEnabled (CodeOptimizations.UnusedInterfaces, type))
- return true;
-
- if (Context.Resolve (iface.InterfaceType) is not TypeDefinition resolvedInterfaceType)
- return false;
-
- if (Annotations.IsMarked (resolvedInterfaceType))
- return true;
-
- // It's hard to know if a com or windows runtime interface will be needed from managed code alone,
- // so as a precaution we will mark these interfaces once the type is instantiated
- if (resolvedInterfaceType.IsImport || resolvedInterfaceType.IsWindowsRuntime)
- return true;
-
-
- return IsFullyPreserved (type);
- }
protected internal virtual void MarkInterfaceImplementation (InterfaceImplementation iface, MessageOrigin? origin = null, DependencyInfo? reason = null)
{
if (Annotations.IsMarked (iface))
return;
+ Annotations.MarkProcessed (iface, reason ?? new DependencyInfo (DependencyKind.InterfaceImplementationOnType, ScopeStack.CurrentScope.Origin.Provider));
using var localScope = origin.HasValue ? ScopeStack.PushScope (origin.Value) : null;
@@ -3728,7 +3713,6 @@ namespace Mono.Linker.Steps
MarkCustomAttributes (iface, new DependencyInfo (DependencyKind.CustomAttribute, iface));
// Blame the interface type on the interfaceimpl itself.
MarkType (iface.InterfaceType, reason ?? new DependencyInfo (DependencyKind.InterfaceImplementationInterfaceType, iface));
- Annotations.MarkProcessed (iface, reason ?? new DependencyInfo (DependencyKind.InterfaceImplementationOnType, ScopeStack.CurrentScope.Origin.Provider));
}
//
diff --git a/src/linker/Linker.Steps/SealerStep.cs b/src/linker/Linker.Steps/SealerStep.cs
index 52d6957f2..dbc64f0f1 100644
--- a/src/linker/Linker.Steps/SealerStep.cs
+++ b/src/linker/Linker.Steps/SealerStep.cs
@@ -96,7 +96,7 @@ namespace Mono.Linker.Steps
//
// cannot de-virtualize nor seal methods if something overrides them
//
- if (IsAnyMarked (overrides))
+ if (IsAnyOverrideMarked (overrides))
continue;
SealMethod (method);
@@ -108,7 +108,7 @@ namespace Mono.Linker.Steps
var bases = Annotations.GetBaseMethods (method);
// Devirtualize if a method is not override to existing marked methods
- if (!IsAnyMarked (bases))
+ if (!IsAnyBaseMarked (bases))
method.IsVirtual = method.IsFinal = method.IsNewSlot = false;
}
}
@@ -123,7 +123,7 @@ namespace Mono.Linker.Steps
method.IsFinal = true;
}
- bool IsAnyMarked (IEnumerable<OverrideInformation>? list)
+ bool IsAnyOverrideMarked (IEnumerable<OverrideInformation>? list)
{
if (list == null)
return false;
@@ -135,12 +135,13 @@ namespace Mono.Linker.Steps
return false;
}
- bool IsAnyMarked (List<MethodDefinition>? list)
+ bool IsAnyBaseMarked (IEnumerable<OverrideInformation>? list)
{
if (list == null)
return false;
+
foreach (var m in list) {
- if (Annotations.IsMarked (m))
+ if (Annotations.IsMarked (m.Base))
return true;
}
return false;
diff --git a/src/linker/Linker.Steps/ValidateVirtualMethodAnnotationsStep.cs b/src/linker/Linker.Steps/ValidateVirtualMethodAnnotationsStep.cs
index 7470186a2..a13876880 100644
--- a/src/linker/Linker.Steps/ValidateVirtualMethodAnnotationsStep.cs
+++ b/src/linker/Linker.Steps/ValidateVirtualMethodAnnotationsStep.cs
@@ -13,11 +13,11 @@ namespace Mono.Linker.Steps
{
var annotations = Context.Annotations;
foreach (var method in annotations.VirtualMethodsWithAnnotationsToValidate) {
- var baseMethods = annotations.GetBaseMethods (method);
- if (baseMethods != null) {
- foreach (var baseMethod in baseMethods) {
- annotations.FlowAnnotations.ValidateMethodAnnotationsAreSame (method, baseMethod);
- ValidateMethodRequiresUnreferencedCodeAreSame (method, baseMethod);
+ var baseOverrideInformations = annotations.GetBaseMethods (method);
+ if (baseOverrideInformations != null) {
+ foreach (var baseOv in baseOverrideInformations) {
+ annotations.FlowAnnotations.ValidateMethodAnnotationsAreSame (method, baseOv.Base);
+ ValidateMethodRequiresUnreferencedCodeAreSame (method, baseOv.Base);
}
}
diff --git a/src/linker/Linker/Annotations.cs b/src/linker/Linker/Annotations.cs
index 227468101..4729ec773 100644
--- a/src/linker/Linker/Annotations.cs
+++ b/src/linker/Linker/Annotations.cs
@@ -436,6 +436,9 @@ namespace Mono.Linker
return public_api.Contains (provider);
}
+ /// <summary>
+ /// Returns a list of all known methods that override <paramref name="method"/>. The list may be incomplete if other overrides exist in assemblies that haven't been processed by TypeMapInfo yet
+ /// </summary>
public IEnumerable<OverrideInformation>? GetOverrides (MethodDefinition method)
{
return TypeMapInfo.GetOverrides (method);
@@ -446,7 +449,14 @@ namespace Mono.Linker
return TypeMapInfo.GetDefaultInterfaceImplementations (method);
}
- public List<MethodDefinition>? GetBaseMethods (MethodDefinition method)
+ /// <summary>
+ /// Returns all base methods that <paramref name="method"/> overrides.
+ /// This includes methods on <paramref name="method"/>'s declaring type's base type (but not methods higher up in the type hierarchy),
+ /// methods on an interface that <paramref name="method"/>'s delcaring type implements,
+ /// and methods an interface implemented by a derived type of <paramref name="method"/>'s declaring type if the derived type uses <paramref name="method"/> as the implementing method.
+ /// The list may be incomplete if there are derived types in assemblies that havent been processed yet that use <paramref name="method"/> to implement an interface.
+ /// </summary>
+ public List<OverrideInformation>? GetBaseMethods (MethodDefinition method)
{
return TypeMapInfo.GetBaseMethods (method);
}
diff --git a/src/linker/Linker/TypeMapInfo.cs b/src/linker/Linker/TypeMapInfo.cs
index c8ef8cc8f..416055021 100644
--- a/src/linker/Linker/TypeMapInfo.cs
+++ b/src/linker/Linker/TypeMapInfo.cs
@@ -40,7 +40,7 @@ namespace Mono.Linker
{
readonly HashSet<AssemblyDefinition> assemblies = new HashSet<AssemblyDefinition> ();
readonly LinkContext context;
- protected readonly Dictionary<MethodDefinition, List<MethodDefinition>> base_methods = new Dictionary<MethodDefinition, List<MethodDefinition>> ();
+ protected readonly Dictionary<MethodDefinition, List<OverrideInformation>> base_methods = new Dictionary<MethodDefinition, List<OverrideInformation>> ();
protected readonly Dictionary<MethodDefinition, List<OverrideInformation>> override_methods = new Dictionary<MethodDefinition, List<OverrideInformation>> ();
protected readonly Dictionary<MethodDefinition, List<(TypeDefinition InstanceType, InterfaceImplementation ImplementationProvider)>> default_interface_implementations = new Dictionary<MethodDefinition, List<(TypeDefinition, InterfaceImplementation)>> ();
@@ -58,6 +58,9 @@ namespace Mono.Linker
MapType (type);
}
+ /// <summary>
+ /// Returns a list of all known methods that override <paramref name="method"/>. The list may be incomplete if other overrides exist in assemblies that haven't been processed by TypeMapInfo yet
+ /// </summary>
public IEnumerable<OverrideInformation>? GetOverrides (MethodDefinition method)
{
EnsureProcessed (method.Module.Assembly);
@@ -65,10 +68,17 @@ namespace Mono.Linker
return overrides;
}
- public List<MethodDefinition>? GetBaseMethods (MethodDefinition method)
+ /// <summary>
+ /// Returns all base methods that <paramref name="method"/> overrides.
+ /// This includes the closest overridden virtual method on <paramref name="method"/>'s base types
+ /// methods on an interface that <paramref name="method"/>'s declaring type implements,
+ /// and methods an interface implemented by a derived type of <paramref name="method"/>'s declaring type if the derived type uses <paramref name="method"/> as the implementing method.
+ /// The list may be incomplete if there are derived types in assemblies that havent been processed yet that use <paramref name="method"/> to implement an interface.
+ /// </summary>
+ public List<OverrideInformation>? GetBaseMethods (MethodDefinition method)
{
EnsureProcessed (method.Module.Assembly);
- base_methods.TryGetValue (method, out List<MethodDefinition>? bases);
+ base_methods.TryGetValue (method, out List<OverrideInformation>? bases);
return bases;
}
@@ -78,14 +88,14 @@ namespace Mono.Linker
return ret;
}
- public void AddBaseMethod (MethodDefinition method, MethodDefinition @base)
+ public void AddBaseMethod (MethodDefinition method, MethodDefinition @base, InterfaceImplementation? matchingInterfaceImplementation)
{
- if (!base_methods.TryGetValue (method, out List<MethodDefinition>? methods)) {
- methods = new List<MethodDefinition> ();
+ if (!base_methods.TryGetValue (method, out List<OverrideInformation>? methods)) {
+ methods = new List<OverrideInformation> ();
base_methods[method] = methods;
}
- methods.Add (@base);
+ methods.Add (new OverrideInformation (@base, method, context, matchingInterfaceImplementation));
}
public void AddOverride (MethodDefinition @base, MethodDefinition @override, InterfaceImplementation? matchingInterfaceImplementation = null)
@@ -205,7 +215,7 @@ namespace Mono.Linker
void AnnotateMethods (MethodDefinition @base, MethodDefinition @override, InterfaceImplementation? matchingInterfaceImplementation = null)
{
- AddBaseMethod (@override, @base);
+ AddBaseMethod (@override, @base, matchingInterfaceImplementation);
AddOverride (@base, @override, matchingInterfaceImplementation);
}
diff --git a/test/Mono.Linker.Tests.Cases/Attributes/TypeWithDynamicInterfaceCastableImplementationAttributeIsKept.cs b/test/Mono.Linker.Tests.Cases/Attributes/TypeWithDynamicInterfaceCastableImplementationAttributeIsKept.cs
index 7065930ac..c6dd27cb5 100644
--- a/test/Mono.Linker.Tests.Cases/Attributes/TypeWithDynamicInterfaceCastableImplementationAttributeIsKept.cs
+++ b/test/Mono.Linker.Tests.Cases/Attributes/TypeWithDynamicInterfaceCastableImplementationAttributeIsKept.cs
@@ -16,6 +16,8 @@ namespace Mono.Linker.Tests.Cases.Attributes
[KeptMemberInAssembly ("impl", "Mono.Linker.Tests.Cases.Attributes.Dependencies.IReferencedAssemblyImpl", "Foo()")]
[KeptInterfaceOnTypeInAssembly ("impl", "Mono.Linker.Tests.Cases.Attributes.Dependencies.IReferencedAssemblyImpl",
"interface", "Mono.Linker.Tests.Cases.Attributes.Dependencies.IReferencedAssembly")]
+ [SetupLinkerTrimMode ("link")]
+ [IgnoreDescriptors (false)]
public class TypeWithDynamicInterfaceCastableImplementationAttributeIsKept
{
public static void Main ()
@@ -54,6 +56,7 @@ namespace Mono.Linker.Tests.Cases.Attributes
#if NETCOREAPP
[Kept]
[KeptMember (".ctor()")]
+ [KeptInterface (typeof (IDynamicInterfaceCastable))]
class Foo : IDynamicInterfaceCastable
{
[Kept]
@@ -74,6 +77,7 @@ namespace Mono.Linker.Tests.Cases.Attributes
[Kept]
[KeptMember (".ctor()")]
+ [KeptInterface (typeof (IDynamicInterfaceCastable))]
class DynamicCastableImplementedInOtherAssembly : IDynamicInterfaceCastable
{
[Kept]
diff --git a/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceVariants.cs b/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceVariants.cs
index 347c3a6a0..b19208bd5 100644
--- a/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceVariants.cs
+++ b/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceVariants.cs
@@ -21,16 +21,21 @@ namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces
t = typeof (UninstantiatedPublicClassWithPrivateInterface);
t = typeof (ImplementsUsedStaticInterface.InterfaceMethodUnused);
- ImplementsUnusedStaticInterface.Test (); ;
+ ImplementsUnusedStaticInterface.Test ();
GenericMethodThatCallsInternalStaticInterfaceMethod
<ImplementsUsedStaticInterface.InterfaceMethodUsedThroughInterface> ();
// Use all public interfaces - they're marked as public only to denote them as "used"
typeof (IPublicInterface).RequiresPublicMethods ();
typeof (IPublicStaticInterface).RequiresPublicMethods ();
- var ___ = new InstantiatedClassWithInterfaces ();
+ _ = new InstantiatedClassWithInterfaces ();
+ MarkIFormattable (null);
}
[Kept]
+ static void MarkIFormattable (IFormattable x)
+ { }
+
+ [Kept]
internal static void GenericMethodThatCallsInternalStaticInterfaceMethod<T> () where T : IStaticInterfaceUsed
{
T.StaticMethodUsedThroughInterface ();
@@ -113,8 +118,8 @@ namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces
}
}
+ // Interfaces are kept despite being uninstantiated because it is relevant to variant casting
[Kept]
- [KeptInterface (typeof (IEnumerator))]
[KeptInterface (typeof (IPublicInterface))]
[KeptInterface (typeof (IPublicStaticInterface))]
[KeptInterface (typeof (ICopyLibraryInterface))]
@@ -151,18 +156,12 @@ namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces
static void IInternalStaticInterface.ExplicitImplementationInternalStaticInterfaceMethod () { }
- [Kept]
- [ExpectBodyModified]
bool IEnumerator.MoveNext () { throw new PlatformNotSupportedException (); }
- [Kept]
object IEnumerator.Current {
- [Kept]
- [ExpectBodyModified]
get { throw new PlatformNotSupportedException (); }
}
- [Kept]
void IEnumerator.Reset () { }
[Kept]
@@ -198,7 +197,6 @@ namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces
}
[Kept]
- [KeptInterface (typeof (IEnumerator))]
[KeptInterface (typeof (IPublicInterface))]
[KeptInterface (typeof (IPublicStaticInterface))]
[KeptInterface (typeof (ICopyLibraryInterface))]
@@ -235,13 +233,10 @@ namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces
static void IInternalStaticInterface.ExplicitImplementationInternalStaticInterfaceMethod () { }
- [Kept]
bool IEnumerator.MoveNext () { throw new PlatformNotSupportedException (); }
- [Kept]
- object IEnumerator.Current { [Kept] get { throw new PlatformNotSupportedException (); } }
+ object IEnumerator.Current { get { throw new PlatformNotSupportedException (); } }
- [Kept]
void IEnumerator.Reset () { }
[Kept]
diff --git a/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/StaticInterfaceMethods/UnusedInterfacesInPreservedScope.cs b/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/StaticInterfaceMethods/UnusedInterfacesInPreservedScope.cs
index a03a28bd2..35ae2dda0 100644
--- a/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/StaticInterfaceMethods/UnusedInterfacesInPreservedScope.cs
+++ b/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/StaticInterfaceMethods/UnusedInterfacesInPreservedScope.cs
@@ -37,10 +37,15 @@ namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.StaticInterfaceMethods
public int InstanceMethod () => 0;
}
+ // Keep MyType without marking it relevant to variant casting
+ [Kept]
+ static void KeepMyType (MyType x)
+ { }
+
[Kept]
static void Test ()
{
- var x = typeof (MyType); // The only use of MyType
+ KeepMyType (null);
}
}
}