Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Check for marking virtual method due to base only when state changes #3073

Merged
merged 43 commits into from
Oct 27, 2022
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
34ce174
Check for marking method for base only when state changes
jtschuster Oct 13, 2022
bdca16a
Special case IDynamicInterfaceCastable and don't keep all interfaces …
jtschuster Oct 18, 2022
e5643fb
Remove unused method and redundant while loop
jtschuster Oct 18, 2022
3854d3f
Add comments to TypeMapInfo methods
jtschuster Oct 18, 2022
04801f1
Change analyzer versions such that the repo can be built with .NET 7 …
vitek-karas Oct 19, 2022
d406e2b
PR feedback: Fix typo and return early
jtschuster Oct 19, 2022
20c52af
Remove commented code and extra null check
jtschuster Oct 19, 2022
c027846
Merge branch 'main' of https://github.com/dotnet/linker into virtualC…
jtschuster Oct 20, 2022
3dbce05
Merge branch 'EnableRC2'
jtschuster Oct 20, 2022
6484a01
Update src/linker/Linker/TypeMapInfo.cs
jtschuster Oct 20, 2022
e6c0429
Update src/linker/Linker/TypeMapInfo.cs
jtschuster Oct 20, 2022
d53e433
Merge branch 'virtualCheckMove' of https://github.com/jtschuster/link…
jtschuster Oct 20, 2022
324d4cb
PR feedback:
jtschuster Oct 20, 2022
756f547
format
jtschuster Oct 20, 2022
e18e579
remove using
jtschuster Oct 21, 2022
cd38208
Don't ignore descriptors in test
jtschuster Oct 21, 2022
8088e70
Merge branch 'main' of https://github.com/dotnet/linker
jtschuster Oct 24, 2022
95403f1
Merge branch 'main' of https://github.com/dotnet/linker into virtualC…
jtschuster Oct 25, 2022
c0db7f5
Don't go through bases of bases when in GetMarkedBaseMethods
jtschuster Oct 25, 2022
44c4c74
Check for marking method for base only when state changes
jtschuster Oct 13, 2022
f45f2d0
Special case IDynamicInterfaceCastable and don't keep all interfaces …
jtschuster Oct 18, 2022
cebaff6
Remove unused method and redundant while loop
jtschuster Oct 18, 2022
af16936
Add comments to TypeMapInfo methods
jtschuster Oct 18, 2022
4ff3fbe
PR feedback: Fix typo and return early
jtschuster Oct 19, 2022
6a131b8
Remove commented code and extra null check
jtschuster Oct 19, 2022
6e78835
Update src/linker/Linker/TypeMapInfo.cs
jtschuster Oct 20, 2022
e78d069
Update src/linker/Linker/TypeMapInfo.cs
jtschuster Oct 20, 2022
f1af79b
PR feedback:
jtschuster Oct 20, 2022
077e336
format
jtschuster Oct 20, 2022
20ea808
remove using
jtschuster Oct 21, 2022
0de4d04
Don't ignore descriptors in test
jtschuster Oct 21, 2022
4cef7a7
Change analyzer versions such that the repo can be built with .NET 7 …
vitek-karas Oct 25, 2022
c84326e
Don't go through bases of bases when in GetMarkedBaseMethods
jtschuster Oct 25, 2022
2bc88c0
Remove GetInterfaceImplementors
jtschuster Oct 25, 2022
959c7c3
Merge branch 'virtualCheckMove' of https://github.com/jtschuster/link…
jtschuster Oct 25, 2022
fdc6bb3
Remove GetInterfaceImplementors
jtschuster Oct 25, 2022
0cf21d0
Remove leftover IsDirectBase code
jtschuster Oct 25, 2022
eb6a399
Merge branch 'virtualCheckMove2' into virtualCheckMove
jtschuster Oct 25, 2022
07c28b5
Remove call to MarkMethodIfNeededByBaseMethod from MarkAsRelevantToVa…
jtschuster Oct 25, 2022
c2dcdfe
Merge branch 'main' of https://github.com/dotnet/linker into virtualC…
jtschuster Oct 27, 2022
7b85004
Merge branch 'main' of https://github.com/dotnet/linker into virtualC…
jtschuster Oct 27, 2022
49d2e0f
PR Feedback: inline methods and remove changes that are not longer re…
jtschuster Oct 27, 2022
4c12f43
Remove outdated comment
jtschuster Oct 27, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
270 changes: 150 additions & 120 deletions src/linker/Linker.Steps/MarkStep.cs

