149 строки
5.9 KiB
C#
149 строки
5.9 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
|
|
using Mono.Cecil;
|
|
using Mono.Cecil.Cil;
|
|
using Mono.Cecil.Rocks;
|
|
|
|
using static Microsoft.Build.Framework.MessageImportance;
|
|
|
|
namespace Xamarin.Forms.Build.Tasks
|
|
{
|
|
public class DebugXamlCTask : XamlTask
|
|
{
|
|
public override bool Execute(out IList<Exception> thrownExceptions)
|
|
{
|
|
thrownExceptions = null;
|
|
LoggingHelper.LogMessage(Normal, $"{new string(' ', 0)}Preparing debug code for xamlc, assembly: {Assembly}");
|
|
|
|
var resolver = new DefaultAssemblyResolver();
|
|
if (!string.IsNullOrEmpty(DependencyPaths)) {
|
|
foreach (var dep in DependencyPaths.Split(';')) {
|
|
LoggingHelper.LogMessage(Low, $"{new string(' ', 2)}Adding searchpath {dep}");
|
|
resolver.AddSearchDirectory(dep);
|
|
}
|
|
}
|
|
if (!string.IsNullOrEmpty(ReferencePath)) {
|
|
var paths = ReferencePath.Replace("//", "/").Split(';');
|
|
foreach (var p in paths) {
|
|
var searchpath = Path.GetDirectoryName(p);
|
|
LoggingHelper.LogMessage(Low, $"{new string(' ', 2)}Adding searchpath {searchpath}");
|
|
resolver.AddSearchDirectory(searchpath);
|
|
}
|
|
}
|
|
|
|
var debug = DebugSymbols || (!string.IsNullOrEmpty(DebugType) && DebugType.ToLowerInvariant() != "none");
|
|
|
|
using (var assemblyDefinition = AssemblyDefinition.ReadAssembly(Assembly, new ReaderParameters {
|
|
ReadWrite = true,
|
|
ReadSymbols = debug,
|
|
AssemblyResolver = resolver
|
|
})) {
|
|
foreach (var module in assemblyDefinition.Modules) {
|
|
LoggingHelper.LogMessage(Low, $"{new string(' ', 2)}Module: {module.Name}");
|
|
foreach (var resource in module.Resources.OfType<EmbeddedResource>()) {
|
|
LoggingHelper.LogMessage(Low, $"{new string(' ', 4)}Resource: {resource.Name}");
|
|
if (!resource.IsXaml(module, out var classname)) {
|
|
LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}skipped.");
|
|
continue;
|
|
}
|
|
TypeDefinition typeDef = module.GetType(classname);
|
|
if (typeDef == null) {
|
|
LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}no type found... skipped.");
|
|
continue;
|
|
}
|
|
|
|
var initComp = typeDef.Methods.FirstOrDefault(md => md.Name == "InitializeComponent");
|
|
if (initComp == null) {
|
|
LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}no InitializeComponent found... skipped.");
|
|
continue;
|
|
}
|
|
var initCompRuntime = typeDef.Methods.FirstOrDefault(md => md.Name == "__InitComponentRuntime");
|
|
if (initCompRuntime == null) {
|
|
LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}Creating empty {typeDef.Name}.__InitComponentRuntime");
|
|
initCompRuntime = new MethodDefinition("__InitComponentRuntime", initComp.Attributes, initComp.ReturnType);
|
|
initCompRuntime.Body.InitLocals = true;
|
|
LoggingHelper.LogMessage(Low, $"{new string(' ', 8)}done.");
|
|
LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}Copying body of InitializeComponent to __InitComponentRuntime");
|
|
initCompRuntime.Body = new MethodBody(initCompRuntime);
|
|
var iCRIl = initCompRuntime.Body.GetILProcessor();
|
|
foreach (var instr in initComp.Body.Instructions)
|
|
iCRIl.Append(instr);
|
|
initComp.Body.Instructions.Clear();
|
|
initComp.Body.GetILProcessor().Emit(OpCodes.Ret);
|
|
typeDef.Methods.Add(initCompRuntime);
|
|
LoggingHelper.LogMessage(Low, $"{new string(' ', 8)}done.");
|
|
}
|
|
|
|
// IL_0000: ldarg.0
|
|
// IL_0001: callvirt instance void class [Xamarin.Forms.Core]Xamarin.Forms.ContentPage::'.ctor'()
|
|
//
|
|
// IL_0006: nop
|
|
// IL_0007: ldarg.1
|
|
// IL_0008: brfalse IL_0018
|
|
//
|
|
// IL_000d: ldarg.0
|
|
// IL_000e: callvirt instance void class Xamarin.Forms.Xaml.XamlcTests.MyPage::InitializeComponent()
|
|
// IL_0013: br IL_001e
|
|
//
|
|
// IL_0018: ldarg.0
|
|
// IL_0019: callvirt instance void class Xamarin.Forms.Xaml.XamlcTests.MyPage::__InitComponentRuntime()
|
|
// IL_001e: ret
|
|
|
|
var altCtor = typeDef.Methods.FirstOrDefault(md => md.IsConstructor
|
|
&& md.Parameters.Count == 1
|
|
&& md.Parameters[0].ParameterType == module.TypeSystem.Boolean);
|
|
if (altCtor != null)
|
|
LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}Replacing body of {typeDef.Name}.{typeDef.Name} (bool {altCtor.Parameters[0].Name})");
|
|
else {
|
|
LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}Adding {typeDef.Name}.{typeDef.Name} (bool useCompiledXaml)");
|
|
altCtor = new MethodDefinition(".ctor",
|
|
MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName |
|
|
MethodAttributes.RTSpecialName, module.TypeSystem.Void);
|
|
altCtor.Parameters.Add(new ParameterDefinition("useCompiledXaml", ParameterAttributes.None,
|
|
module.TypeSystem.Boolean));
|
|
}
|
|
|
|
var body = new MethodBody(altCtor) {
|
|
InitLocals = true
|
|
};
|
|
var il = body.GetILProcessor();
|
|
var br2 = Instruction.Create(OpCodes.Ldarg_0);
|
|
var ret = Instruction.Create(OpCodes.Ret);
|
|
il.Emit(OpCodes.Ldarg_0);
|
|
il.Emit(OpCodes.Callvirt,
|
|
module.ImportReference(typeDef.BaseType.Resolve().GetConstructors().First(c => c.HasParameters == false)));
|
|
|
|
il.Emit(OpCodes.Nop);
|
|
il.Emit(OpCodes.Ldarg_1);
|
|
il.Emit(OpCodes.Brfalse, br2);
|
|
|
|
il.Emit(OpCodes.Ldarg_0);
|
|
il.Emit(OpCodes.Callvirt, initComp);
|
|
il.Emit(OpCodes.Br, ret);
|
|
|
|
il.Append(br2);
|
|
il.Emit(OpCodes.Callvirt, initCompRuntime);
|
|
il.Append(ret);
|
|
|
|
altCtor.Body = body;
|
|
if (!typeDef.Methods.Contains(altCtor))
|
|
typeDef.Methods.Add(altCtor);
|
|
LoggingHelper.LogMessage(Low, $"{new string(' ', 8)}done.");
|
|
}
|
|
|
|
}
|
|
LoggingHelper.LogMessage(Normal, $"{new string(' ', 0)}Writing the assembly.");
|
|
assemblyDefinition.Write(new WriterParameters {
|
|
WriteSymbols = debug
|
|
});
|
|
}
|
|
LoggingHelper.LogMessage(Normal, $"{new string(' ', 2)}done.");
|
|
|
|
return true;
|
|
}
|
|
}
|
|
}
|