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;
		}
	}
}