Skip to content

Commit edaa5d8

Browse files
committed
First Pass at using Resource Designer assembly [WIP]
Context #6310
1 parent f3de57d commit edaa5d8

27 files changed

+1013
-152
lines changed

build-tools/installers/create-installers.targets

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,7 @@
268268
<_MSBuildFiles Include="$(MSBuildSrcDir)\Xamarin.Android.CSharp.targets" ExcludeFromAndroidNETSdk="true" />
269269
<_MSBuildFiles Include="$(MSBuildSrcDir)\Xamarin.Android.D8.targets" />
270270
<_MSBuildFiles Include="$(MSBuildSrcDir)\Xamarin.Android.Designer.targets" />
271+
<_MSBuildFiles Include="$(MSBuildSrcDir)\Xamarin.Android.Resource.Designer.targets" />
271272
<_MSBuildFiles Include="$(MSBuildSrcDir)\Xamarin.Android.DesignTime.targets" />
272273
<_MSBuildFiles Include="$(MSBuildSrcDir)\Xamarin.Android.DX.targets" ExcludeFromAndroidNETSdk="true" />
273274
<_MSBuildFiles Include="$(MSBuildSrcDir)\Xamarin.Android.EmbeddedResource.targets" />
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
5+
using Mono.Cecil;
6+
using Mono.Cecil.Cil;
7+
8+
using Java.Interop.Tools.Cecil;
9+
10+
using Mono.Linker;
11+
using Mono.Linker.Steps;
12+
13+
using Mono.Tuner;
14+
#if ILLINK
15+
using Microsoft.Android.Sdk.ILLink;
16+
#endif // ILLINK
17+
18+
namespace MonoDroid.Tuner
19+
{
20+
public class FixLegacyResourceDesignerStep : LinkDesignerBase
21+
{
22+
#if ILLINK
23+
protected override void Process ()
24+
{
25+
cache = Context;
26+
}
27+
#else
28+
public FixLegacyResourceDesignerStep (IMetadataResolver cache)
29+
{
30+
this.cache = cache;
31+
}
32+
33+
readonly
34+
#endif
35+
IMetadataResolver cache;
36+
AssemblyDefinition designerAssembly = null;
37+
TypeDefinition designerType = null;
38+
Dictionary<string, MethodDefinition> lookup;
39+
40+
protected override void EndProcess ()
41+
{
42+
if (designerAssembly != null) {
43+
LogMessage ($" Setting Action on {designerAssembly.Name} to Link.");
44+
Annotations.SetAction (designerAssembly, AssemblyAction.Link);
45+
}
46+
}
47+
48+
protected override void LoadDesigner ()
49+
{
50+
if (designerAssembly != null)
51+
return;
52+
var designerNameAssembly = AssemblyNameReference.Parse ("Xamarin.Android.Resource.Designer, Version=1.0.0.0");
53+
try {
54+
designerAssembly = Resolve (designerNameAssembly);
55+
} catch (Mono.Cecil.AssemblyResolutionException) {
56+
} catch (System.IO.FileNotFoundException) {
57+
}
58+
if (designerAssembly == null) {
59+
LogMessage ($" Did not find Xamarin.Android.Resource.Designer");
60+
return;
61+
}
62+
designerType = designerAssembly.MainModule.GetTypes ().First (x => x.FullName == "Xamarin.Android.Resource.Designer.Resource");
63+
lookup = BuildResourceDesignerPropertyLookup (designerType);
64+
return;
65+
}
66+
internal override bool ProcessAssemblyDesigner (AssemblyDefinition assembly)
67+
{
68+
if (designerAssembly == null) {
69+
LogMessage ($" Not using Xamarin.Android.Resource.Designer");
70+
return false;
71+
}
72+
73+
if (!FindResourceDesigner (assembly, mainApplication: false, out TypeDefinition designer, out CustomAttribute designerAttribute)) {
74+
LogMessage ($" {assembly.Name.Name} has no designer. ");
75+
return false;
76+
}
77+
78+
LogMessage ($" {assembly.Name.Name} has a designer. ");
79+
if (designer.BaseType.FullName == "Xamarin.Android.Resource.Designer.Resource") {
80+
LogMessage ($" {assembly.Name.Name} has already been processed. ");
81+
return false;
82+
}
83+
84+
assembly.MainModule.AssemblyReferences.Add (designerAssembly.Name);
85+
var importedDesignerType = assembly.MainModule.ImportReference (designerType.Resolve ());
86+
87+
// now replace all ldsfld with a call to the property get_ method.
88+
FixupAssemblyTypes (assembly, designer);
89+
90+
// then clean out the designer.
91+
ClearDesignerClass (designer);
92+
designer.BaseType = importedDesignerType;
93+
return true;
94+
}
95+
96+
Dictionary<string, MethodDefinition> BuildResourceDesignerPropertyLookup (TypeDefinition type)
97+
{
98+
LogMessage ($" Building Designer Lookups for {type.FullName}");
99+
var output = new Dictionary<string, MethodDefinition> ();
100+
foreach (TypeDefinition definition in type.NestedTypes)
101+
{
102+
foreach (PropertyDefinition property in definition.Properties)
103+
{
104+
string key = $"{definition.Name}::{property.Name}";
105+
if (!output.ContainsKey (key)) {
106+
output.Add(key, property.GetMethod);
107+
}
108+
}
109+
}
110+
return output;
111+
}
112+
113+
protected override void FixBody (MethodBody body, TypeDefinition designer)
114+
{
115+
// replace
116+
// IL_0068: ldsfld int32 Xamarin.Forms.Platform.Android.Resource/Layout::Toolbar
117+
// with
118+
// call int32 Xamarin.Forms.Platform.Android.Resource/Layout::get_Toolbar()
119+
string designerFullName = $"{designer.FullName}/";
120+
var processor = body.GetILProcessor ();
121+
Dictionary<Instruction, Instruction> instructions = new Dictionary<Instruction, Instruction>();
122+
foreach (var i in body.Instructions)
123+
{
124+
string line = i.ToString ();
125+
int idx = line.IndexOf (designerFullName, StringComparison.OrdinalIgnoreCase);
126+
if (idx >= 0) {
127+
string key = line.Substring (idx + designerFullName.Length);
128+
if (lookup.TryGetValue (key, out MethodDefinition method)) {
129+
var importedMethod = designer.Module.ImportReference (method);
130+
var newIn = Instruction.Create (OpCodes.Call, importedMethod);
131+
instructions.Add (i, newIn);
132+
}
133+
}
134+
}
135+
if (instructions.Count > 0)
136+
LogMessage ($" Fixing up {body.Method.FullName}");
137+
foreach (var i in instructions)
138+
{
139+
LogMessage ($" Replacing {i.Key}");
140+
LogMessage ($" With {i.Value}");
141+
processor.Replace(i.Key, i.Value);
142+
}
143+
}
144+
}
145+
}
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
using Mono.Cecil;
2+
using Mono.Linker;
3+
using Mono.Linker.Steps;
4+
using System;
5+
using System.Linq;
6+
using Xamarin.Android.Tasks;
7+
using System.Collections.Generic;
8+
using Mono.Cecil.Cil;
9+
using System.Text.RegularExpressions;
10+
#if ILLINK
11+
using Microsoft.Android.Sdk.ILLink;
12+
#endif
13+
14+
15+
namespace MonoDroid.Tuner {
16+
public abstract class LinkDesignerBase : BaseStep {
17+
public virtual void LogMessage (string message)
18+
{
19+
Context.LogMessage (message);
20+
}
21+
22+
public virtual AssemblyDefinition Resolve (AssemblyNameReference name)
23+
{
24+
return Context.Resolve (name);
25+
}
26+
27+
protected bool FindResourceDesigner (AssemblyDefinition assembly, bool mainApplication, out TypeDefinition designer, out CustomAttribute designerAttribute)
28+
{
29+
string designerFullName = null;
30+
designer = null;
31+
designerAttribute = null;
32+
foreach (CustomAttribute attribute in assembly.CustomAttributes)
33+
{
34+
if (attribute.AttributeType.FullName == "Android.Runtime.ResourceDesignerAttribute")
35+
{
36+
designerAttribute = attribute;
37+
if (attribute.HasProperties)
38+
{
39+
foreach (var p in attribute.Properties)
40+
{
41+
if (p.Name == "IsApplication" && (bool)p.Argument.Value == (mainApplication ? mainApplication : (bool)p.Argument.Value))
42+
{
43+
designerFullName = attribute.ConstructorArguments[0].Value.ToString ();
44+
break;
45+
}
46+
}
47+
}
48+
break;
49+
50+
}
51+
}
52+
if (string.IsNullOrEmpty(designerFullName))
53+
return false;
54+
55+
foreach (ModuleDefinition module in assembly.Modules)
56+
{
57+
foreach (TypeDefinition type in module.Types)
58+
{
59+
if (type.FullName == designerFullName)
60+
{
61+
designer = type;
62+
return true;
63+
}
64+
}
65+
}
66+
return false;
67+
}
68+
69+
protected void ClearDesignerClass (TypeDefinition designer)
70+
{
71+
LogMessage ($" TryRemoving {designer.FullName}");
72+
designer.NestedTypes.Clear ();
73+
designer.Methods.Clear ();
74+
designer.Fields.Clear ();
75+
designer.Properties.Clear ();
76+
designer.CustomAttributes.Clear ();
77+
designer.Interfaces.Clear ();
78+
designer.Events.Clear ();
79+
}
80+
81+
protected void FixType (TypeDefinition type, TypeDefinition localDesigner)
82+
{
83+
foreach (MethodDefinition method in type.Methods)
84+
{
85+
if (!method.HasBody)
86+
continue;
87+
FixBody (method.Body, localDesigner);
88+
}
89+
foreach (PropertyDefinition property in type.Properties)
90+
{
91+
if (property.GetMethod != null && property.GetMethod.HasBody)
92+
{
93+
FixBody (property.GetMethod.Body, localDesigner);
94+
}
95+
if (property.SetMethod != null && property.SetMethod.HasBody)
96+
{
97+
FixBody (property.SetMethod.Body, localDesigner);
98+
}
99+
}
100+
foreach (TypeDefinition nestedType in type.NestedTypes)
101+
{
102+
FixType (nestedType, localDesigner);
103+
}
104+
}
105+
106+
protected void FixupAssemblyTypes (AssemblyDefinition assembly, TypeDefinition designer)
107+
{
108+
foreach (ModuleDefinition module in assembly.Modules)
109+
{
110+
foreach (TypeDefinition type in module.Types)
111+
{
112+
if (type.FullName == designer.FullName)
113+
continue;
114+
FixType (type, designer);
115+
}
116+
}
117+
}
118+
119+
protected override void ProcessAssembly (AssemblyDefinition assembly)
120+
{
121+
LoadDesigner ();
122+
123+
var action = Annotations.HasAction (assembly) ? Annotations.GetAction (assembly) : AssemblyAction.Skip;
124+
if (action == AssemblyAction.Delete)
125+
return;
126+
127+
if (ProcessAssemblyDesigner (assembly)) {
128+
if (action == AssemblyAction.Skip || action == AssemblyAction.Copy)
129+
Annotations.SetAction (assembly, AssemblyAction.Save);
130+
}
131+
}
132+
133+
internal abstract bool ProcessAssemblyDesigner (AssemblyDefinition assemblyDefinition);
134+
protected abstract void LoadDesigner ();
135+
protected abstract void FixBody (MethodBody body, TypeDefinition designer);
136+
}
137+
}

