| File: DebugXamlCTask.cs | Web Access |
| Project: src\src\Controls\src\Build.Tasks\Controls.Build.Tasks.csproj (Microsoft.Maui.Controls.Build.Tasks) |
using System; using System.Collections.Generic; using System.Linq; using Mono.Cecil; using Mono.Cecil.Cil; using Mono.Cecil.Rocks; using static Microsoft.Build.Framework.MessageImportance; using IOPath = System.IO.Path; namespace Microsoft.Maui.Controls.Build.Tasks { public class DebugXamlCTask : XamlTask { readonly XamlCache cache = new(); 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 (ReferencePath != null) { var paths = ReferencePath.Select(p => IOPath.GetDirectoryName(p.Replace("//", "/"))).Distinct(); foreach (var searchpath in paths) { 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(cache, 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 [Microsoft.Maui.Controls]Microsoft.Maui.Controls.ContentPage::'.ctor'() // // IL_0006: nop // IL_0007: ldarg.1 // IL_0008: brfalse IL_0018 // // IL_000d: ldarg.0 // IL_000e: callvirt instance void class Microsoft.Maui.Controls.Xaml.XamlcTests.MyPage::InitializeComponent() // IL_0013: br IL_001e // // IL_0018: ldarg.0 // IL_0019: callvirt instance void class Microsoft.Maui.Controls.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); MethodReference baseCtor; if (typeDef.BaseType.Resolve().GetConstructors().FirstOrDefault(c => c.HasParameters && c.Parameters.Count == 1 && c.Parameters[0].Name == "useCompiledXaml") is MethodDefinition baseCtorDef) { baseCtor = module.ImportReference(baseCtorDef); baseCtor = module.ImportReference(baseCtor.ResolveGenericParameters(typeDef.BaseType, module)); il.Emit(OpCodes.Ldarg_1); } else { baseCtor = module.ImportReference(typeDef.BaseType.Resolve().GetConstructors().First(c => c.HasParameters == false)); baseCtor = module.ImportReference(baseCtor.ResolveGenericParameters(typeDef.BaseType, module)); } il.Emit(OpCodes.Callvirt, baseCtor); 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; } } }