Skip to content

Commit

Permalink
[Xamarin.Android.Build.Tasks] Fix the RemoveResourceDesignerStep to…
Browse files Browse the repository at this point in the history
… work under .NET 6

The current implementation of the `RemoveResourceDesignerStep` does not
work under .NET 6. This is mostly down to the fact that we didn't
`override` the require methods to make it work in that environment.

This PR does also rework the `RemoveResourceDesignerStep` to split some
of the functionality out into a new base class. This new base class will
be used in the future by the Resource Assembly linker step. Since much of
the code is identical.
  • Loading branch information
dellis1972 committed Feb 15, 2022
1 parent 9666fe4 commit 1fe732a
Show file tree
Hide file tree
Showing 4 changed files with 189 additions and 123 deletions.
Original file line number Diff line number Diff line change
@@ -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<string, int> BuildResourceDesignerFieldLookup (TypeDefinition type)
{
var output = new Dictionary<string, int> ();
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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, int> 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)
Expand All @@ -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<string, int> BuildResourceDesignerFieldLookup (TypeDefinition type)
{
var output = new Dictionary<string, int> ();
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<Instruction, int> instructions = new Dictionary<Instruction, int>();
var processor = body.GetILProcessor ();
string designerFullName = $"{localDesigner.FullName}/";
string designerFullName = $"{designer.FullName}/";
foreach (var i in body.Instructions)
{
string line = i.ToString ();
Expand All @@ -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;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,9 @@
<InvariantGlobalization Condition="'$(InvariantGlobalization)' == ''">false</InvariantGlobalization>
<StartupHookSupport Condition="'$(StartupHookSupport)' == ''">false</StartupHookSupport>
<UseNativeHttpHandler Condition="'$(UseNativeHttpHandler)' == ''">true</UseNativeHttpHandler>
<_AggressiveAttributeTrimming Condition="'$(_AggressiveAttributeTrimming)' == ''">true</_AggressiveAttributeTrimming>
<_AggressiveAttributeTrimming Condition="'$(_AggressiveAttributeTrimming)' == ''">true</_AggressiveAttributeTrimming>
<NullabilityInfoContextSupport Condition="'$(NullabilityInfoContextSupport)' == ''">false</NullabilityInfoContextSupport>
<BuiltInComInteropSupport Condition="'$(BuiltInComInteropSupport)' == ''">false</BuiltInComInteropSupport>
<BuiltInComInteropSupport Condition="'$(BuiltInComInteropSupport)' == ''">false</BuiltInComInteropSupport>
<!-- Verify DI trimmability at development-time, but turn the validation off for production/trimmed builds. -->
<VerifyDependencyInjectionOpenGenericServiceTrimmability Condition="'$(VerifyDependencyInjectionOpenGenericServiceTrimmability)' == '' and '$(PublishTrimmed)' == 'true'">false</VerifyDependencyInjectionOpenGenericServiceTrimmability>
<VerifyDependencyInjectionOpenGenericServiceTrimmability Condition="'$(VerifyDependencyInjectionOpenGenericServiceTrimmability)' == ''">true</VerifyDependencyInjectionOpenGenericServiceTrimmability>
Expand Down
Loading

0 comments on commit 1fe732a

Please sign in to comment.