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 = IOPath.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); 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); }
public override bool Execute(out IList <Exception> thrownExceptions) { thrownExceptions = null; Logger = Logger ?? new Logger(null, Verbosity); Logger.LogLine(1, "Compiling Xaml"); Logger.LogLine(1, "\nAssembly: {0}", Assembly); if (!string.IsNullOrEmpty(DependencyPaths)) { Logger.LogLine(1, "DependencyPaths: \t{0}", DependencyPaths); } if (!string.IsNullOrEmpty(ReferencePath)) { Logger.LogLine(1, "ReferencePath: \t{0}", ReferencePath.Replace("//", "/")); } Logger.LogLine(3, "DebugSymbols:\"{0}\"", DebugSymbols); Logger.LogLine(3, "DebugType:\"{0}\"", DebugType); var skipassembly = true; //change this to false to enable XamlC by default bool success = true; if (!File.Exists(Assembly)) { Logger.LogLine(1, "Assembly file not found. Skipping XamlC."); return(true); } var resolver = new XamlCAssemblyResolver(); if (!string.IsNullOrEmpty(DependencyPaths)) { foreach (var dep in DependencyPaths.Split(';')) { Logger.LogLine(3, "Adding searchpath {0}", dep); resolver.AddSearchDirectory(dep); } } if (!string.IsNullOrEmpty(ReferencePath)) { var paths = ReferencePath.Replace("//", "/").Split(';'); foreach (var p in paths) { var searchpath = Path.GetDirectoryName(p); Logger.LogLine(3, "Adding searchpath {0}", searchpath); resolver.AddSearchDirectory(searchpath); } } var debug = DebugSymbols || (!string.IsNullOrEmpty(DebugType) && DebugType.ToLowerInvariant() != "none"); var readerParameters = new ReaderParameters { AssemblyResolver = resolver, ReadWrite = !ReadOnly, ReadSymbols = debug, }; using (var assemblyDefinition = AssemblyDefinition.ReadAssembly(Path.GetFullPath(Assembly), readerParameters)) { CustomAttribute xamlcAttr; if (assemblyDefinition.HasCustomAttributes && (xamlcAttr = assemblyDefinition.CustomAttributes.FirstOrDefault( ca => ca.AttributeType.FullName == "Xamarin.Forms.Xaml.XamlCompilationAttribute")) != null) { var options = (XamlCompilationOptions)xamlcAttr.ConstructorArguments[0].Value; if ((options & XamlCompilationOptions.Skip) == XamlCompilationOptions.Skip) { skipassembly = true; } if ((options & XamlCompilationOptions.Compile) == XamlCompilationOptions.Compile) { skipassembly = false; } } foreach (var module in assemblyDefinition.Modules) { var skipmodule = skipassembly; if (module.HasCustomAttributes && (xamlcAttr = module.CustomAttributes.FirstOrDefault( ca => ca.AttributeType.FullName == "Xamarin.Forms.Xaml.XamlCompilationAttribute")) != null) { var options = (XamlCompilationOptions)xamlcAttr.ConstructorArguments[0].Value; if ((options & XamlCompilationOptions.Skip) == XamlCompilationOptions.Skip) { skipmodule = true; } if ((options & XamlCompilationOptions.Compile) == XamlCompilationOptions.Compile) { skipmodule = false; } } Logger.LogLine(2, " Module: {0}", module.Name); var resourcesToPrune = new List <EmbeddedResource>(); foreach (var resource in module.Resources.OfType <EmbeddedResource>()) { Logger.LogString(2, " Resource: {0}... ", resource.Name); string classname; if (!resource.IsXaml(out classname)) { Logger.LogLine(2, "skipped."); continue; } TypeDefinition typeDef = module.GetType(classname); if (typeDef == null) { Logger.LogLine(2, "no type found... skipped."); continue; } var skiptype = skipmodule; if (typeDef.HasCustomAttributes && (xamlcAttr = typeDef.CustomAttributes.FirstOrDefault( ca => ca.AttributeType.FullName == "Xamarin.Forms.Xaml.XamlCompilationAttribute")) != null) { var options = (XamlCompilationOptions)xamlcAttr.ConstructorArguments[0].Value; if ((options & XamlCompilationOptions.Skip) == XamlCompilationOptions.Skip) { skiptype = true; } if ((options & XamlCompilationOptions.Compile) == XamlCompilationOptions.Compile) { skiptype = false; } } if (Type != null) { skiptype = !(Type == classname); } if (skiptype) { Logger.LogLine(2, "Has XamlCompilationAttribute set to Skip and not Compile... skipped"); continue; } var initComp = typeDef.Methods.FirstOrDefault(md => md.Name == "InitializeComponent"); if (initComp == null) { Logger.LogLine(2, "no InitializeComponent found... skipped."); continue; } Logger.LogLine(2, ""); CustomAttribute xamlFilePathAttr; var xamlFilePath = typeDef.HasCustomAttributes && (xamlFilePathAttr = typeDef.CustomAttributes.FirstOrDefault(ca => ca.AttributeType.FullName == "Xamarin.Forms.Xaml.XamlFilePathAttribute")) != null ? (string)xamlFilePathAttr.ConstructorArguments[0].Value : resource.Name; var initCompRuntime = typeDef.Methods.FirstOrDefault(md => md.Name == "__InitComponentRuntime"); if (initCompRuntime != null) { Logger.LogLine(2, " __InitComponentRuntime already exists... not creating"); } else { Logger.LogString(2, " Creating empty {0}.__InitComponentRuntime ...", typeDef.Name); initCompRuntime = new MethodDefinition("__InitComponentRuntime", initComp.Attributes, initComp.ReturnType); initCompRuntime.Body.InitLocals = true; Logger.LogLine(2, "done."); Logger.LogString(2, " Copying body of InitializeComponent to __InitComponentRuntime ...", typeDef.Name); 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); initComp.Body.InitLocals = true; typeDef.Methods.Add(initCompRuntime); Logger.LogLine(2, "done."); } Logger.LogString(2, " Parsing Xaml... "); var rootnode = ParseXaml(resource.GetResourceStream(), typeDef); if (rootnode == null) { Logger.LogLine(2, "failed."); continue; } Logger.LogLine(2, "done."); hasCompiledXamlResources = true; Logger.LogString(2, " Replacing {0}.InitializeComponent ()... ", typeDef.Name); Exception e; if (!TryCoreCompile(initComp, initCompRuntime, rootnode, resource.Name, out e)) { success = false; Logger.LogLine(2, "failed."); (thrownExceptions = thrownExceptions ?? new List <Exception>()).Add(e); Logger.LogException(null, null, null, xamlFilePath, e); Logger.LogLine(4, e.StackTrace); continue; } if (Type != null) { InitCompForType = initComp; } Logger.LogLine(2, "done."); if (OptimizeIL) { Logger.LogString(2, " Optimizing IL... "); initComp.Body.Optimize(); Logger.LogLine(2, "done"); } Logger.LogLine(2, ""); #pragma warning disable 0618 if (OutputGeneratedILAsCode) { Logger.LogLine(2, " Decompiling option has been removed. Use a 3rd party decompiler to admire the beauty of the IL generated"); } #pragma warning restore 0618 resourcesToPrune.Add(resource); } if (!KeepXamlResources) { if (resourcesToPrune.Any()) { Logger.LogLine(2, " Removing compiled xaml resources"); } foreach (var resource in resourcesToPrune) { Logger.LogString(2, " Removing {0}... ", resource.Name); module.Resources.Remove(resource); Logger.LogLine(2, "done"); } } Logger.LogLine(2, ""); } if (!hasCompiledXamlResources) { Logger.LogLine(1, "No compiled resources. Skipping writing assembly."); return(success); } if (ReadOnly) { return(success); } Logger.LogString(1, "Writing the assembly... "); try { assemblyDefinition.Write(new WriterParameters { WriteSymbols = debug, }); Logger.LogLine(1, "done."); } catch (Exception e) { Logger.LogLine(1, "failed."); Logger.LogException(null, null, null, null, e); (thrownExceptions = thrownExceptions ?? new List <Exception>()).Add(e); Logger.LogLine(4, e.StackTrace); success = false; } } return(success); }
public override bool Execute(out IList <Exception> thrownExceptions) { thrownExceptions = null; LoggingHelper.LogMessage(Normal, $"{new string(' ', 0)}Compiling Xaml, assembly: {Assembly}"); var skipassembly = !CompileByDefault; bool success = true; if (!File.Exists(Assembly)) { LoggingHelper.LogMessage(Normal, $"{new string(' ', 2)}Assembly file not found. Skipping XamlC."); return(true); } using (var fallbackResolver = DefaultAssemblyResolver == null ? new XamlCAssemblyResolver() : null) { var resolver = DefaultAssemblyResolver ?? fallbackResolver; if (resolver is XamlCAssemblyResolver xamlCResolver) { if (!string.IsNullOrEmpty(DependencyPaths)) { foreach (var dep in DependencyPaths.Split(';').Distinct()) { LoggingHelper.LogMessage(Low, $"{new string(' ', 2)}Adding searchpath {dep}"); xamlCResolver.AddSearchDirectory(dep); } } if (!string.IsNullOrEmpty(ReferencePath)) { var paths = ReferencePath.Replace("//", "/").Split(';').Distinct(); foreach (var p in paths) { var searchpath = Path.GetDirectoryName(p); LoggingHelper.LogMessage(Low, $"{new string(' ', 2)}Adding searchpath {searchpath}"); xamlCResolver.AddSearchDirectory(searchpath); } } } else { LoggingHelper.LogMessage(Low, $"{new string(' ', 2)}Ignoring dependency and reference paths due to an unsupported resolver"); } var debug = DebugSymbols || (!string.IsNullOrEmpty(DebugType) && DebugType.ToLowerInvariant() != "none"); var readerParameters = new ReaderParameters { AssemblyResolver = resolver, ReadWrite = !ValidateOnly, ReadSymbols = debug && !ValidateOnly, // We don't need symbols for ValidateOnly, since we won't be writing }; using (var assemblyDefinition = AssemblyDefinition.ReadAssembly(Path.GetFullPath(Assembly), readerParameters)) { CustomAttribute xamlcAttr; if (assemblyDefinition.HasCustomAttributes && (xamlcAttr = assemblyDefinition.CustomAttributes.FirstOrDefault( ca => ca.AttributeType.FullName == "Xamarin.Forms.Xaml.XamlCompilationAttribute")) != null) { var options = (XamlCompilationOptions)xamlcAttr.ConstructorArguments[0].Value; if ((options & XamlCompilationOptions.Skip) == XamlCompilationOptions.Skip) { skipassembly = true; } if ((options & XamlCompilationOptions.Compile) == XamlCompilationOptions.Compile) { skipassembly = false; } } foreach (var module in assemblyDefinition.Modules) { var skipmodule = skipassembly; if (module.HasCustomAttributes && (xamlcAttr = module.CustomAttributes.FirstOrDefault( ca => ca.AttributeType.FullName == "Xamarin.Forms.Xaml.XamlCompilationAttribute")) != null) { var options = (XamlCompilationOptions)xamlcAttr.ConstructorArguments[0].Value; if ((options & XamlCompilationOptions.Skip) == XamlCompilationOptions.Skip) { skipmodule = true; } if ((options & XamlCompilationOptions.Compile) == XamlCompilationOptions.Compile) { skipmodule = false; } } LoggingHelper.LogMessage(Low, $"{new string(' ', 2)}Module: {module.Name}"); var resourcesToPrune = new List <EmbeddedResource>(); foreach (var resource in module.Resources.OfType <EmbeddedResource>()) { LoggingHelper.LogMessage(Low, $"{new string(' ', 4)}Resource: {resource.Name}"); string classname; if (!resource.IsXaml(module, out 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 skiptype = skipmodule; if (typeDef.HasCustomAttributes && (xamlcAttr = typeDef.CustomAttributes.FirstOrDefault( ca => ca.AttributeType.FullName == "Xamarin.Forms.Xaml.XamlCompilationAttribute")) != null) { var options = (XamlCompilationOptions)xamlcAttr.ConstructorArguments[0].Value; if ((options & XamlCompilationOptions.Skip) == XamlCompilationOptions.Skip) { skiptype = true; } if ((options & XamlCompilationOptions.Compile) == XamlCompilationOptions.Compile) { skiptype = false; } } if (Type != null) { skiptype = !(Type == classname); } if (skiptype && !ForceCompile) { LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}has XamlCompilationAttribute set to Skip and not Compile... 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; } CustomAttribute xamlFilePathAttr; var xamlFilePath = typeDef.HasCustomAttributes && (xamlFilePathAttr = typeDef.CustomAttributes.FirstOrDefault(ca => ca.AttributeType.FullName == "Xamarin.Forms.Xaml.XamlFilePathAttribute")) != null ? (string)xamlFilePathAttr.ConstructorArguments[0].Value : resource.Name; MethodDefinition initCompRuntime = null; if (!ValidateOnly) { initCompRuntime = typeDef.Methods.FirstOrDefault(md => md.Name == "__InitComponentRuntime"); if (initCompRuntime != null) { LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}__InitComponentRuntime already exists... not creating"); } else { 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); initComp.Body.InitLocals = true; typeDef.Methods.Add(initCompRuntime); LoggingHelper.LogMessage(Low, $"{new string(' ', 8)}done."); } } LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}Parsing Xaml"); var rootnode = ParseXaml(resource.GetResourceStream(), typeDef); if (rootnode == null) { LoggingHelper.LogMessage(Low, $"{new string(' ', 8)}failed."); continue; } LoggingHelper.LogMessage(Low, $"{new string(' ', 8)}done."); hasCompiledXamlResources = true; LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}Replacing {0}.InitializeComponent ()"); Exception e; if (!TryCoreCompile(initComp, initCompRuntime, rootnode, xamlFilePath, out e)) { success = false; LoggingHelper.LogMessage(Low, $"{new string(' ', 8)}failed."); (thrownExceptions = thrownExceptions ?? new List <Exception>()).Add(e); if (e is XamlParseException xpe) { LoggingHelper.LogError(null, null, null, xamlFilePath, xpe.XmlInfo.LineNumber, xpe.XmlInfo.LinePosition, 0, 0, xpe.Message, xpe.HelpLink, xpe.Source); } else if (e is XmlException xe) { LoggingHelper.LogError(null, null, null, xamlFilePath, xe.LineNumber, xe.LinePosition, 0, 0, xe.Message, xe.HelpLink, xe.Source); } else { LoggingHelper.LogError(null, null, null, xamlFilePath, 0, 0, 0, 0, e.Message, e.HelpLink, e.Source); } LoggingHelper.LogMessage(Low, e.StackTrace); continue; } if (Type != null) { InitCompForType = initComp; } LoggingHelper.LogMessage(Low, $"{new string(' ', 8)}done."); if (ValidateOnly) { continue; } if (OptimizeIL) { LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}Optimizing IL"); initComp.Body.Optimize(); LoggingHelper.LogMessage(Low, $"{new string(' ', 8)}done."); } #pragma warning disable 0618 if (OutputGeneratedILAsCode) { LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}Decompiling option has been removed. Use a 3rd party decompiler to admire the beauty of the IL generated"); } #pragma warning restore 0618 resourcesToPrune.Add(resource); } if (hasCompiledXamlResources) { LoggingHelper.LogMessage(Low, $"{new string(' ', 4)}Changing the module MVID"); module.Mvid = Guid.NewGuid(); LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}done."); } if (!KeepXamlResources) { if (resourcesToPrune.Any()) { LoggingHelper.LogMessage(Low, $"{new string(' ', 4)}Removing compiled xaml resources"); } foreach (var resource in resourcesToPrune) { LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}Removing {resource.Name}"); module.Resources.Remove(resource); LoggingHelper.LogMessage(Low, $"{new string(' ', 8)}done."); } } } if (ValidateOnly) { LoggingHelper.LogMessage(Low, $"{new string(' ', 0)}ValidateOnly=True. Skipping writing assembly."); return(success); } if (!hasCompiledXamlResources) { LoggingHelper.LogMessage(Low, $"{new string(' ', 0)}No compiled resources. Skipping writing assembly."); return(success); } LoggingHelper.LogMessage(Low, $"{new string(' ', 0)}Writing the assembly"); try { assemblyDefinition.Write(new WriterParameters { WriteSymbols = debug, }); LoggingHelper.LogMessage(Low, $"{new string(' ', 2)}done."); } catch (Exception e) { LoggingHelper.LogMessage(Low, $"{new string(' ', 2)}failed."); LoggingHelper.LogErrorFromException(e); (thrownExceptions = thrownExceptions ?? new List <Exception>()).Add(e); LoggingHelper.LogMessage(Low, e.StackTrace); success = false; } } } return(success); }
bool TryCoreCompile(MethodDefinition initComp, MethodDefinition initCompRuntime, ILRootNode rootnode, string xamlFilePath, out Exception exception) { try { var body = new MethodBody(initComp); var module = body.Method.Module; body.InitLocals = true; var il = body.GetILProcessor(); var resourcePath = GetPathForType(module, initComp.DeclaringType); il.Emit(Nop); var visitorContext = new ILContext(il, body, module) { DefineDebug = DebugSymbols || (!string.IsNullOrEmpty(DebugType) && DebugType.ToLowerInvariant() != "none"), XamlFilePath = xamlFilePath }; rootnode.Accept(new XamlNodeVisitor((node, parent) => node.Parent = parent), null); rootnode.Accept(new ExpandMarkupsVisitor(visitorContext), null); rootnode.Accept(new PruneIgnoredNodesVisitor(), null); rootnode.Accept(new CreateObjectVisitor(visitorContext), null); rootnode.Accept(new SetNamescopesAndRegisterNamesVisitor(visitorContext), null); rootnode.Accept(new SetFieldVisitor(visitorContext), null); rootnode.Accept(new SetResourcesVisitor(visitorContext), null); rootnode.Accept(new SetPropertiesVisitor(visitorContext, true), null); il.Emit(Ret); initComp.Body = body; exception = null; return(true); } catch (Exception e) { exception = e; return(false); } }
public override bool Execute(out IList <Exception> thrownExceptions) { thrownExceptions = null; Logger = Logger ?? new Logger(null, Verbosity); Logger.LogLine(1, "Preparing debug code for xamlc"); Logger.LogLine(1, "\nAssembly: {0}", Assembly); var resolver = new DefaultAssemblyResolver(); if (!string.IsNullOrEmpty(DependencyPaths)) { foreach (var dep in DependencyPaths.Split(';')) { Logger.LogLine(3, "Adding searchpath {0}", dep); resolver.AddSearchDirectory(dep); } } if (!string.IsNullOrEmpty(ReferencePath)) { var paths = ReferencePath.Replace("//", "/").Split(';'); foreach (var p in paths) { var searchpath = Path.GetDirectoryName(p); Logger.LogLine(3, "Adding searchpath {0}", searchpath); resolver.AddSearchDirectory(searchpath); // LogLine (3, "Referencing {0}", p); // resolver.AddAssembly (p); } } 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) { Logger.LogLine(2, " Module: {0}", module.Name); foreach (var resource in module.Resources.OfType <EmbeddedResource>()) { Logger.LogString(2, " Resource: {0}... ", resource.Name); string classname; if (!resource.IsXaml(module, out classname)) { Logger.LogLine(2, "skipped."); continue; } else { Logger.LogLine(2, ""); } TypeDefinition typeDef = module.GetType(classname); if (typeDef == null) { Logger.LogLine(2, "no type found... skipped."); continue; } var initComp = typeDef.Methods.FirstOrDefault(md => md.Name == "InitializeComponent"); if (initComp == null) { Logger.LogLine(2, "no InitializeComponent found... skipped."); continue; } var initCompRuntime = typeDef.Methods.FirstOrDefault(md => md.Name == "__InitComponentRuntime"); if (initCompRuntime == null) { Logger.LogString(2, " Creating empty {0}.__InitComponentRuntime ...", typeDef.Name); initCompRuntime = new MethodDefinition("__InitComponentRuntime", initComp.Attributes, initComp.ReturnType); initCompRuntime.Body.InitLocals = true; Logger.LogLine(2, "done."); Logger.LogString(2, " Copying body of InitializeComponent to __InitComponentRuntime ...", typeDef.Name); 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); Logger.LogLine(2, "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.Where( md => md.IsConstructor && md.Parameters.Count == 1 && md.Parameters[0].ParameterType == module.TypeSystem.Boolean) .FirstOrDefault(); if (altCtor != null) { Logger.LogString(2, " Replacing body of {0}.{0} (bool {1}) ... ", typeDef.Name, altCtor.Parameters[0].Name); } else { Logger.LogString(2, " Adding {0}.{0} (bool useCompiledXaml) ... ", typeDef.Name); 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); body.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); } Logger.LogLine(2, "done."); } Logger.LogLine(2, ""); } Logger.LogString(1, "Writing the assembly... "); assemblyDefinition.Write(new WriterParameters { WriteSymbols = debug }); } Logger.LogLine(1, "done."); return(true); }
public override bool Execute(out IList <Exception> thrownExceptions) { thrownExceptions = null; LoggingHelper.LogMessage(Normal, $"{new string(' ', 0)}Compiling Xaml, assembly: {Assembly}"); var skipassembly = !DefaultCompile; bool success = true; if (!File.Exists(Assembly)) { LoggingHelper.LogMessage(Normal, $"{new string(' ', 2)}Assembly file not found. Skipping XamlC."); return(true); } using (var fallbackResolver = DefaultAssemblyResolver == null ? new XamlCAssemblyResolver() : null) { var resolver = DefaultAssemblyResolver ?? fallbackResolver; if (resolver is XamlCAssemblyResolver xamlCResolver) { 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}"); xamlCResolver.AddSearchDirectory(searchpath); } } } else { LoggingHelper.LogMessage(Low, $"{new string(' ', 2)}Ignoring dependency and reference paths due to an unsupported resolver"); } var debug = DebugSymbols || (!string.IsNullOrEmpty(DebugType) && DebugType.ToLowerInvariant() != "none"); var readerParameters = new ReaderParameters { AssemblyResolver = resolver, ReadWrite = !ValidateOnly, ReadSymbols = debug && !ValidateOnly, // We don't need symbols for ValidateOnly, since we won't be writing }; using (var assemblyDefinition = AssemblyDefinition.ReadAssembly(IOPath.GetFullPath(Assembly), readerParameters)) { CustomAttribute xamlcAttr; if (assemblyDefinition.HasCustomAttributes && (xamlcAttr = assemblyDefinition.CustomAttributes.FirstOrDefault( ca => ca.AttributeType.FullName == "Microsoft.Maui.Controls.Xaml.XamlCompilationAttribute")) != null) { var options = (XamlCompilationOptions)xamlcAttr.ConstructorArguments[0].Value; if ((options & XamlCompilationOptions.Skip) == XamlCompilationOptions.Skip) { skipassembly = true; } if ((options & XamlCompilationOptions.Compile) == XamlCompilationOptions.Compile) { skipassembly = false; } } foreach (var module in assemblyDefinition.Modules) { var skipmodule = skipassembly; if (module.HasCustomAttributes && (xamlcAttr = module.CustomAttributes.FirstOrDefault( ca => ca.AttributeType.FullName == "Microsoft.Maui.Controls.Xaml.XamlCompilationAttribute")) != null) { var options = (XamlCompilationOptions)xamlcAttr.ConstructorArguments[0].Value; if ((options & XamlCompilationOptions.Skip) == XamlCompilationOptions.Skip) { skipmodule = true; } if ((options & XamlCompilationOptions.Compile) == XamlCompilationOptions.Compile) { skipmodule = false; } } LoggingHelper.LogMessage(Low, $"{new string(' ', 2)}Module: {module.Name}"); var resourcesToPrune = new List <EmbeddedResource>(); foreach (var resource in module.Resources.OfType <EmbeddedResource>()) { LoggingHelper.LogMessage(Low, $"{new string(' ', 4)}Resource: {resource.Name}"); string classname; if (!resource.IsXaml(module, out 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 skiptype = skipmodule; if (typeDef.HasCustomAttributes && (xamlcAttr = typeDef.CustomAttributes.FirstOrDefault( ca => ca.AttributeType.FullName == "Microsoft.Maui.Controls.Xaml.XamlCompilationAttribute")) != null) { var options = (XamlCompilationOptions)xamlcAttr.ConstructorArguments[0].Value; if ((options & XamlCompilationOptions.Skip) == XamlCompilationOptions.Skip) { skiptype = true; } if ((options & XamlCompilationOptions.Compile) == XamlCompilationOptions.Compile) { skiptype = false; } } if (Type != null) { skiptype = !(Type == classname); } if (skiptype && !ForceCompile) { LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}has XamlCompilationAttribute set to Skip and not Compile... 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; } CustomAttribute xamlFilePathAttr; var xamlFilePath = typeDef.HasCustomAttributes && (xamlFilePathAttr = typeDef.CustomAttributes.FirstOrDefault(ca => ca.AttributeType.FullName == "Microsoft.Maui.Controls.Xaml.XamlFilePathAttribute")) != null ? (string)xamlFilePathAttr.ConstructorArguments[0].Value : resource.Name; LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}Parsing Xaml"); var rootnode = ParseXaml(resource.GetResourceStream(), typeDef); if (rootnode == null) { LoggingHelper.LogMessage(Low, $"{new string(' ', 8)}failed."); continue; } LoggingHelper.LogMessage(Low, $"{new string(' ', 8)}done."); hasCompiledXamlResources = true; LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}Replacing {0}.InitializeComponent ()"); Exception e; if (!TryCoreCompile(initComp, rootnode, xamlFilePath, LoggingHelper, out e)) { success = false; LoggingHelper.LogMessage(Low, $"{new string(' ', 8)}failed."); (thrownExceptions = thrownExceptions ?? new List <Exception>()).Add(e); if (e is BuildException be) { LoggingHelper.LogError("XamlC", be.Code.Code, be.HelpLink, xamlFilePath, be.XmlInfo?.LineNumber ?? 0, be.XmlInfo?.LinePosition ?? 0, 0, 0, ErrorMessages.ResourceManager.GetString(be.Code.ErrorMessageKey), be.MessageArgs); } else if (e is XamlParseException xpe) //shouldn't happen anymore { LoggingHelper.LogError("XamlC", null, xpe.HelpLink, xamlFilePath, xpe.XmlInfo.LineNumber, xpe.XmlInfo.LinePosition, 0, 0, xpe.Message); } else if (e is XmlException xe) { LoggingHelper.LogError("XamlC", null, xe.HelpLink, xamlFilePath, xe.LineNumber, xe.LinePosition, 0, 0, xe.Message); } else { LoggingHelper.LogError("XamlC", null, e.HelpLink, xamlFilePath, 0, 0, 0, 0, e.Message); } LoggingHelper.LogMessage(Low, e.StackTrace); continue; } if (Type != null) { InitCompForType = initComp; } LoggingHelper.LogMessage(Low, $"{new string(' ', 8)}done."); if (ValidateOnly) { continue; } if (OptimizeIL) { LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}Optimizing IL"); initComp.Body.Optimize(); LoggingHelper.LogMessage(Low, $"{new string(' ', 8)}done."); } resourcesToPrune.Add(resource); } if (hasCompiledXamlResources) { LoggingHelper.LogMessage(Low, $"{new string(' ', 4)}Changing the module MVID"); module.Mvid = Guid.NewGuid(); LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}done."); } if (!KeepXamlResources) { if (resourcesToPrune.Any()) { LoggingHelper.LogMessage(Low, $"{new string(' ', 4)}Removing compiled xaml resources"); } foreach (var resource in resourcesToPrune) { LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}Removing {resource.Name}"); module.Resources.Remove(resource); LoggingHelper.LogMessage(Low, $"{new string(' ', 8)}done."); } } } if (ValidateOnly) { LoggingHelper.LogMessage(Low, $"{new string(' ', 0)}ValidateOnly=True. Skipping writing assembly."); return(success); } if (!hasCompiledXamlResources) { LoggingHelper.LogMessage(Low, $"{new string(' ', 0)}No compiled resources. Skipping writing assembly."); return(success); } LoggingHelper.LogMessage(Low, $"{new string(' ', 0)}Writing the assembly"); try { assemblyDefinition.Write(new WriterParameters { WriteSymbols = debug, }); LoggingHelper.LogMessage(Low, $"{new string(' ', 2)}done."); } catch (Exception e) { LoggingHelper.LogMessage(Low, $"{new string(' ', 2)}failed."); LoggingHelper.LogErrorFromException(e); (thrownExceptions = thrownExceptions ?? new List <Exception>()).Add(e); LoggingHelper.LogMessage(Low, e.StackTrace); success = false; } } } return(success); }