src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/Linker.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ static Pipeline CreatePipeline (LinkerOptions options)
111111
pipeline.AppendStep (new RemoveResources (options.I18nAssemblies)); // remove collation tables
112112
// end monodroid specific
113113

114+
if (options.UseDesignerAssembly)
115+
pipeline.AppendStep (new FixLegacyResourceDesignerStep (cache));
114116
pipeline.AppendStep (new FixAbstractMethodsStep (cache));
115117
pipeline.AppendStep (new MonoDroidMarkStep (cache));
116118
pipeline.AppendStep (new SweepStep ());

src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/LinkerOptions.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,6 @@ class LinkerOptions
2626
public bool PreserveJniMarshalMethods { get; set; }
2727
public bool DeterministicOutput { get; set; }
2828
public bool LinkResources { get; set; }
29+
public bool UseDesignerAssembly { get; set; }
2930
}
3031
}

src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/MonoDroidProfile.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ protected override bool IsSdk (string assemblyName)
1919
{
2020
return assemblyName.Equals ("Java.Interop", StringComparison.Ordinal)
2121
|| assemblyName.Equals ("Java.Interop.GenericMarshaler", StringComparison.Ordinal)
22+
|| assemblyName.Equals ("Xamarin.Android.Resource.Designer", StringComparison.Ordinal)
2223
|| base.IsSdk (assemblyName);
2324
}
2425
}

0 commit comments

Comments
 (0)