diff --git a/src/Microsoft.Android.Sdk.ILLink/GetAssembliesStep.cs b/src/Microsoft.Android.Sdk.ILLink/GetAssembliesStep.cs
deleted file mode 100644
index 7721dc05151..00000000000
--- a/src/Microsoft.Android.Sdk.ILLink/GetAssembliesStep.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-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;
-
-namespace MonoDroid.Tuner
-{
- public class GetAssembliesStep : BaseStep
- {
- AndroidLinkConfiguration config = null;
-
- protected override void Process ()
- {
- config = AndroidLinkConfiguration.GetInstance (Context);
- }
-
- protected override void ProcessAssembly (AssemblyDefinition assembly)
- {
- if (config == null)
- return;
- config.Assemblies.Add (assembly);
- }
- }
-}
diff --git a/src/Microsoft.Android.Sdk.ILLink/Microsoft.Android.Sdk.ILLink.csproj b/src/Microsoft.Android.Sdk.ILLink/Microsoft.Android.Sdk.ILLink.csproj
index 92c92c0ee94..26d2067d1fc 100644
--- a/src/Microsoft.Android.Sdk.ILLink/Microsoft.Android.Sdk.ILLink.csproj
+++ b/src/Microsoft.Android.Sdk.ILLink/Microsoft.Android.Sdk.ILLink.csproj
@@ -19,7 +19,6 @@
-
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 c5a34887a0f..51e25383a0d 100644
--- a/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/RemoveResourceDesignerStep.cs
+++ b/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/RemoveResourceDesignerStep.cs
@@ -1,123 +1,323 @@
#nullable disable
-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.Globalization;
using System.Text.RegularExpressions;
-#if ILLINK
-using Microsoft.Android.Sdk.ILLink;
-#endif
+using Mono.Cecil;
+using Mono.Cecil.Cil;
+using Mono.Collections.Generic;
+using Xamarin.Android.Tasks;
+
+namespace MonoDroid.Tuner;
-namespace MonoDroid.Tuner
+///
+/// Inlines resource designer constants and removes designer classes.
+/// The core logic is adapted from the original ILLink RemoveResourceDesignerStep / LinkDesignerBase.
+///
+class RemoveResourceDesignerStep : IAssemblyModifierPipelineStep
{
- public class RemoveResourceDesignerStep : LinkDesignerBase
+ readonly IList allAssemblies;
+ readonly Action logMessage;
+ readonly Regex opCodeRegex = new Regex (@"([\w]+): ([\w]+) ([\w.]+) ([\w:./]+)");
+
+ TypeDefinition mainDesigner = null;
+ AssemblyDefinition mainAssembly = null;
+ CustomAttribute mainDesignerAttribute;
+ Dictionary designerConstants;
+ bool designerLoaded;
+
+ public RemoveResourceDesignerStep (IList allAssemblies, Action logMessage)
+ {
+ this.allAssemblies = allAssemblies;
+ this.logMessage = logMessage;
+ }
+
+ public void ProcessAssembly (AssemblyDefinition assembly, StepContext context)
{
- TypeDefinition mainDesigner = null;
- AssemblyDefinition mainAssembly = null;
- CustomAttribute mainDesignerAttribute;
- Dictionary designerConstants;
- Regex opCodeRegex = new Regex (@"([\w]+): ([\w]+) ([\w.]+) ([\w:./]+)");
+ LoadDesigner ();
+ context.IsAssemblyModified |= ProcessAssemblyDesigner (assembly);
+ }
+
+ void LoadDesigner ()
+ {
+ if (designerLoaded)
+ return;
+ designerLoaded = true;
+
+ foreach (var asm in allAssemblies) {
+ if (FindResourceDesigner (asm, mainApplication: true, designer: out mainDesigner, designerAttribute: out mainDesignerAttribute)) {
+ mainAssembly = asm;
+ break;
+ }
+ }
+ if (mainDesigner == null) {
+ logMessage (" Main Designer not found.");
+ return;
+ }
+ logMessage ($" Main Designer found {mainDesigner.FullName}.");
+ designerConstants = BuildResourceDesignerFieldLookup (mainDesigner);
+ }
+
+ bool ProcessAssemblyDesigner (AssemblyDefinition assembly)
+ {
+ if (mainDesigner == null)
+ return false;
+ if (MonoAndroidHelper.IsFrameworkAssembly (assembly))
+ return false;
+
+ logMessage ($" Fixing up {assembly.Name.Name}");
+ TypeDefinition localDesigner = null;
+ CustomAttribute designerAttribute;
+ if (assembly != mainAssembly) {
+ logMessage ($" {assembly.Name.Name} is not the main assembly. ");
+ if (!FindResourceDesigner (assembly, mainApplication: false, designer: out localDesigner, designerAttribute: out designerAttribute)) {
+ logMessage ($" {assembly.Name.Name} does not have a designer file.");
+ return false;
+ }
+ } else {
+ logMessage ($" {assembly.Name.Name} is the main assembly. ");
+ localDesigner = mainDesigner;
+ designerAttribute = mainDesignerAttribute;
+ }
+
+ logMessage ($" {assembly.Name.Name} has designer {localDesigner.FullName}.");
+
+ FixupAssemblyTypes (assembly, localDesigner);
+
+ ClearDesignerClass (localDesigner);
+ if (designerAttribute != null) {
+ assembly.CustomAttributes.Remove (designerAttribute);
+ }
+ return true;
+ }
+
+ // ---- Methods below are from LinkDesignerBase, adapted for non-ILLink use ----
- protected override void LoadDesigner ()
+ 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 (mainAssembly != null)
- return;
- // resolve the MainAssembly Resource designer TypeDefinition
- AndroidLinkConfiguration config = AndroidLinkConfiguration.GetInstance (Context);
- if (config == null)
- return;
- foreach(var asm in config.Assemblies) {
- if (FindResourceDesigner (asm, mainApplication: true, designer: out mainDesigner, designerAttribute: out mainDesignerAttribute)) {
- mainAssembly = asm;
- break;
+ 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 (mainDesigner == null) {
- LogMessage ($" Main Designer not found.");
- return;
+ }
+
+ if (string.IsNullOrEmpty(designerFullName)) {
+ logMessage ($"Inspecting member references for assembly: {assembly.FullName};");
+ var memberRefs = assembly.MainModule.GetMemberReferences ();
+ foreach (var memberRef in memberRefs) {
+ string declaringType = memberRef.DeclaringType?.ToString () ?? string.Empty;
+ if (!declaringType.Contains (".Resource/")) {
+ continue;
+ }
+ if (declaringType.Contains ("_Microsoft.Android.Resource.Designer")) {
+ continue;
+ }
+ var resolved = false;
+ try {
+ var def = memberRef.Resolve ();
+ if (resolved = def != null) {
+ logMessage ($"Resolved member `{memberRef?.Name}`");
+ }
+ } catch (Exception ex) {
+ logMessage ($"Exception resolving member `{memberRef?.Name}`: {ex}");
+ resolved = false;
+ }
+ if (!resolved) {
+ logMessage ($"Adding _Linker.Generated.Resource to {assembly.Name.Name}. Could not resolve {memberRef?.Name} : {declaringType}");
+ designer = new TypeDefinition ("_Linker.Generated", "Resource", TypeAttributes.Public | TypeAttributes.AnsiClass);
+ designer.BaseType = new TypeDefinition ("System", "Object", TypeAttributes.Public | TypeAttributes.AnsiClass);
+ return true;
+ }
}
- LogMessage ($" Main Designer found {mainDesigner.FullName}.");
- designerConstants = BuildResourceDesignerFieldLookup (mainDesigner);
}
- protected override void EndProcess ()
+ if (string.IsNullOrEmpty(designerFullName))
+ return false;
+
+ foreach (ModuleDefinition module in assembly.Modules)
{
- if (mainDesigner != null) {
- LogMessage ($" Setting Action on {mainAssembly.Name} to Save.");
- Annotations.SetAction (mainAssembly, AssemblyAction.Save);
+ foreach (TypeDefinition type in module.Types)
+ {
+ if (type.FullName == designerFullName)
+ {
+ designer = type;
+ return true;
+ }
}
}
+ return false;
+ }
- protected override void FixBody (MethodBody body, TypeDefinition designer)
+ void FixBody (MethodBody body, TypeDefinition designer)
+ {
+ Dictionary instructions = new Dictionary();
+ var processor = body.GetILProcessor ();
+ string designerFullName = $"{designer.FullName}/";
+ bool isDesignerMethod = designerFullName.Contains (body.Method.DeclaringType.FullName);
+ string declaringTypeName = body.Method.DeclaringType.Name;
+ foreach (var i in body.Instructions)
{
- Dictionary instructions = new Dictionary();
- var processor = body.GetILProcessor ();
- string designerFullName = $"{designer.FullName}/";
- bool isDesignerMethod = designerFullName.Contains (body.Method.DeclaringType.FullName);
- string declaringTypeName = body.Method.DeclaringType.Name;
- foreach (var i in body.Instructions)
+ string line = i.ToString ();
+ if ((line.Contains (designerFullName) || (isDesignerMethod && i.OpCode == OpCodes.Stsfld)) && !instructions.ContainsKey (i))
{
- string line = i.ToString ();
- if ((line.Contains (designerFullName) || (isDesignerMethod && i.OpCode == OpCodes.Stsfld)) && !instructions.ContainsKey (i))
- {
- var match = opCodeRegex.Match (line);
- if (match.Success && match.Groups.Count == 5) {
- string key = match.Groups[4].Value.Replace (designerFullName, string.Empty);
- if (isDesignerMethod) {
- key = declaringTypeName +"::" + key;
- }
- if (designerConstants.ContainsKey (key) && !instructions.ContainsKey (i))
- instructions.Add(i, designerConstants [key]);
+ var match = opCodeRegex.Match (line);
+ if (match.Success && match.Groups.Count == 5) {
+ string key = match.Groups[4].Value.Replace (designerFullName, string.Empty);
+ if (isDesignerMethod) {
+ key = declaringTypeName +"::" + key;
}
+ if (designerConstants.ContainsKey (key) && !instructions.ContainsKey (i))
+ instructions.Add(i, designerConstants [key]);
}
}
- if (instructions.Count > 0)
- LogMessage ($" Fixing up {body.Method.FullName}");
- foreach (var i in instructions)
+ }
+ if (instructions.Count > 0)
+ logMessage ($" Fixing up {body.Method.FullName}");
+ foreach (var i in instructions)
+ {
+ var newCode = Extensions.CreateLoadArraySizeOrOffsetInstruction (i.Value);
+ logMessage ($" Replacing {i.Key}");
+ logMessage ($" With {newCode}");
+ processor.Replace(i.Key, newCode);
+ }
+ }
+
+ Dictionary BuildResourceDesignerFieldLookup (TypeDefinition type)
+ {
+ var output = new Dictionary ();
+ foreach (TypeDefinition definition in type.NestedTypes)
+ {
+ foreach (FieldDefinition field in definition.Fields)
{
- var newCode = Extensions.CreateLoadArraySizeOrOffsetInstruction (i.Value);
- LogMessage ($" Replacing {i.Key}");
- LogMessage ($" With {newCode}");
- processor.Replace(i.Key, newCode);
+ string key = $"{definition.Name}::{field.Name}";
+ if (!output.ContainsKey (key))
+ output.Add(key, int.Parse (field.Constant?.ToString () ?? "0", CultureInfo.InvariantCulture));
}
}
+ return output;
+ }
- internal override bool ProcessAssemblyDesigner (AssemblyDefinition assembly)
+ void ClearDesignerClass (TypeDefinition designer, bool completely = false)
+ {
+ logMessage ($" TryRemoving {designer.FullName}");
+ // for each of the nested types clear all but the
+ // int[] fields.
+ if (!completely) {
+ for (int i = designer.NestedTypes.Count -1; i >= 0; i--) {
+ var nestedType = designer.NestedTypes [i];
+ RemoveFieldsFromType (nestedType, designer.Module);
+ if (nestedType.Fields.Count == 0) {
+ // no fields we do not need this class at all.
+ designer.NestedTypes.RemoveAt (i);
+ }
+ }
+ RemoveUpdateIdValues (designer);
+ } else {
+ designer.NestedTypes.Clear ();
+ }
+ designer.Fields.Clear ();
+ designer.Properties.Clear ();
+ designer.CustomAttributes.Clear ();
+ designer.Interfaces.Clear ();
+ designer.Events.Clear ();
+ }
+
+ void FixType (TypeDefinition type, TypeDefinition localDesigner)
+ {
+ foreach (MethodDefinition method in type.Methods)
{
- if (mainDesigner == null)
- return false;
- if (MonoAndroidHelper.IsFrameworkAssembly (assembly))
- return false;
+ 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);
+ }
+ }
- LogMessage ($" Fixing up {assembly.Name.Name}");
- TypeDefinition localDesigner = null;
- CustomAttribute designerAttribute;
- if (assembly != mainAssembly) {
- 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 false;
- }
- } else {
- LogMessage ($" {assembly.Name.Name} is the main assembly. ");
- localDesigner = mainDesigner;
- designerAttribute = mainDesignerAttribute;
+ 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);
+ }
+ }
+ }
+
+ void RemoveFieldsFromType (TypeDefinition type, ModuleDefinition module)
+ {
+ for (int i = type.Fields.Count - 1; i >= 0; i--) {
+ var field = type.Fields [i];
+ if (field.FieldType.IsArray) {
+ continue;
}
+ logMessage ($"Removing {type.Name}::{field.Name}");
+ type.Fields.RemoveAt (i);
+ }
+ }
- LogMessage ($" {assembly.Name.Name} has designer {localDesigner.FullName}.");
+ void RemoveUpdateIdValues (TypeDefinition type)
+ {
+ foreach (var method in type.Methods) {
+ if (method.Name.Contains ("UpdateIdValues")) {
+ FixUpdateIdValuesBody (method);
+ } else {
+ FixBody (method.Body, type);
+ }
+ }
- FixupAssemblyTypes (assembly, localDesigner);
+ foreach (var nestedType in type.NestedTypes) {
+ RemoveUpdateIdValues (nestedType);
+ }
+ }
- ClearDesignerClass (localDesigner);
- if (designerAttribute != null) {
- assembly.CustomAttributes.Remove (designerAttribute);
+ void FixUpdateIdValuesBody (MethodDefinition method)
+ {
+ List finalInstructions = new List ();
+ Collection instructions = method.Body.Instructions;
+ for (int i = 0; i < method.Body.Instructions.Count-1; i++) {
+ Instruction instruction = instructions[i];
+ string line = instruction.ToString ();
+ bool found = line.Contains ("Int32[]") || instruction.OpCode == OpCodes.Ret;
+ if (!found) {
+ method.Body.Instructions.Remove (instruction);
+ i--;
}
- return true;
}
}
}
diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.LlvmIr.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.LlvmIr.targets
index 84e68d8bf6f..9b8870a8295 100644
--- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.LlvmIr.targets
+++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.LlvmIr.targets
@@ -198,18 +198,6 @@
<_TrimmerCustomSteps Include="$(_AndroidLinkerCustomStepAssembly)" Type="MonoDroid.Tuner.FixAbstractMethodsStep" />
- <_TrimmerCustomSteps
- Condition=" '$(AndroidLinkResources)' == 'true' "
- Include="$(_AndroidLinkerCustomStepAssembly)"
- AfterStep="CleanStep"
- Type="MonoDroid.Tuner.RemoveResourceDesignerStep"
- />
- <_TrimmerCustomSteps
- Condition=" '$(AndroidLinkResources)' == 'true' "
- Include="$(_AndroidLinkerCustomStepAssembly)"
- AfterStep="CleanStep"
- Type="MonoDroid.Tuner.GetAssembliesStep"
- />
<_TrimmerCustomSteps
Condition=" '$(AndroidUseDesignerAssembly)' == 'true' "
Include="$(_AndroidLinkerCustomStepAssembly)"
@@ -259,6 +247,7 @@
diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/PostTrimmingPipeline.cs b/src/Xamarin.Android.Build.Tasks/Tasks/PostTrimmingPipeline.cs
index 7a33ea3c7be..f7b91cfb1ba 100644
--- a/src/Xamarin.Android.Build.Tasks/Tasks/PostTrimmingPipeline.cs
+++ b/src/Xamarin.Android.Build.Tasks/Tasks/PostTrimmingPipeline.cs
@@ -29,6 +29,8 @@ public class PostTrimmingPipeline : AndroidTask
public bool AddKeepAlives { get; set; }
+ public bool AndroidLinkResources { get; set; }
+
public bool Deterministic { get; set; }
public override bool RunTask ()
@@ -66,6 +68,13 @@ public override bool RunTask ()
},
(msg) => Log.LogDebugMessage (msg)));
}
+ if (AndroidLinkResources) {
+ var allAssemblies = new List (Assemblies.Length);
+ foreach (var item in Assemblies) {
+ allAssemblies.Add (resolver.GetAssembly (item.ItemSpec));
+ }
+ steps.Add (new RemoveResourceDesignerStep (allAssemblies, (msg) => Log.LogDebugMessage (msg)));
+ }
foreach (var item in Assemblies) {
var assembly = resolver.GetAssembly (item.ItemSpec);
diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.csproj b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.csproj
index c9157be3712..a79d667bb18 100644
--- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.csproj
+++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.csproj
@@ -53,12 +53,12 @@
+
-