Large diffs are not rendered by default.

15 changes: 8 additions & 7 deletions src/linker/Linker.Steps/SealerStep.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ void ProcessType (TypeDefinition type)
//
// cannot de-virtualize nor seal methods if something overrides them
//
if (IsAnyMarked (overrides))
if (IsAnyOverrideMarked (overrides))
continue;

SealMethod (method);
Expand All @@ -106,9 +106,9 @@ void ProcessType (TypeDefinition type)
if (!type.IsSealed)
continue;

var bases = Annotations.GetBaseMethods (method);
// Devirtualize if a method is not override to existing marked methods
if (!IsAnyMarked (bases))
var bases = Annotations.GetBaseMethods (method); //?.Select (static x => x.Base);
// Devirtualize if a method is not override to existing marked methods
if (bases is not null && !IsAnyBaseMarked (bases))
method.IsVirtual = method.IsFinal = method.IsNewSlot = false;
}
}
Expand All @@ -123,7 +123,7 @@ protected virtual void SealMethod (MethodDefinition method)
method.IsFinal = true;
}

bool IsAnyMarked (IEnumerable<OverrideInformation>? list)
bool IsAnyOverrideMarked (IEnumerable<OverrideInformation>? list)
{
if (list == null)
return false;
Expand All @@ -135,12 +135,13 @@ bool IsAnyMarked (IEnumerable<OverrideInformation>? list)
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;
Expand Down
10 changes: 5 additions & 5 deletions src/linker/Linker.Steps/ValidateVirtualMethodAnnotationsStep.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ protected override void Process ()
{
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);
}
}

Expand Down
20 changes: 19 additions & 1 deletion src/linker/Linker/Annotations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,9 @@ public bool IsPublic (IMetadataTokenProvider provider)
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);
Expand All @@ -446,11 +449,26 @@ public bool IsPublic (IMetadataTokenProvider provider)
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);
}

/// <summary>
/// Returns a list of all known types that implement <paramref name="interfaceType"/>. The list may be incomplete if other implementing types exist in assemblies that haven't been processed by TypeMapInfo yet
/// </summary>
public List<(TypeDefinition Implementor, InterfaceImplementation InterfaceImplementation)>? GetInterfaceImplementors (TypeDefinition type)
{
return TypeMapInfo.GetInterfaceImplementors (type);
}

public List<MethodDefinition>? GetPreservedMethods (TypeDefinition type)
{
return GetPreservedMethods (type as IMemberDefinition);
Expand Down
53 changes: 45 additions & 8 deletions src/linker/Linker/TypeMapInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//

using System;
using System.Collections.Generic;
using Mono.Cecil;

Expand All @@ -39,9 +40,10 @@ public class TypeMapInfo
{
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)>> ();
protected readonly Dictionary<TypeDefinition, List<(TypeDefinition, InterfaceImplementation)>> interface_implementors = new ();

public TypeMapInfo (LinkContext context)
{
Expand All @@ -57,17 +59,27 @@ void EnsureProcessed (AssemblyDefinition assembly)
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);
override_methods.TryGetValue (method, out List<OverrideInformation>? overrides);
return overrides;
}

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),
jtschuster marked this conversation as resolved.
Show resolved Hide resolved
/// methods on an interface that <paramref name="method"/>'s delcaring type implements,
jtschuster marked this conversation as resolved.
Show resolved Hide resolved
/// 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;
}

