Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
16 changes: 16 additions & 0 deletions linker/Linker.Steps/MarkStep.cs
Original file line number Diff line number Diff line change
Expand Up @@ -982,6 +982,8 @@ protected virtual TypeDefinition MarkType (TypeReference reference)
// and there are no other usages of that interface type, then we need to make sure the interface type is still marked because
// this type is going to retain the interface implementation
MarkRequirementsForInstantiatedTypes (type);
} else if (AlwaysMarkTypeAsInstantiated (type)) {
MarkRequirementsForInstantiatedTypes (type);
}

if (type.HasInterfaces)
Expand Down Expand Up @@ -1401,6 +1403,20 @@ static bool IsMulticastDelegate (TypeDefinition td)
return td.BaseType != null && td.BaseType.FullName == "System.MulticastDelegate";
}

protected virtual bool AlwaysMarkTypeAsInstantiated (TypeDefinition td)
{
switch (td.Name) {
// These types are created from native code which means we are unable to track when they are instantiated
// Since these are such foundational types, let's take the easy route and just always assume an instance of one of these
// could exist
case "Delegate":
case "MulticastDelegate":
return td.Namespace == "System";
}

return false;
}

void MarkEventSourceProviders (TypeDefinition td)
{
foreach (var nestedType in td.NestedTypes) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using System;

namespace Mono.Linker.Tests.Cases.Expectations.Assertions {
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
public class KeptBaseOnTypeInAssemblyAttribute : BaseInAssemblyAttribute {
public KeptBaseOnTypeInAssemblyAttribute (string assemblyFileName, Type type, string baseAssemblyFileName, Type baseType)
{
if (type == null)
throw new ArgumentNullException (nameof (type));
if (string.IsNullOrEmpty (assemblyFileName))
throw new ArgumentException ("Value cannot be null or empty.", nameof (assemblyFileName));

if (string.IsNullOrEmpty (baseAssemblyFileName))
throw new ArgumentException ("Value cannot be null or empty.", nameof (baseAssemblyFileName));
if (baseType == null)
throw new ArgumentException ("Value cannot be null or empty.", nameof (baseType));
}

public KeptBaseOnTypeInAssemblyAttribute (string assemblyFileName, string typeName, string baseAssemblyFileName, string baseTypeName)
{
if (string.IsNullOrEmpty (assemblyFileName))
throw new ArgumentException ("Value cannot be null or empty.", nameof (assemblyFileName));
if (string.IsNullOrEmpty (typeName))
throw new ArgumentException ("Value cannot be null or empty.", nameof (typeName));

if (string.IsNullOrEmpty (baseAssemblyFileName))
throw new ArgumentException ("Value cannot be null or empty.", nameof (baseAssemblyFileName));
if (string.IsNullOrEmpty (baseTypeName))
throw new ArgumentException ("Value cannot be null or empty.", nameof (baseTypeName));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
<Compile Include="Assertions\KeptAttributeInAssemblyAttribute.cs" />
<Compile Include="Assertions\KeptAttributeOnFixedBufferTypeAttribute.cs" />
<Compile Include="Assertions\KeptBackingFieldAttribute.cs" />
<Compile Include="Assertions\KeptBaseOnTypeInAssemblyAttribute.cs" />
<Compile Include="Assertions\KeptDelegateCacheFieldAttribute.cs" />
<Compile Include="Assertions\KeptEventAddMethodAttribute.cs" />
<Compile Include="Assertions\KeptEventRemoveMethodAttribute.cs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System;
using System.Runtime.Serialization;
using Mono.Linker.Tests.Cases.Expectations.Assertions;
using Mono.Linker.Tests.Cases.Expectations.Metadata;

namespace Mono.Linker.Tests.Cases.CoreLink {
/// <summary>
/// Delegate and is created from
/// </summary>
[SetupLinkerCoreAction ("link")]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will be problematic as the mscorlib will be used for execution.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don’t follow. Are you saying that it’s bad that this test is dependent on a particular mscorlib?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am saying it won't be easy to run the test

[KeptBaseOnTypeInAssembly ("mscorlib.dll", typeof (MulticastDelegate), "mscorlib.dll", typeof (Delegate))]

// Check a couple override methods to verify they were not removed
[KeptMemberInAssembly ("mscorlib.dll", typeof (MulticastDelegate), "GetHashCode()")]
[KeptMemberInAssembly ("mscorlib.dll", typeof (MulticastDelegate), "GetObjectData(System.Runtime.Serialization.SerializationInfo,System.Runtime.Serialization.StreamingContext)")]

[KeptMemberInAssembly ("mscorlib.dll", typeof (Delegate), "GetHashCode()")]
[KeptMemberInAssembly ("mscorlib.dll", typeof (Delegate), "Equals(System.Object)")]
[KeptInterfaceOnTypeInAssembly("mscorlib.dll", typeof (Delegate), "mscorlib.dll", typeof (ICloneable))]
[KeptInterfaceOnTypeInAssembly("mscorlib.dll", typeof (Delegate), "mscorlib.dll", typeof (ISerializable))]

// Fails due to Runtime critical type System.Reflection.CustomAttributeData not found.
[SkipPeVerify(SkipPeVerifyForToolchian.Pedump)]
public class DelegateAndMulticastDelegateKeepInstantiatedReqs {
public static void Main ()
{
typeof (MulticastDelegate).ToString ();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@
<Compile Include="BCLFeatures\ETW\LocalsOfModifiedMethodAreRemoved.cs" />
<Compile Include="BCLFeatures\ETW\StubbedMethodWithExceptionHandlers.cs" />
<Compile Include="CoreLink\CanIncludeI18nAssemblies.cs" />
<Compile Include="CoreLink\DelegateAndMulticastDelegateKeepInstantiatedReqs.cs" />
<Compile Include="CoreLink\NoSecurityPlusOnlyKeepUsedRemovesAllSecurityAttributesFromCoreLibraries.cs" />
<Compile Include="Inheritance.AbstractClasses\UnusedAbstractMethodRemoved.cs" />
<Compile Include="Inheritance.AbstractClasses\UnusedVirtualMethodRemoved.cs" />
Expand Down
20 changes: 20 additions & 0 deletions linker/Tests/TestCasesRunner/ResultChecker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,11 @@ void VerifyLinkingOfOtherAssemblies (AssemblyDefinition original)

VerifyRemovedMemberInAssembly (checkAttrInAssembly, linkedType);
break;
case nameof (KeptBaseOnTypeInAssemblyAttribute):
if (linkedType == null)
Assert.Fail ($"Type `{expectedTypeName}' should have been kept");
VerifyKeptBaseOnTypeInAssembly (checkAttrInAssembly, linkedType);
break;
case nameof (KeptMemberInAssemblyAttribute):
if (linkedType == null)
Assert.Fail ($"Type `{expectedTypeName}' should have been kept");
Expand Down Expand Up @@ -393,6 +398,21 @@ void VerifyKeptInterfaceOnTypeInAssembly (CustomAttribute inAssemblyAttribute, T
Assert.Fail ($"Expected `{linkedType}` to have interface of type {originalInterface.FullName}");
}

void VerifyKeptBaseOnTypeInAssembly (CustomAttribute inAssemblyAttribute, TypeDefinition linkedType)
{
var originalType = GetOriginalTypeFromInAssemblyAttribute (inAssemblyAttribute);

var baseAssemblyName = inAssemblyAttribute.ConstructorArguments [2].Value.ToString ();
var baseType = inAssemblyAttribute.ConstructorArguments [3].Value;

var originalBase = GetOriginalTypeFromInAssemblyAttribute (baseAssemblyName, baseType);
if (originalType.BaseType.Resolve () != originalBase)
Assert.Fail ("Invalid assertion. Original type's base does not match the expected base");

Assert.That (originalBase.FullName, Is.EqualTo (linkedType.BaseType.FullName),
$"Incorrect base on `{linkedType.FullName}`. Expected `{originalBase.FullName}` but was `{linkedType.BaseType.FullName}`");
}

protected static InterfaceImplementation GetMatchingInterfaceImplementationOnType (TypeDefinition type, string expectedInterfaceTypeName)
{
return type.Interfaces.FirstOrDefault (impl =>
Expand Down