-
Notifications
You must be signed in to change notification settings - Fork 537
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
First Pass at using Resource Designer assembly [WIP]
Context #6310
- Loading branch information
1 parent
f3de57d
commit edaa5d8
Showing
27 changed files
with
1,013 additions
and
152 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
145 changes: 145 additions & 0 deletions
145
src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/FixLegacyResourceDesignerStep.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
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 | ||
{ | ||
#if ILLINK | ||
protected override void Process () | ||
{ | ||
cache = Context; | ||
} | ||
#else | ||
public FixLegacyResourceDesignerStep (IMetadataResolver cache) | ||
{ | ||
this.cache = cache; | ||
} | ||
|
||
readonly | ||
#endif | ||
IMetadataResolver cache; | ||
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 (designerAssembly != null) | ||
return; | ||
var designerNameAssembly = AssemblyNameReference.Parse ("Xamarin.Android.Resource.Designer, Version=1.0.0.0"); | ||
try { | ||
designerAssembly = Resolve (designerNameAssembly); | ||
} catch (Mono.Cecil.AssemblyResolutionException) { | ||
} catch (System.IO.FileNotFoundException) { | ||
} | ||
if (designerAssembly == null) { | ||
LogMessage ($" Did not find Xamarin.Android.Resource.Designer"); | ||
return; | ||
} | ||
designerType = designerAssembly.MainModule.GetTypes ().First (x => x.FullName == "Xamarin.Android.Resource.Designer.Resource"); | ||
lookup = BuildResourceDesignerPropertyLookup (designerType); | ||
return; | ||
} | ||
internal override bool ProcessAssemblyDesigner (AssemblyDefinition assembly) | ||
{ | ||
if (designerAssembly == null) { | ||
LogMessage ($" Not using Xamarin.Android.Resource.Designer"); | ||
return false; | ||
} | ||
|
||
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. "); | ||
if (designer.BaseType.FullName == "Xamarin.Android.Resource.Designer.Resource") { | ||
LogMessage ($" {assembly.Name.Name} has already been processed. "); | ||
return false; | ||
} | ||
|
||
assembly.MainModule.AssemblyReferences.Add (designerAssembly.Name); | ||
var importedDesignerType = assembly.MainModule.ImportReference (designerType.Resolve ()); | ||
|
||
// now replace all ldsfld with a call to the property get_ method. | ||
FixupAssemblyTypes (assembly, designer); | ||
|
||
// then clean out the designer. | ||
ClearDesignerClass (designer); | ||
designer.BaseType = importedDesignerType; | ||
return true; | ||
} | ||
|
||
Dictionary<string, MethodDefinition> BuildResourceDesignerPropertyLookup (TypeDefinition type) | ||
{ | ||
LogMessage ($" Building Designer Lookups for {type.FullName}"); | ||
var output = new Dictionary<string, MethodDefinition> (); | ||
foreach (TypeDefinition definition in type.NestedTypes) | ||
{ | ||
foreach (PropertyDefinition property in definition.Properties) | ||
{ | ||
string key = $"{definition.Name}::{property.Name}"; | ||
if (!output.ContainsKey (key)) { | ||
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) | ||
{ | ||
string line = i.ToString (); | ||
int idx = line.IndexOf (designerFullName, StringComparison.OrdinalIgnoreCase); | ||
if (idx >= 0) { | ||
string key = line.Substring (idx + designerFullName.Length); | ||
if (lookup.TryGetValue (key, out MethodDefinition method)) { | ||
var importedMethod = designer.Module.ImportReference (method); | ||
var newIn = Instruction.Create (OpCodes.Call, importedMethod); | ||
instructions.Add (i, newIn); | ||
} | ||
} | ||
} | ||
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); | ||
} | ||
} | ||
} | ||
} |
137 changes: 137 additions & 0 deletions
137
src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/LinkDesignerBase.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
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}"); | ||
designer.NestedTypes.Clear (); | ||
designer.Methods.Clear (); | ||
designer.Fields.Clear (); | ||
designer.Properties.Clear (); | ||
designer.CustomAttributes.Clear (); | ||
designer.Interfaces.Clear (); | ||
designer.Events.Clear (); | ||
} | ||
|
||
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); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.