Expand All @@ -77,14 +89,14 @@ void EnsureProcessed (AssemblyDefinition assembly)
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)
Expand All @@ -107,10 +119,23 @@ public void AddDefaultInterfaceImplementation (MethodDefinition @base, TypeDefin
implementations.Add ((implementingType, matchingInterfaceImplementation));
}

/// <summary>
/// Returns a list of all known types that implement <paramref name="interfaceType"/>. The list may be incomplete if other implementing types exist in assemblies that haven't been processed by TypeMapInfo yet
/// </summary>
public List<(TypeDefinition Implementor, InterfaceImplementation InterfaceImplementation)>? GetInterfaceImplementors (TypeDefinition interfaceType)
{
if (!interfaceType.IsInterface)
throw new InvalidOperationException ("Cannot get interface implementors of a type that is not an interface");
if (interface_implementors.TryGetValue (interfaceType, out var interfaces))
return interfaces;
return null;
}

protected virtual void MapType (TypeDefinition type)
{
MapVirtualMethods (type);
MapInterfaceMethodsInTypeHierarchy (type);
MapInterfaces (type);

if (!type.HasNestedTypes)
return;
Expand Down Expand Up @@ -163,6 +188,18 @@ void MapInterfaceMethodsInTypeHierarchy (TypeDefinition type)
}
}

void MapInterfaces (TypeDefinition type)
{
foreach (var iface in type.GetAllInterfaceImplementations (context, true)) {
if (context.Resolve (iface.InterfaceType) is not TypeDefinition ifaceType)
continue;
if (!interface_implementors.TryGetValue (ifaceType, out var implementors)) {
implementors = new ();
}
implementors.Add ((type, iface));
}
}

void MapVirtualMethods (TypeDefinition type)
{
if (!type.HasMethods)
Expand Down Expand Up @@ -204,7 +241,7 @@ void MapOverrides (MethodDefinition method)

void AnnotateMethods (MethodDefinition @base, MethodDefinition @override, InterfaceImplementation? matchingInterfaceImplementation = null)
{
AddBaseMethod (@override, @base);
AddBaseMethod (@override, @base, matchingInterfaceImplementation);
AddOverride (@base, @override, matchingInterfaceImplementation);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ static IReferencedAssembly GetReferencedInterface (object obj)
#if NETCOREAPP
[Kept]
[KeptMember (".ctor()")]
[KeptInterface (typeof (IDynamicInterfaceCastable))]
class Foo : IDynamicInterfaceCastable
{
[Kept]
Expand All @@ -74,6 +75,7 @@ public bool IsInterfaceImplemented (RuntimeTypeHandle interfaceType, bool throwI

[Kept]
[KeptMember (".ctor()")]
[KeptInterface (typeof (IDynamicInterfaceCastable))]
class DynamicCastableImplementedInOtherAssembly : IDynamicInterfaceCastable
{
[Kept]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,20 @@ public static void Main ()
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
{
Expand Down Expand Up @@ -113,8 +118,8 @@ public static void Test ()
}
}

// 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))]
Expand Down Expand Up @@ -151,18 +156,12 @@ public static void InternalStaticInterfaceMethod () { }
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]
Expand Down Expand Up @@ -198,7 +197,6 @@ public string ToString (string format, IFormatProvider formatProvider)
}

[Kept]
[KeptInterface (typeof (IEnumerator))]
[KeptInterface (typeof (IPublicInterface))]
[KeptInterface (typeof (IPublicStaticInterface))]
[KeptInterface (typeof (ICopyLibraryInterface))]
Expand Down Expand Up @@ -235,13 +233,10 @@ public static void InternalStaticInterfaceMethod () { }

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]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,15 @@ class MyType : IStaticInterfaceWithDefaultImpls
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);
}
}
}