forked from dotnet/android
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathFixLegacyResourceDesignerStep.cs
More file actions
160 lines (143 loc) · 5.38 KB
/
FixLegacyResourceDesignerStep.cs
File metadata and controls
160 lines (143 loc) · 5.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
using System;
using System.Collections.Generic;
using System.Linq;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Java.Interop.Tools.Cecil;
using Mono.Linker;
using Mono.Linker.Steps;
using Mono.Tuner;
#if ILLINK
using Microsoft.Android.Sdk.ILLink;
#endif // ILLINK
namespace MonoDroid.Tuner
{
public class FixLegacyResourceDesignerStep : LinkDesignerBase
{
internal const string DesignerAssemblyName = "_Microsoft.Android.Resource.Designer";
internal const string DesignerAssemblyNamespace = "Microsoft.Android.Resource.Designer";
bool designerLoaded = false;
AssemblyDefinition designerAssembly = null;
TypeDefinition designerType = null;
Dictionary<string, MethodDefinition> lookup;
protected override void EndProcess ()
{
if (designerAssembly != null) {
LogMessage ($" Setting Action on {designerAssembly.Name} to Link.");
Annotations.SetAction (designerAssembly, AssemblyAction.Link);
}
}
protected override void LoadDesigner ()
{
if (designerLoaded)
return;
try {
var designerNameAssembly = AssemblyNameReference.Parse ($"{DesignerAssemblyName}, Version=1.0.0.0");
try {
designerAssembly = Resolve (designerNameAssembly);
LogMessage ($" Loaded {designerNameAssembly}");
} catch (Mono.Cecil.AssemblyResolutionException) {
LogMessage ($" Could not resolve assembly {DesignerAssemblyName}.");
} catch (System.IO.FileNotFoundException) {
LogMessage ($" Assembly {DesignerAssemblyName} did not exist.");
}
if (designerAssembly == null) {
return;
}
designerType = designerAssembly.MainModule.GetTypes ().FirstOrDefault (x => x.FullName == $"{DesignerAssemblyNamespace}.Resource");
if (designerType == null) {
LogMessage ($" Did not find {DesignerAssemblyNamespace}.Resource type. It was probably linked out.");
return;
}
lookup = BuildResourceDesignerPropertyLookup (designerType);
} finally {
designerLoaded = true;
}
}
internal override bool ProcessAssemblyDesigner (AssemblyDefinition assembly)
{
if (!FindResourceDesigner (assembly, mainApplication: false, out TypeDefinition designer, out CustomAttribute designerAttribute)) {
LogMessage ($" {assembly.Name.Name} has no designer. ");
return false;
}
LogMessage ($" {assembly.Name.Name} has a designer. ");
LogMessage ($" BaseType: {designer.BaseType.FullName}. ");
if (designer.BaseType.FullName == $"{DesignerAssemblyNamespace}.Resource") {
LogMessage ($" {assembly.Name.Name} has already been processed. ");
return false;
}
// This is expected for the first call, in <LinkAssembliesNoShrink/>
if (!designerLoaded)
LoadDesigner ();
if (designerAssembly == null || designerType == null) {
LogMessage ($" Not using {DesignerAssemblyName}");
return false;
}
LogMessage ($" Adding reference {designerAssembly.Name.Name}.");
assembly.MainModule.AssemblyReferences.Add (designerAssembly.Name);
var importedDesignerType = assembly.MainModule.ImportReference (designerType.Resolve ());
LogMessage ($" FixupAssemblyTypes {assembly.Name.Name}.");
// now replace all ldsfld with a call to the property get_ method.
FixupAssemblyTypes (assembly, designer);
LogMessage ($" ClearDesignerClass {assembly.Name.Name}.");
// then clean out the designer.
ClearDesignerClass (designer, completely: true);
designer.BaseType = importedDesignerType;
return true;
}
Dictionary<string, MethodDefinition> BuildResourceDesignerPropertyLookup (TypeDefinition type)
{
LogMessage ($" Building Designer Lookups for {type.FullName}");
var output = new Dictionary<string, MethodDefinition> (StringComparer.Ordinal);
foreach (TypeDefinition definition in type.NestedTypes)
{
foreach (PropertyDefinition property in definition.Properties)
{
string key = $"{definition.Name}::{property.Name}";
if (output.ContainsKey (key)) {
LogMessage ($" Found duplicate {key}");
} else {
output.Add (key, property.GetMethod);
}
}
}
return output;
}
protected override void FixBody (MethodBody body, TypeDefinition designer)
{
// replace
// IL_0068: ldsfld int32 Xamarin.Forms.Platform.Android.Resource/Layout::Toolbar
// with
// call int32 Xamarin.Forms.Platform.Android.Resource/Layout::get_Toolbar()
string designerFullName = $"{designer.FullName}/";
var processor = body.GetILProcessor ();
Dictionary<Instruction, Instruction> instructions = new Dictionary<Instruction, Instruction>();
foreach (var i in body.Instructions)
{
if (i.OpCode != OpCodes.Ldsfld)
continue;
string line = i.ToString ();
int idx = line.IndexOf (designerFullName, StringComparison.Ordinal);
if (idx >= 0) {
string key = line.Substring (idx + designerFullName.Length);
LogMessage ($"Looking for {key}.");
if (lookup.TryGetValue (key, out MethodDefinition method)) {
var importedMethod = designer.Module.ImportReference (method);
var newIn = Instruction.Create (OpCodes.Call, importedMethod);
instructions.Add (i, newIn);
} else {
LogMessage ($"DEBUG! Failed to find {key}!");
}
}
}
if (instructions.Count > 0)
LogMessage ($" Fixing up {body.Method.FullName}");
foreach (var i in instructions)
{
LogMessage ($" Replacing {i.Key}");
LogMessage ($" With {i.Value}");
processor.Replace(i.Key, i.Value);
}
}
}
}