Skip to content

Support "type granularity" option #854

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

Closed
Closed
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
7 changes: 7 additions & 0 deletions src/ILLink.Tasks/LinkTask.cs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,13 @@ protected override string GenerateResponseFileCommands ()
args.Append (action);
args.Append (" ").AppendLine (Quote (assemblyName));
}

string actionflag = assembly.GetMetadata ("actionflag");
if ((actionflag != null) && (actionflag.Length > 0)) {
args.Append ("-f ");
args.Append (actionflag);
args.Append (" ").AppendLine (Quote (assemblyName));
}
}

if (ReferenceAssemblyPaths != null) {
Expand Down
4 changes: 4 additions & 0 deletions src/linker/Linker.Steps/MarkStep.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1699,6 +1699,10 @@ void ApplyPreserveInfo (TypeDefinition type)
{
ApplyPreserveMethods (type);

if (_context.HasActionFlag(type.Module.Assembly, AssemblyActionFlag.TypeGranularity)) {
Annotations.SetPreserve (type, TypePreserve.All);
}

if (!Annotations.TryGetPreserve (type, out TypePreserve preserve))
return;

Expand Down
9 changes: 9 additions & 0 deletions src/linker/Linker/AssemblyActionFlag.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using System;

namespace Mono.Linker {
[Flags]
public enum AssemblyActionFlag : int {
// If there is any reference to a type, preserve all members in that type
TypeGranularity = 1,
}
}
14 changes: 14 additions & 0 deletions src/linker/Linker/Driver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,13 @@ public void Run (ILogger customLogger = null)
AssemblyAction action = ParseAssemblyAction (GetParam ());
context.Actions [GetParam ()] = action;
break;
case 'f':
AssemblyActionFlag newFlag = ParseAssemblyActionFlag (GetParam ());
string flagTargetName = GetParam ();
context.ActionFlags [flagTargetName] = context.ActionFlags.TryGetValue (flagTargetName, out AssemblyActionFlag existingFlag)
? existingFlag | newFlag
: newFlag;
break;
case 't':
context.KeepTypeForwarderOnlyAssemblies = true;
break;
Expand Down Expand Up @@ -535,6 +542,11 @@ AssemblyAction ParseAssemblyAction (string s)
return assemblyAction;
}

AssemblyActionFlag ParseAssemblyActionFlag (string s)
{
return (AssemblyActionFlag)Enum.Parse (typeof (AssemblyActionFlag), s, true);
}

string GetParam ()
{
if (_queue.Count == 0)
Expand Down Expand Up @@ -591,6 +603,8 @@ static void Usage (string msg)
Console.WriteLine (" addbypassngenused: Same as addbypassngen but unused assemblies are removed");
Console.WriteLine (" -u <action> Action on the user assemblies. Defaults to 'link'");
Console.WriteLine (" -p <action> <name> Overrides the default action for an assembly");
Console.WriteLine (" -f <flag> <name> Adds a linker action flag for an assembly");
Console.WriteLine (" typegranularity: If there is any reference to a type, preserve the whole type");

Console.WriteLine ();
Console.WriteLine ("Advanced");
Expand Down
12 changes: 12 additions & 0 deletions src/linker/Linker/LinkContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ public class LinkContext : IDisposable {
AssemblyAction _userAction;
Dictionary<string, AssemblyAction> _actions;
string _outputDirectory;
readonly Dictionary<string, AssemblyActionFlag> _actionFlags;
readonly Dictionary<string, string> _parameters;
bool _linkSymbols;
bool _keepTypeForwarderOnlyAssemblies;
Expand Down Expand Up @@ -123,6 +124,10 @@ public System.Collections.IDictionary Actions {
get { return _actions; }
}

public IDictionary<string, AssemblyActionFlag> ActionFlags {
get { return _actionFlags; }
}

public AssemblyResolver Resolver {
get { return _resolver; }
}
Expand Down Expand Up @@ -178,6 +183,7 @@ public LinkContext (Pipeline pipeline, AssemblyResolver resolver, ReaderParamete
_resolver = resolver;
_resolver.Context = this;
_actions = new Dictionary<string, AssemblyAction> ();
_actionFlags = new Dictionary<string, AssemblyActionFlag> ();
_parameters = new Dictionary<string, string> ();
_readerParameters = readerParameters;

Expand Down Expand Up @@ -400,6 +406,12 @@ public string GetParameter (string key)
return val;
}

public bool HasActionFlag (AssemblyDefinition assembly, AssemblyActionFlag flag)
{
return ActionFlags.TryGetValue (assembly.Name.Name, out AssemblyActionFlag foundFlags)
&& foundFlags.HasFlag (flag);
}

public void Dispose ()
{
_resolver.Dispose ();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using Mono.Linker.Tests.Cases.Expectations.Assertions;
using Mono.Linker.Tests.Cases.Expectations.Metadata;
using System;

namespace Mono.Linker.Tests.Cases.TypeGranularity
{
[SetupLinkerArgument ("-f", "typegranularity", "test")]
static class UsedTypeKeepsAllMembersWithTypeGranularity {
public static void Main() {
GC.KeepAlive (typeof(ReferencedClass));
}

[KeptMember (".ctor()")]
class ReferencedClass {
[Kept]
[KeptBackingField]
public int Prop { [Kept] get; [Kept] set; }

[Kept]
public void SomeMethod() {
Console.WriteLine (typeof (KeptNestedClass));
}

[KeptMember (".ctor()")]
class KeptNestedClass { }

// Unkept because nested types are not members. Type-level granularity doesn't
// preserve nested classes unless there is a reference to them.
class UnkeptNestedClass {
public string unkeptField;
}
}
}
}
5 changes: 5 additions & 0 deletions test/Mono.Linker.Tests/TestCases/TestDatabase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@ public static IEnumerable<TestCaseData> PreserveDependenciesTests ()
return NUnitCasesBySuiteName ("PreserveDependencies");
}

public static IEnumerable<TestCaseData> TypeGranularityTests ()
{
return NUnitCasesBySuiteName ("TypeGranularity");
}

public static IEnumerable<TestCaseData> LibrariesTests ()
{
return NUnitCasesBySuiteName ("Libraries");
Expand Down
6 changes: 6 additions & 0 deletions test/Mono.Linker.Tests/TestCases/TestSuites.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,12 @@ public void PreserveDependenciesTests (TestCase testCase)
Run (testCase);
}

[TestCaseSource (typeof (TestDatabase), nameof (TestDatabase.TypeGranularityTests))]
public void TypeGranularityTests (TestCase testCase)
{
Run (testCase);
}

[TestCaseSource (typeof (TestDatabase), nameof (TestDatabase.SymbolsTests))]
public void SymbolsTests (TestCase testCase)
{
Expand Down