diff --git a/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/LinkDesignerBase.cs b/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/LinkDesignerBase.cs new file mode 100644 index 00000000000..a911417c31c --- /dev/null +++ b/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/LinkDesignerBase.cs @@ -0,0 +1,152 @@ +using Mono.Cecil; +using Mono.Linker; +using Mono.Linker.Steps; +using System; +using System.Linq; +using Xamarin.Android.Tasks; +using System.Collections.Generic; +using Mono.Cecil.Cil; +using System.Text.RegularExpressions; +#if ILLINK +using Microsoft.Android.Sdk.ILLink; +#endif + + +namespace MonoDroid.Tuner { + public abstract class LinkDesignerBase : BaseStep { + public virtual void LogMessage (string message) + { + Context.LogMessage (message); + } + + public virtual AssemblyDefinition Resolve (AssemblyNameReference name) + { + return Context.Resolve (name); + } + + protected bool FindResourceDesigner (AssemblyDefinition assembly, bool mainApplication, out TypeDefinition designer, out CustomAttribute designerAttribute) + { + string designerFullName = null; + designer = null; + designerAttribute = null; + foreach (CustomAttribute attribute in assembly.CustomAttributes) + { + if (attribute.AttributeType.FullName == "Android.Runtime.ResourceDesignerAttribute") + { + designerAttribute = attribute; + if (attribute.HasProperties) + { + foreach (var p in attribute.Properties) + { + if (p.Name == "IsApplication" && (bool)p.Argument.Value == (mainApplication ? mainApplication : (bool)p.Argument.Value)) + { + designerFullName = attribute.ConstructorArguments[0].Value.ToString (); + break; + } + } + } + break; + + } + } + if (string.IsNullOrEmpty(designerFullName)) + return false; + + foreach (ModuleDefinition module in assembly.Modules) + { + foreach (TypeDefinition type in module.Types) + { + if (type.FullName == designerFullName) + { + designer = type; + return true; + } + } + } + return false; + } + + protected void ClearDesignerClass (TypeDefinition designer) + { + LogMessage ($" TryRemoving {designer.FullName}"); + string designerFullName = $"{designer.FullName}/"; + designer.NestedTypes.Clear (); + designer.Methods.Clear (); + designer.Fields.Clear (); + designer.Properties.Clear (); + designer.CustomAttributes.Clear (); + designer.Interfaces.Clear (); + designer.Events.Clear (); + } + protected Dictionary BuildResourceDesignerFieldLookup (TypeDefinition type) + { + var output = new Dictionary (); + foreach (TypeDefinition definition in type.NestedTypes) + { + foreach (FieldDefinition field in definition.Fields) + { + string key = $"{definition.Name}::{field.Name}"; + if (!output.ContainsKey (key)) + output.Add(key, int.Parse (field.Constant?.ToString () ?? "0")); + } + } + return output; + } + + protected void FixType (TypeDefinition type, TypeDefinition localDesigner) + { + foreach (MethodDefinition method in type.Methods) + { + if (!method.HasBody) + continue; + FixBody (method.Body, localDesigner); + } + foreach (PropertyDefinition property in type.Properties) + { + if (property.GetMethod != null && property.GetMethod.HasBody) + { + FixBody (property.GetMethod.Body, localDesigner); + } + if (property.SetMethod != null && property.SetMethod.HasBody) + { + FixBody (property.SetMethod.Body, localDesigner); + } + } + foreach (TypeDefinition nestedType in type.NestedTypes) + { + FixType (nestedType, localDesigner); + } + } + + protected void FixupAssemblyTypes (AssemblyDefinition assembly, TypeDefinition designer) + { + foreach (ModuleDefinition module in assembly.Modules) + { + foreach (TypeDefinition type in module.Types) + { + if (type.FullName == designer.FullName) + continue; + FixType (type, designer); + } + } + } + + protected override void ProcessAssembly (AssemblyDefinition assembly) + { + LoadDesigner (); + + var action = Annotations.HasAction (assembly) ? Annotations.GetAction (assembly) : AssemblyAction.Skip; + if (action == AssemblyAction.Delete) + return; + + if (ProcessAssemblyDesigner (assembly)) { + if (action == AssemblyAction.Skip || action == AssemblyAction.Copy) + Annotations.SetAction (assembly, AssemblyAction.Save); + } + } + + internal abstract bool ProcessAssemblyDesigner (AssemblyDefinition assemblyDefinition); + protected abstract void LoadDesigner (); + protected abstract void FixBody (MethodBody body, TypeDefinition designer); + } +} diff --git a/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/RemoveResourceDesignerStep.cs b/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/RemoveResourceDesignerStep.cs index 970d0dfbf0a..db75512db24 100644 --- a/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/RemoveResourceDesignerStep.cs +++ b/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/RemoveResourceDesignerStep.cs @@ -7,19 +7,23 @@ using System.Collections.Generic; using Mono.Cecil.Cil; using System.Text.RegularExpressions; +#if ILLINK +using Microsoft.Android.Sdk.ILLink; +#endif namespace MonoDroid.Tuner { - public class RemoveResourceDesignerStep : BaseStep + public class RemoveResourceDesignerStep : LinkDesignerBase { TypeDefinition mainDesigner = null; AssemblyDefinition mainAssembly = null; CustomAttribute mainDesignerAttribute; Dictionary designerConstants; Regex opCodeRegex = new Regex (@"([\w]+): ([\w]+) ([\w.]+) ([\w:./]+)"); - - protected override void Process () + protected override void LoadDesigner () { + if (mainAssembly != null) + return; // resolve the MainAssembly Resource designer TypeDefinition AndroidLinkConfiguration config = AndroidLinkConfiguration.GetInstance (Context); if (config == null) @@ -31,95 +35,26 @@ protected override void Process () } } if (mainDesigner == null) { - Context.LogMessage ($" Main Designer not found."); + LogMessage ($" Main Designer not found."); return; } - Context.LogMessage ($" Main Designer found {mainDesigner.FullName}."); + LogMessage ($" Main Designer found {mainDesigner.FullName}."); designerConstants = BuildResourceDesignerFieldLookup (mainDesigner); } protected override void EndProcess () { if (mainDesigner != null) { - Context.LogMessage ($" Setting Action on {mainAssembly.Name} to Save."); + LogMessage ($" Setting Action on {mainAssembly.Name} to Save."); Annotations.SetAction (mainAssembly, AssemblyAction.Save); } } - bool FindResourceDesigner (AssemblyDefinition assembly, bool mainApplication, out TypeDefinition designer, out CustomAttribute designerAttribute) - { - string designerFullName = null; - designer = null; - designerAttribute = null; - foreach (CustomAttribute attribute in assembly.CustomAttributes) - { - if (attribute.AttributeType.FullName == "Android.Runtime.ResourceDesignerAttribute") - { - designerAttribute = attribute; - if (attribute.HasProperties) - { - foreach (var p in attribute.Properties) - { - if (p.Name == "IsApplication" && (bool)p.Argument.Value == (mainApplication ? mainApplication : (bool)p.Argument.Value)) - { - designerFullName = attribute.ConstructorArguments[0].Value.ToString (); - break; - } - } - } - break; - - } - } - if (string.IsNullOrEmpty(designerFullName)) - return false; - - foreach (ModuleDefinition module in assembly.Modules) - { - foreach (TypeDefinition type in module.Types) - { - if (type.FullName == designerFullName) - { - designer = type; - return true; - } - } - } - return false; - } - - Dictionary BuildResourceDesignerFieldLookup (TypeDefinition type) - { - var output = new Dictionary (); - foreach (TypeDefinition definition in type.NestedTypes) - { - foreach (FieldDefinition field in definition.Fields) - { - string key = $"{definition.Name}::{field.Name}"; - if (!output.ContainsKey (key)) - output.Add(key, int.Parse (field.Constant?.ToString () ?? "0")); - } - } - return output; - } - - void ClearDesignerClass (TypeDefinition designer) - { - Context.LogMessage ($" TryRemoving {designer.FullName}"); - designer.NestedTypes.Clear (); - designer.Methods.Clear (); - designer.Fields.Clear (); - designer.Properties.Clear (); - designer.CustomAttributes.Clear (); - designer.Interfaces.Clear (); - designer.Events.Clear (); - } - - void FixBody (MethodBody body, TypeDefinition localDesigner) + protected override void FixBody (MethodBody body, TypeDefinition designer) { Dictionary instructions = new Dictionary(); var processor = body.GetILProcessor (); - string designerFullName = $"{localDesigner.FullName}/"; + string designerFullName = $"{designer.FullName}/"; foreach (var i in body.Instructions) { string line = i.ToString (); @@ -134,77 +69,48 @@ void FixBody (MethodBody body, TypeDefinition localDesigner) } } if (instructions.Count > 0) - Context.LogMessage ($" Fixing up {body.Method.FullName}"); + LogMessage ($" Fixing up {body.Method.FullName}"); foreach (var i in instructions) { var newCode = Extensions.CreateLoadArraySizeOrOffsetInstruction (i.Value); - Context.LogMessage ($" Replacing {i.Key}"); - Context.LogMessage ($" With {newCode}"); + LogMessage ($" Replacing {i.Key}"); + LogMessage ($" With {newCode}"); processor.Replace(i.Key, newCode); } } - void FixType (TypeDefinition type, TypeDefinition localDesigner) - { - foreach (MethodDefinition method in type.Methods) - { - if (!method.HasBody) - continue; - FixBody (method.Body, localDesigner); - } - foreach (PropertyDefinition property in type.Properties) - { - if (property.GetMethod != null && property.GetMethod.HasBody) - { - FixBody (property.GetMethod.Body, localDesigner); - } - if (property.SetMethod != null && property.SetMethod.HasBody) - { - FixBody (property.SetMethod.Body, localDesigner); - } - } - foreach (TypeDefinition nestedType in type.NestedTypes) - { - FixType (nestedType, localDesigner); - } - } - - protected override void ProcessAssembly (AssemblyDefinition assembly) + internal override bool ProcessAssemblyDesigner (AssemblyDefinition assembly) { if (mainDesigner == null) - return; + return false; var fileName = assembly.Name.Name + ".dll"; if (MonoAndroidHelper.IsFrameworkAssembly (fileName)) - return; + return false; - Context.LogMessage ($" Fixing up {assembly.Name.Name}"); + LogMessage ($" Fixing up {assembly.Name.Name}"); TypeDefinition localDesigner = null; CustomAttribute designerAttribute; if (assembly != mainAssembly) { - Context.LogMessage ($" {assembly.Name.Name} is not the main assembly. "); + LogMessage ($" {assembly.Name.Name} is not the main assembly. "); if (!FindResourceDesigner (assembly, mainApplication: false, designer: out localDesigner, designerAttribute: out designerAttribute)) { Context.LogMessage ($" {assembly.Name.Name} does not have a designer file."); - return; + return false; } } else { - Context.LogMessage ($" {assembly.Name.Name} is the main assembly. "); + LogMessage ($" {assembly.Name.Name} is the main assembly. "); localDesigner = mainDesigner; designerAttribute = mainDesignerAttribute; } - Context.LogMessage ($" {assembly.Name.Name} has designer {localDesigner.FullName}."); + LogMessage ($" {assembly.Name.Name} has designer {localDesigner.FullName}."); + + FixupAssemblyTypes (assembly, localDesigner); - foreach (var mod in assembly.Modules) { - foreach (var type in mod.Types) { - if (type == localDesigner) - continue; - FixType (type, localDesigner); - } - } ClearDesignerClass (localDesigner); if (designerAttribute != null) { assembly.CustomAttributes.Remove (designerAttribute); } + return true; } } } diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets index 94f9f4d22cb..b3ef9c9b68b 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets @@ -91,9 +91,9 @@ false false true - <_AggressiveAttributeTrimming Condition="'$(_AggressiveAttributeTrimming)' == ''">true + <_AggressiveAttributeTrimming Condition="'$(_AggressiveAttributeTrimming)' == ''">true false - false + false false true diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/LinkerTests.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/LinkerTests.cs index 1fd94e2f404..7e2fa8b8fd8 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/LinkerTests.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/LinkerTests.cs @@ -231,7 +231,15 @@ public void RemoveDesigner ([Values (true, false)] bool useAssemblyStore) stream.Position = 0; using (var assembly = AssemblyDefinition.ReadAssembly (stream)) { var type = assembly.MainModule.GetType ($"{assemblyName}.Resource"); - Assert.AreEqual (0, type.NestedTypes.Count, "All Nested Resource Types should be removed."); + var intType = typeof(int); + foreach (var nestedType in type.NestedTypes) { + int count = 0; + foreach (var field in nestedType.Fields) { + if (field.FieldType.FullName == intType.FullName) + count++; + } + Assert.AreEqual (0, count, "All Nested Resource Type int fields should be removed."); + } } } }