summaryrefslogtreecommitdiff
path: root/Xamarin.Forms.Build.Tasks
diff options
context:
space:
mode:
Diffstat (limited to 'Xamarin.Forms.Build.Tasks')
-rw-r--r--Xamarin.Forms.Build.Tasks/DebugXamlCTask.cs68
-rw-r--r--Xamarin.Forms.Build.Tasks/ExpandMarkupsVisitor.cs5
-rw-r--r--Xamarin.Forms.Build.Tasks/ILContext.cs5
-rw-r--r--Xamarin.Forms.Build.Tasks/Logger.cs76
-rw-r--r--Xamarin.Forms.Build.Tasks/MethodReferenceExtensions.cs18
-rw-r--r--Xamarin.Forms.Build.Tasks/SetPropertiesVisitor.cs373
-rw-r--r--Xamarin.Forms.Build.Tasks/Xamarin.Forms.Build.Tasks.csproj2
-rw-r--r--Xamarin.Forms.Build.Tasks/XamlCTask.cs262
-rw-r--r--Xamarin.Forms.Build.Tasks/XamlTask.cs96
9 files changed, 646 insertions, 259 deletions
diff --git a/Xamarin.Forms.Build.Tasks/DebugXamlCTask.cs b/Xamarin.Forms.Build.Tasks/DebugXamlCTask.cs
index c4744cc2..a8ef874a 100644
--- a/Xamarin.Forms.Build.Tasks/DebugXamlCTask.cs
+++ b/Xamarin.Forms.Build.Tasks/DebugXamlCTask.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.IO;
using System.Linq;
using Mono.Cecil;
@@ -7,21 +8,20 @@ using Mono.Cecil.Rocks;
namespace Xamarin.Forms.Build.Tasks
{
- public class DebugXamlCTask : XamlCTask
+ public class DebugXamlCTask : XamlTask
{
- public override bool Execute()
+ public override bool Execute(IList<Exception> thrownExceptions)
{
- InMsBuild = true;
- Verbosity = Int32.MaxValue;
- LogLine(1, "Preparing debug code for xamlc");
- LogLine(1, "\nAssembly: {0}", Assembly);
+ 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(';'))
{
- LogLine(3, "Adding searchpath {0}", dep);
+ Logger.LogLine(3, "Adding searchpath {0}", dep);
resolver.AddSearchDirectory(dep);
}
}
@@ -31,7 +31,7 @@ namespace Xamarin.Forms.Build.Tasks
foreach (var p in paths)
{
var searchpath = Path.GetDirectoryName(p);
- LogLine(3, "Adding searchpath {0}", searchpath);
+ Logger.LogLine(3, "Adding searchpath {0}", searchpath);
resolver.AddSearchDirectory(searchpath);
// LogLine (3, "Referencing {0}", p);
// resolver.AddAssembly (p);
@@ -45,58 +45,58 @@ namespace Xamarin.Forms.Build.Tasks
foreach (var module in assemblyDefinition.Modules)
{
- LogLine(2, " Module: {0}", module.Name);
+ Logger.LogLine(2, " Module: {0}", module.Name);
foreach (var resource in module.Resources.OfType<EmbeddedResource>())
{
- LogString(2, " Resource: {0}... ", resource.Name);
+ Logger.LogString(2, " Resource: {0}... ", resource.Name);
string classname;
if (!resource.IsXaml(out classname))
{
- LogLine(2, "skipped.");
+ Logger.LogLine(2, "skipped.");
continue;
}
TypeDefinition typeDef = module.GetType(classname);
if (typeDef == null)
{
- LogLine(2, "no type found... skipped.");
+ Logger.LogLine(2, "no type found... skipped.");
continue;
}
var initComp = typeDef.Methods.FirstOrDefault(md => md.Name == "InitializeComponent");
if (initComp == null)
{
- LogLine(2, "no InitializeComponent found... skipped.");
+ Logger.LogLine(2, "no InitializeComponent found... skipped.");
continue;
}
var initCompRuntime = typeDef.Methods.FirstOrDefault(md => md.Name == "__InitComponentRuntime");
if (initCompRuntime == null) {
- LogLine(2, "no __InitComponentRuntime found... duplicating.");
+ Logger.LogLine(2, "no __InitComponentRuntime found... duplicating.");
initCompRuntime = DuplicateMethodDef(typeDef, initComp, "__InitComponentRuntime");
}
- // 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
+// 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)
- LogString(2, " Replacing body of {0}.{0} (bool {1}) ... ", typeDef.Name, altCtor.Parameters[0].Name);
+ Logger.LogString(2, " Replacing body of {0}.{0} (bool {1}) ... ", typeDef.Name, altCtor.Parameters[0].Name);
else
{
- LogString(2, " Adding {0}.{0} (bool useCompiledXaml) ... ", typeDef.Name);
+ 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);
@@ -127,17 +127,17 @@ namespace Xamarin.Forms.Build.Tasks
altCtor.Body = body;
if (!typeDef.Methods.Contains(altCtor))
typeDef.Methods.Add(altCtor);
- LogLine(2, "done.");
+ Logger.LogLine(2, "done.");
}
- LogLine(2, "");
+ Logger.LogLine(2, "");
}
- LogString(1, "Writing the assembly... ");
+ Logger.LogString(1, "Writing the assembly... ");
assemblyDefinition.Write(Assembly, new WriterParameters
{
WriteSymbols = DebugSymbols
});
- LogLine(1, "done.");
+ Logger.LogLine(1, "done.");
return true;
}
diff --git a/Xamarin.Forms.Build.Tasks/ExpandMarkupsVisitor.cs b/Xamarin.Forms.Build.Tasks/ExpandMarkupsVisitor.cs
index 26e241ab..e647f6c2 100644
--- a/Xamarin.Forms.Build.Tasks/ExpandMarkupsVisitor.cs
+++ b/Xamarin.Forms.Build.Tasks/ExpandMarkupsVisitor.cs
@@ -13,7 +13,8 @@ namespace Xamarin.Forms.Build.Tasks
XmlName.xKey,
XmlName.xTypeArguments,
XmlName.xFactoryMethod,
- XmlName.xName
+ XmlName.xName,
+ XmlName.xDataType
};
public ExpandMarkupsVisitor(ILContext context)
@@ -165,7 +166,7 @@ namespace Xamarin.Forms.Build.Tasks
try
{
type = new XmlType(namespaceuri, name + "Extension", null);
- type.GetTypeReference(contextProvider.Context.Body.Method.Module, null);
+ type.GetTypeReference(contextProvider.Context.Module, null);
}
catch (XamlParseException)
{
diff --git a/Xamarin.Forms.Build.Tasks/ILContext.cs b/Xamarin.Forms.Build.Tasks/ILContext.cs
index 14ade391..8f36227a 100644
--- a/Xamarin.Forms.Build.Tasks/ILContext.cs
+++ b/Xamarin.Forms.Build.Tasks/ILContext.cs
@@ -7,7 +7,7 @@ namespace Xamarin.Forms.Build.Tasks
{
class ILContext
{
- public ILContext(ILProcessor il, MethodBody body, FieldDefinition parentContextValues = null)
+ public ILContext(ILProcessor il, MethodBody body, ModuleDefinition module, FieldDefinition parentContextValues = null)
{
IL = il;
Body = body;
@@ -16,6 +16,7 @@ namespace Xamarin.Forms.Build.Tasks
Scopes = new Dictionary<INode, VariableDefinition>();
TypeExtensions = new Dictionary<INode, TypeReference>();
ParentContextValues = parentContextValues;
+ Module = module;
}
public Dictionary<IValueNode, object> Values { get; private set; }
@@ -33,5 +34,7 @@ namespace Xamarin.Forms.Build.Tasks
public ILProcessor IL { get; private set; }
public MethodBody Body { get; private set; }
+
+ public ModuleDefinition Module { get; private set; }
}
} \ No newline at end of file
diff --git a/Xamarin.Forms.Build.Tasks/Logger.cs b/Xamarin.Forms.Build.Tasks/Logger.cs
new file mode 100644
index 00000000..b706e610
--- /dev/null
+++ b/Xamarin.Forms.Build.Tasks/Logger.cs
@@ -0,0 +1,76 @@
+using System;
+using System.Xml;
+using Microsoft.Build.Utilities;
+using Xamarin.Forms.Xaml;
+
+namespace Xamarin.Forms.Build.Tasks
+{
+ public class Logger {
+ public TaskLoggingHelper Helper { get; }
+ public int Verbosity { get; }
+
+ public Logger(TaskLoggingHelper helper, int verbosity)
+ {
+ Verbosity = verbosity;
+ Helper = helper;
+ }
+
+ string buffer = "";
+
+ public void LogException(string subcategory, string errorCode, string helpKeyword, string file, Exception e)
+ {
+ var xpe = e as XamlParseException;
+ var xe = e as XmlException;
+ if (xpe != null)
+ LogError(subcategory, errorCode, helpKeyword, file, xpe.XmlInfo.LineNumber, xpe.XmlInfo.LinePosition, 0, 0, xpe.Message, xpe.HelpLink, xpe.Source);
+ else if (xe != null)
+ LogError(subcategory, errorCode, helpKeyword, file, xe.LineNumber, xe.LinePosition, 0, 0, xe.Message, xe.HelpLink, xe.Source);
+ else
+ LogError(subcategory, errorCode, helpKeyword, file, 0, 0, 0, 0, e.Message, e.HelpLink, e.Source);
+ }
+
+ public void LogError(string subcategory, string errorCode, string helpKeyword, string file, int lineNumber,
+ int columnNumber, int endLineNumber, int endColumnNumber, string message, params object [] messageArgs)
+ {
+ if (!string.IsNullOrEmpty(buffer))
+ LogLine(-1, null, null);
+ if (Helper != null) {
+ Helper.LogError(subcategory, errorCode, helpKeyword, file, lineNumber, columnNumber, endLineNumber,
+ endColumnNumber, message, messageArgs);
+ } else
+ Console.Error.WriteLine($"{file} ({lineNumber}:{columnNumber}) : {message}");
+ }
+
+ public void LogLine(int level, string format, params object [] arg)
+ {
+ if (!string.IsNullOrEmpty(buffer)) {
+ format = buffer + format;
+ buffer = "";
+ }
+
+ if (level < 0) {
+ if (Helper != null)
+ Helper.LogError(format, arg);
+ else
+ Console.Error.WriteLine(format, arg);
+ } else if (level <= Verbosity) {
+ if (Helper != null)
+ Helper.LogMessage(format, arg);
+ else
+ Console.WriteLine(format, arg);
+ }
+ }
+
+ public void LogString(int level, string format, params object [] arg)
+ {
+ if (level <= 0)
+ Console.Error.Write(format, arg);
+ else if (level <= Verbosity) {
+ if (Helper != null)
+ buffer += String.Format(format, arg);
+ else
+ Console.Write(format, arg);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Build.Tasks/MethodReferenceExtensions.cs b/Xamarin.Forms.Build.Tasks/MethodReferenceExtensions.cs
index 3baa230c..13b4aafc 100644
--- a/Xamarin.Forms.Build.Tasks/MethodReferenceExtensions.cs
+++ b/Xamarin.Forms.Build.Tasks/MethodReferenceExtensions.cs
@@ -40,5 +40,23 @@ namespace Xamarin.Forms.Build.Tasks
self.Parameters[i].ParameterType = module.Import(self.Parameters[i].ParameterType);
}
}
+
+ public static MethodReference MakeGeneric(this MethodReference self, TypeReference declaringType, params TypeReference [] arguments)
+ {
+ var reference = new MethodReference(self.Name, self.ReturnType) {
+ DeclaringType = declaringType,
+ HasThis = self.HasThis,
+ ExplicitThis = self.ExplicitThis,
+ CallingConvention = self.CallingConvention,
+ };
+
+ foreach (var parameter in self.Parameters)
+ reference.Parameters.Add(new ParameterDefinition(parameter.ParameterType));
+
+ foreach (var generic_parameter in self.GenericParameters)
+ reference.GenericParameters.Add(new GenericParameter(generic_parameter.Name, reference));
+
+ return reference;
+ }
}
} \ No newline at end of file
diff --git a/Xamarin.Forms.Build.Tasks/SetPropertiesVisitor.cs b/Xamarin.Forms.Build.Tasks/SetPropertiesVisitor.cs
index f481cbee..8b4bf7ad 100644
--- a/Xamarin.Forms.Build.Tasks/SetPropertiesVisitor.cs
+++ b/Xamarin.Forms.Build.Tasks/SetPropertiesVisitor.cs
@@ -16,6 +16,7 @@ namespace Xamarin.Forms.Build.Tasks
class SetPropertiesVisitor : IXamlNodeVisitor
{
static int dtcount;
+ static int typedBindingCount;
static readonly IList<XmlName> skips = new List<XmlName>
{
@@ -23,7 +24,8 @@ namespace Xamarin.Forms.Build.Tasks
XmlName.xTypeArguments,
XmlName.xArguments,
XmlName.xFactoryMethod,
- XmlName.xName
+ XmlName.xName,
+ XmlName.xDataType
};
public SetPropertiesVisitor(ILContext context, bool stopOnResourceDictionary = false)
@@ -271,6 +273,10 @@ namespace Xamarin.Forms.Build.Tasks
else if (vardefref.VariableDefinition.VariableType.ImplementsGenericInterface("Xamarin.Forms.Xaml.IMarkupExtension`1",
out markupExtension, out genericArguments))
{
+ if (vardefref.VariableDefinition.VariableType.FullName == "Xamarin.Forms.Xaml.BindingExtension")
+ foreach (var instruction in CompileBindingPath(node, context, vardefref.VariableDefinition))
+ yield return instruction;
+
var markExt = markupExtension.Resolve();
var provideValueInfo = markExt.Methods.First(md => md.Name == "ProvideValue");
var provideValue = module.Import(provideValueInfo);
@@ -312,6 +318,359 @@ namespace Xamarin.Forms.Build.Tasks
}
}
+ //Once we get compiled IValueProvider, this will move to the BindingExpression
+ static IEnumerable<Instruction> CompileBindingPath(ElementNode node, ILContext context, VariableDefinition bindingExt)
+ {
+ //TODO implement handlers[]
+ //TODO support casting operators
+
+ INode pathNode;
+ if (!node.Properties.TryGetValue(new XmlName("", "Path"), out pathNode) && node.CollectionItems.Any())
+ pathNode = node.CollectionItems [0];
+ var path = (pathNode as ValueNode)?.Value as string;
+
+ INode dataTypeNode = null;
+ IElementNode n = node;
+ while (n != null) {
+ if (n.Properties.TryGetValue(XmlName.xDataType, out dataTypeNode))
+ break;
+ n = n.Parent as IElementNode;
+ }
+ var dataType = (dataTypeNode as ValueNode)?.Value as string;
+ if (dataType == null)
+ yield break; //throw
+
+ var namespaceuri = dataType.Contains(":") ? node.NamespaceResolver.LookupNamespace(dataType.Split(':') [0].Trim()) : "";
+ var dtXType = new XmlType(namespaceuri, dataType, null);
+
+ var tSourceRef = dtXType.GetTypeReference(context.Module, (IXmlLineInfo)node);
+ if (tSourceRef == null)
+ yield break; //throw
+
+ var properties = ParsePath(path, tSourceRef, node as IXmlLineInfo, context.Module);
+ var tPropertyRef = properties != null && properties.Any() ? properties.Last().Item1.PropertyType : tSourceRef;
+
+ var funcRef = context.Module.Import(context.Module.Import(typeof(Func<,>)).MakeGenericInstanceType(new [] { tSourceRef, tPropertyRef }));
+ var actionRef = context.Module.Import(context.Module.Import(typeof(Action<,>)).MakeGenericInstanceType(new [] { tSourceRef, tPropertyRef }));
+ var funcObjRef = context.Module.Import(context.Module.Import(typeof(Func<,>)).MakeGenericInstanceType(new [] { tSourceRef, context.Module.TypeSystem.Object }));
+ var tupleRef = context.Module.Import(context.Module.Import(typeof(Tuple<,>)).MakeGenericInstanceType(new [] { funcObjRef, context.Module.TypeSystem.String}));
+ var typedBindingRef = context.Module.Import(context.Module.Import(typeof(TypedBinding<,>)).MakeGenericInstanceType(new [] { tSourceRef, tPropertyRef}));
+
+ TypeReference _;
+ var ctorInfo = context.Module.Import(typedBindingRef.Resolve().Methods.FirstOrDefault(md => md.IsConstructor && !md.IsStatic && md.Parameters.Count == 3 ));
+ var ctorinforef = ctorInfo.MakeGeneric(typedBindingRef, funcRef, actionRef, tupleRef);
+ var setTypedBinding = context.Module.Import(typeof(BindingExtension)).GetProperty(pd => pd.Name == "TypedBinding", out _).SetMethod;
+
+ yield return Instruction.Create(OpCodes.Ldloc, bindingExt);
+ foreach (var instruction in CompiledBindingGetGetter(tSourceRef, tPropertyRef, properties, node, context))
+ yield return instruction;
+ foreach (var instruction in CompiledBindingGetSetter(tSourceRef, tPropertyRef, properties, node, context))
+ yield return instruction;
+ foreach (var instruction in CompiledBindingGetHandlers(tSourceRef, tPropertyRef, properties, node, context))
+ yield return instruction;
+ yield return Instruction.Create(OpCodes.Newobj, context.Module.Import(ctorinforef));
+ yield return Instruction.Create(OpCodes.Callvirt, context.Module.Import(setTypedBinding));
+ }
+
+ static IList<Tuple<PropertyDefinition, string>> ParsePath(string path, TypeReference tSourceRef, IXmlLineInfo lineInfo, ModuleDefinition module)
+ {
+ if (string.IsNullOrWhiteSpace(path))
+ return null;
+ path = path.Trim(' ', '.'); //trim leading or trailing dots
+ var parts = path.Split(new [] { '.' }, StringSplitOptions.RemoveEmptyEntries);
+ var properties = new List<Tuple<PropertyDefinition, string>>();
+
+ var previousPartTypeRef = tSourceRef;
+ TypeReference _;
+ foreach (var part in parts) {
+ var p = part;
+ string indexArg = null;
+ var lbIndex = p.IndexOf('[');
+ if (lbIndex != -1) {
+ var rbIndex = p.LastIndexOf(']');
+ if (rbIndex == -1)
+ throw new XamlParseException("Binding: Indexer did not contain closing bracket", lineInfo);
+
+ var argLength = rbIndex - lbIndex - 1;
+ if (argLength == 0)
+ throw new XamlParseException("Binding: Indexer did not contain arguments", lineInfo);
+
+ indexArg = p.Substring(lbIndex + 1, argLength).Trim();
+ if (indexArg.Length == 0)
+ throw new XamlParseException("Binding: Indexer did not contain arguments", lineInfo);
+
+ p = p.Substring(0, lbIndex);
+ p = p.Trim();
+ }
+
+ if (p.Length > 0) {
+ var property = previousPartTypeRef.GetProperty(pd => pd.Name == p && pd.GetMethod != null && pd.GetMethod.IsPublic, out _);
+ properties.Add(new Tuple<PropertyDefinition, string>(property,null));
+ previousPartTypeRef = property.PropertyType;
+ }
+ if (indexArg != null) {
+ var defaultMemberAttribute = previousPartTypeRef.GetCustomAttribute(module.Import(typeof(System.Reflection.DefaultMemberAttribute)));
+ var indexerName = defaultMemberAttribute?.ConstructorArguments?.FirstOrDefault().Value as string ?? "Item";
+ var indexer = previousPartTypeRef.GetProperty(pd => pd.Name == indexerName && pd.GetMethod != null && pd.GetMethod.IsPublic, out _);
+ properties.Add(new Tuple<PropertyDefinition, string>(indexer, indexArg));
+ if (indexer.PropertyType != module.TypeSystem.String && indexer.PropertyType != module.TypeSystem.Int32)
+ throw new XamlParseException($"Binding: Unsupported indexer index type: {indexer.PropertyType.FullName}", lineInfo);
+ previousPartTypeRef = indexer.PropertyType;
+ }
+ }
+ return properties;
+ }
+
+ static IEnumerable<Instruction> CompiledBindingGetGetter(TypeReference tSourceRef, TypeReference tPropertyRef, IList<Tuple<PropertyDefinition, string>> properties, ElementNode node, ILContext context)
+ {
+// .method private static hidebysig default string '<Main>m__0' (class ViewModel vm) cil managed
+// {
+// .custom instance void class [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::'.ctor'() = (01 00 00 00 ) // ...
+//
+// IL_0000: ldarg.0
+// IL_0001: callvirt instance class ViewModel class ViewModel::get_Model()
+// IL_0006: callvirt instance string class ViewModel::get_Text()
+// IL_0006: ret
+// }
+
+ var module = context.Module;
+ var compilerGeneratedCtor = module.Import(typeof(System.Runtime.CompilerServices.CompilerGeneratedAttribute)).GetMethods(md => md.IsConstructor, module).First().Item1;
+ var getter = new MethodDefinition($"<{context.Body.Method.Name}>typedBindingsM__{typedBindingCount++}",
+ MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Static,
+ tPropertyRef) {
+ Parameters = {
+ new ParameterDefinition(tSourceRef)
+ },
+ CustomAttributes = {
+ new CustomAttribute (context.Module.Import(compilerGeneratedCtor))
+ }
+ };
+ var il = getter.Body.GetILProcessor();
+
+ il.Emit(OpCodes.Ldarg_0);
+ if (properties != null && properties.Count != 0) {
+ foreach (var propTuple in properties) {
+ var property = propTuple.Item1;
+ var indexerArg = propTuple.Item2;
+ if (indexerArg != null) {
+ if (property.GetMethod.Parameters [0].ParameterType == module.TypeSystem.String)
+ il.Emit(OpCodes.Ldstr, indexerArg);
+ else if (property.GetMethod.Parameters [0].ParameterType == module.TypeSystem.Int32) {
+ int index;
+ if (!int.TryParse(indexerArg, out index))
+ throw new XamlParseException($"Binding: {indexerArg} could not be parsed as an index for a {property.Name}", node as IXmlLineInfo);
+ il.Emit(OpCodes.Ldc_I4, index);
+ }
+ }
+ il.Emit(OpCodes.Callvirt, module.Import(property.GetMethod));
+ }
+ }
+
+ il.Emit(OpCodes.Ret);
+
+ context.Body.Method.DeclaringType.Methods.Add(getter);
+
+ var funcRef = module.Import(typeof(Func<,>));
+ funcRef = module.Import(funcRef.MakeGenericInstanceType(new [] { tSourceRef, tPropertyRef }));
+ var funcCtor = module.Import(funcRef.Resolve().GetConstructors().First());
+ funcCtor = funcCtor.MakeGeneric(funcRef, new [] { tSourceRef, tPropertyRef });
+
+// IL_0007: ldnull
+// IL_0008: ldftn string class Test::'<Main>m__0'(class ViewModel)
+// IL_000e: newobj instance void class [mscorlib]System.Func`2<class ViewModel, string>::'.ctor'(object, native int)
+
+ yield return Instruction.Create(OpCodes.Ldnull);
+ yield return Instruction.Create(OpCodes.Ldftn, getter);
+ yield return Instruction.Create(OpCodes.Newobj, module.Import(funcCtor));
+ }
+
+ static IEnumerable<Instruction> CompiledBindingGetSetter(TypeReference tSourceRef, TypeReference tPropertyRef, IList<Tuple<PropertyDefinition, string>> properties, ElementNode node, ILContext context)
+ {
+ if (properties == null || properties.Count == 0) {
+ yield return Instruction.Create(OpCodes.Ldnull);
+ yield break;
+ }
+
+ // .method private static hidebysig default void '<Main>m__1' (class ViewModel vm, string s) cil managed
+ // {
+ // .custom instance void class [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::'.ctor'() = (01 00 00 00 ) // ....
+ //
+ // IL_0000: ldarg.0
+ // IL_0001: callvirt instance class ViewModel class ViewModel::get_Model()
+ // IL_0006: ldarg.1
+ // IL_0007: callvirt instance void class ViewModel::set_Text(string)
+ // IL_000c: ret
+ // }
+
+ var module = context.Module;
+ var compilerGeneratedCtor = module.Import(typeof(System.Runtime.CompilerServices.CompilerGeneratedAttribute)).GetMethods(md => md.IsConstructor, module).First().Item1;
+ var setter = new MethodDefinition($"<{context.Body.Method.Name}>typedBindingsM__{typedBindingCount++}",
+ MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Static,
+ module.TypeSystem.Void) {
+ Parameters = {
+ new ParameterDefinition(tSourceRef),
+ new ParameterDefinition(tPropertyRef)
+ },
+ CustomAttributes = {
+ new CustomAttribute (module.Import(compilerGeneratedCtor))
+ }
+ };
+
+ var il = setter.Body.GetILProcessor();
+ var lastProperty = properties.LastOrDefault();
+ var setterRef = lastProperty?.Item1.SetMethod;
+ if (setterRef == null) {
+ yield return Instruction.Create(OpCodes.Ldnull); //throw or not ?
+ yield break;
+ }
+
+ il.Emit(OpCodes.Ldarg_0);
+ for (int i = 0; i < properties.Count - 1; i++) {
+ var property = properties[i].Item1;
+ var indexerArg = properties[i].Item2;
+ if (indexerArg != null) {
+ if (property.GetMethod.Parameters [0].ParameterType == module.TypeSystem.String)
+ il.Emit(OpCodes.Ldstr, indexerArg);
+ else if (property.GetMethod.Parameters [0].ParameterType == module.TypeSystem.Int32) {
+ int index;
+ if (!int.TryParse(indexerArg, out index))
+ throw new XamlParseException($"Binding: {indexerArg} could not be parsed as an index for a {property.Name}", node as IXmlLineInfo);
+ il.Emit(OpCodes.Ldc_I4, index);
+ }
+ }
+ il.Emit(OpCodes.Callvirt, module.Import(property.GetMethod));
+ }
+
+ var indexer = properties.Last().Item2;
+ if (indexer != null) {
+ if (lastProperty.Item1.GetMethod.Parameters [0].ParameterType == module.TypeSystem.String)
+ il.Emit(OpCodes.Ldstr, indexer);
+ else if (lastProperty.Item1.GetMethod.Parameters [0].ParameterType == module.TypeSystem.Int32) {
+ int index;
+ if (!int.TryParse(indexer, out index))
+ throw new XamlParseException($"Binding: {indexer} could not be parsed as an index for a {lastProperty.Item1.Name}", node as IXmlLineInfo);
+ il.Emit(OpCodes.Ldc_I4, index);
+ }
+ }
+ il.Emit(OpCodes.Ldarg_1);
+ il.Emit(OpCodes.Callvirt, module.Import(setterRef));
+ il.Emit(OpCodes.Ret);
+
+ context.Body.Method.DeclaringType.Methods.Add(setter);
+
+ var actionRef = module.Import(typeof(Action<,>));
+ actionRef = module.Import(actionRef.MakeGenericInstanceType(new [] { tSourceRef, tPropertyRef }));
+ var actionCtor = module.Import(actionRef.Resolve().GetConstructors().First());
+ actionCtor = actionCtor.MakeGeneric(actionRef, new [] { tSourceRef, tPropertyRef });
+
+// IL_0024: ldnull
+// IL_0025: ldftn void class Test::'<Main>m__1'(class ViewModel, string)
+// IL_002b: newobj instance void class [mscorlib]System.Action`2<class ViewModel, string>::'.ctor'(object, native int)
+ yield return Instruction.Create(OpCodes.Ldnull);
+ yield return Instruction.Create(OpCodes.Ldftn, setter);
+ yield return Instruction.Create(OpCodes.Newobj, module.Import(actionCtor));
+ }
+
+ static IEnumerable<Instruction> CompiledBindingGetHandlers(TypeReference tSourceRef, TypeReference tPropertyRef, IList<Tuple<PropertyDefinition, string>> properties, ElementNode node, ILContext context)
+ {
+// .method private static hidebysig default object '<Main>m__2'(class ViewModel vm) cil managed {
+// .custom instance void class [mscorlib] System.Runtime.CompilerServices.CompilerGeneratedAttribute::'.ctor'() = (01 00 00 00 ) // ....
+// IL_0000: ldarg.0
+// IL_0001: ret
+// } // end of method Test::<Main>m__2
+
+// .method private static hidebysig default object '<Main>m__3' (class ViewModel vm) cil managed {
+// .custom instance void class [mscorlib] System.Runtime.CompilerServices.CompilerGeneratedAttribute::'.ctor'() = (01 00 00 00 ) // ....
+// IL_0000: ldarg.0
+// IL_0001: callvirt instance class ViewModel class ViewModel::get_Model()
+// IL_0006: ret
+// }
+
+ var module = context.Module;
+ var compilerGeneratedCtor = module.Import(typeof(System.Runtime.CompilerServices.CompilerGeneratedAttribute)).GetMethods(md => md.IsConstructor, module).First().Item1;
+
+ var partGetters = new List<MethodDefinition>();
+ if (properties == null || properties.Count == 0) {
+ yield return Instruction.Create(OpCodes.Ldnull);
+ yield break;
+ }
+
+ for (int i = 0; i < properties.Count; i++) {
+ var tuple = properties [i];
+ var partGetter = new MethodDefinition($"<{context.Body.Method.Name}>typedBindingsM__{typedBindingCount++}", MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Static, tPropertyRef) {
+ Parameters = {
+ new ParameterDefinition(tSourceRef)
+ },
+ CustomAttributes = {
+ new CustomAttribute (context.Module.Import(compilerGeneratedCtor))
+ }
+ };
+ var il = partGetter.Body.GetILProcessor();
+ il.Emit(OpCodes.Ldarg_0);
+ for (int j = 0; j < i; j++) {
+ var propTuple = properties [j];
+ var property = propTuple.Item1;
+ var indexerArg = propTuple.Item2;
+ if (indexerArg != null) {
+ if (property.GetMethod.Parameters [0].ParameterType == module.TypeSystem.String)
+ il.Emit(OpCodes.Ldstr, indexerArg);
+ else if (property.GetMethod.Parameters [0].ParameterType == module.TypeSystem.Int32) {
+ int index;
+ if (!int.TryParse(indexerArg, out index))
+ throw new XamlParseException($"Binding: {indexerArg} could not be parsed as an index for a {property.Name}", node as IXmlLineInfo);
+ il.Emit(OpCodes.Ldc_I4, index);
+ }
+ }
+ il.Emit(OpCodes.Callvirt, module.Import(property.GetMethod));
+ }
+ il.Emit(OpCodes.Ret);
+ context.Body.Method.DeclaringType.Methods.Add(partGetter);
+ partGetters.Add(partGetter);
+ }
+
+ var funcObjRef = context.Module.Import(module.Import(typeof(Func<,>)).MakeGenericInstanceType(new [] { tSourceRef, module.TypeSystem.Object }));
+ var tupleRef = context.Module.Import(module.Import(typeof(Tuple<,>)).MakeGenericInstanceType(new [] { funcObjRef, module.TypeSystem.String }));
+ var funcCtor = module.Import(funcObjRef.Resolve().GetConstructors().First());
+ funcCtor = funcCtor.MakeGeneric(funcObjRef, new [] { tSourceRef, module.TypeSystem.Object });
+ var tupleCtor = module.Import(tupleRef.Resolve().GetConstructors().First());
+ tupleCtor = tupleCtor.MakeGeneric(tupleRef, new [] { funcObjRef, module.TypeSystem.String});
+
+// IL_003a: ldc.i4.2
+// IL_003b: newarr class [mscorlib] System.Tuple`2<class [mscorlib]System.Func`2<class ViewModel,object>,string>
+
+// IL_0040: dup
+// IL_0041: ldc.i4.0
+// IL_0049: ldnull
+// IL_004a: ldftn object class Test::'<Main>m__2'(class ViewModel)
+// IL_0050: newobj instance void class [mscorlib]System.Func`2<class ViewModel, object>::'.ctor'(object, native int)
+// IL_005f: ldstr "Model"
+// IL_0064: newobj instance void class [mscorlib]System.Tuple`2<class [mscorlib]System.Func`2<class ViewModel, object>, string>::'.ctor'(!0, !1)
+// IL_0069: stelem.ref
+
+// IL_006a: dup
+// IL_006b: ldc.i4.1
+// IL_0073: ldnull
+// IL_0074: ldftn object class Test::'<Main>m__3'(class ViewModel)
+// IL_007a: newobj instance void class [mscorlib]System.Func`2<class ViewModel, object>::'.ctor'(object, native int)
+// IL_0089: ldstr "Text"
+// IL_008e: newobj instance void class [mscorlib]System.Tuple`2<class [mscorlib]System.Func`2<class ViewModel, object>, string>::'.ctor'(!0, !1)
+// IL_0093: stelem.ref
+
+ yield return Instruction.Create(OpCodes.Ldc_I4, properties.Count);
+ yield return Instruction.Create(OpCodes.Newarr, tupleRef);
+
+ for (var i = 0; i < properties.Count; i++) {
+ yield return Instruction.Create(OpCodes.Dup);
+ yield return Instruction.Create(OpCodes.Ldc_I4, i);
+ yield return Instruction.Create(OpCodes.Ldnull);
+ yield return Instruction.Create(OpCodes.Ldftn, partGetters [i]);
+ yield return Instruction.Create(OpCodes.Newobj, module.Import(funcCtor));
+ yield return Instruction.Create(OpCodes.Ldstr, properties [i].Item1.Name);
+ yield return Instruction.Create(OpCodes.Newobj, module.Import(tupleCtor));
+ yield return Instruction.Create(OpCodes.Stelem_Ref);
+ }
+ }
+
public static IEnumerable<Instruction> SetPropertyValue(VariableDefinition parent, XmlName propertyName, INode valueNode, ILContext context, IXmlLineInfo iXmlLineInfo)
{
var module = context.Body.Method.Module;
@@ -685,7 +1044,9 @@ namespace Xamarin.Forms.Build.Tasks
// .class nested private auto ansi sealed beforefieldinit '<Main>c__AnonStorey0'
// extends [mscorlib]System.Object
- var module = parentContext.Body.Method.Module;
+
+ var module = parentContext.Module;
+ var compilerGeneratedCtor = module.Import(typeof(System.Runtime.CompilerServices.CompilerGeneratedAttribute)).GetMethods(md => md.IsConstructor, module).First().Item1;
var anonType = new TypeDefinition(
null,
"<" + parentContext.Body.Method.Name + ">_anonXamlCDataTemplate_" + dtcount++,
@@ -693,7 +1054,10 @@ namespace Xamarin.Forms.Build.Tasks
TypeAttributes.Sealed |
TypeAttributes.NestedPrivate)
{
- BaseType = module.TypeSystem.Object
+ BaseType = module.TypeSystem.Object,
+ CustomAttributes = {
+ new CustomAttribute (module.Import(compilerGeneratedCtor))
+ }
};
parentContext.Body.Method.DeclaringType.NestedTypes.Add(anonType);
@@ -721,7 +1085,7 @@ namespace Xamarin.Forms.Build.Tasks
//Fill the loadTemplate Body
var templateIl = loadTemplate.Body.GetILProcessor();
templateIl.Emit(OpCodes.Nop);
- var templateContext = new ILContext(templateIl, loadTemplate.Body, parentValues)
+ var templateContext = new ILContext(templateIl, loadTemplate.Body, module, parentValues)
{
Root = root
};
@@ -730,6 +1094,7 @@ namespace Xamarin.Forms.Build.Tasks
node.Accept(new SetFieldVisitor(templateContext), null);
node.Accept(new SetResourcesVisitor(templateContext), null);
node.Accept(new SetPropertiesVisitor(templateContext), null);
+
templateIl.Emit(OpCodes.Ldloc, templateContext.Variables[node]);
templateIl.Emit(OpCodes.Ret);
diff --git a/Xamarin.Forms.Build.Tasks/Xamarin.Forms.Build.Tasks.csproj b/Xamarin.Forms.Build.Tasks/Xamarin.Forms.Build.Tasks.csproj
index b7250038..6c820c67 100644
--- a/Xamarin.Forms.Build.Tasks/Xamarin.Forms.Build.Tasks.csproj
+++ b/Xamarin.Forms.Build.Tasks/Xamarin.Forms.Build.Tasks.csproj
@@ -98,6 +98,8 @@
<Compile Include="CompiledConverters\ICompiledTypeConverter.cs" />
<Compile Include="CompiledConverters\LayoutOptionsConverter.cs" />
<Compile Include="CompiledConverters\RectangleTypeConverter.cs" />
+ <Compile Include="Logger.cs" />
+ <Compile Include="XamlTask.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<Target Name="AfterBuild">
diff --git a/Xamarin.Forms.Build.Tasks/XamlCTask.cs b/Xamarin.Forms.Build.Tasks/XamlCTask.cs
index 206fc452..689a6a59 100644
--- a/Xamarin.Forms.Build.Tasks/XamlCTask.cs
+++ b/Xamarin.Forms.Build.Tasks/XamlCTask.cs
@@ -1,13 +1,9 @@
using System;
using System.Collections.Generic;
-using System.Diagnostics;
using System.IO;
using System.Linq;
-using System.Xml;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Ast;
-using Microsoft.Build.Framework;
-using Microsoft.Build.Utilities;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Cecil.Rocks;
@@ -15,134 +11,31 @@ using Xamarin.Forms.Xaml;
namespace Xamarin.Forms.Build.Tasks
{
- public class XamlCTask : AppDomainIsolatedTask
+ public class XamlCTask : XamlTask
{
- string buffer = "";
-
bool hasCompiledXamlResources;
-
- [Required]
- public string Assembly { get; set; }
-
- public string DependencyPaths { get; set; }
-
- public string ReferencePath { get; set; }
-
- public int Verbosity { get; set; }
-
public bool KeepXamlResources { get; set; }
-
public bool OptimizeIL { get; set; }
-
- public bool DebugSymbols { get; set; }
-
public bool OutputGeneratedILAsCode { get; set; }
- protected bool InMsBuild { get; set; }
-
internal string Type { get; set; }
- public override bool Execute()
- {
- InMsBuild = true;
- return Compile();
- }
-
- protected void LogException(string subcategory, string errorCode, string helpKeyword, string file, Exception e)
- {
- var xpe = e as XamlParseException;
- var xe = e as XmlException;
- if (xpe != null)
- LogError(subcategory, errorCode, helpKeyword, file, xpe.XmlInfo.LineNumber, xpe.XmlInfo.LinePosition, 0, 0, xpe.Message, xpe.HelpLink, xpe.Source);
- else if (xe != null)
- LogError(subcategory, errorCode, helpKeyword, file, xe.LineNumber, xe.LinePosition, 0, 0, xe.Message, xe.HelpLink, xe.Source);
- else
- LogError(subcategory, errorCode, helpKeyword, file, 0, 0, 0, 0, e.Message, e.HelpLink, e.Source);
- }
-
- protected void LogError(string subcategory, string errorCode, string helpKeyword, string file, int lineNumber,
- int columnNumber, int endLineNumber, int endColumnNumber, string message, params object[] messageArgs)
- {
- if (!string.IsNullOrEmpty(buffer))
- LogLine(-1, null, null);
- if (InMsBuild)
- {
- base.Log.LogError(subcategory, errorCode, helpKeyword, file, lineNumber, columnNumber, endLineNumber,
- endColumnNumber, message, messageArgs);
- }
- else
- Console.Error.WriteLine("{0} ({1}:{2}) : {3}", file, lineNumber, columnNumber, message);
- }
-
- protected void LogLine(int level, string format, params object[] arg)
- {
- if (!string.IsNullOrEmpty(buffer))
- {
- format = buffer + format;
- buffer = "";
- }
-
- if (level < 0)
- {
- if (InMsBuild)
- base.Log.LogError(format, arg);
- else
- Console.Error.WriteLine(format, arg);
- }
- else if (level <= Verbosity)
- {
- if (InMsBuild)
- base.Log.LogMessage(format, arg);
- else
- Console.WriteLine(format, arg);
- }
- }
-
- protected void LogString(int level, string format, params object[] arg)
- {
- if (level <= 0)
- Console.Error.Write(format, arg);
- else if (level <= Verbosity)
- {
- if (InMsBuild)
- buffer += String.Format(format, arg);
- else
- Console.Write(format, arg);
- }
- }
-
- public static void Compile(string assemblyFileName, int verbosity = 0, bool keep = false, bool optimize = false,
- string dependencyPaths = null, string referencePath = null, bool outputCSharp = false)
+ public override bool Execute(IList<Exception> thrownExceptions)
{
- var xamlc = new XamlCTask
- {
- Assembly = assemblyFileName,
- Verbosity = verbosity,
- KeepXamlResources = keep,
- OptimizeIL = optimize,
- InMsBuild = false,
- DependencyPaths = dependencyPaths,
- ReferencePath = referencePath,
- OutputGeneratedILAsCode = outputCSharp,
- };
- xamlc.Compile();
- }
-
- public bool Compile(IList<Exception> thrownExceptions = null)
- {
- LogLine(1, "Compiling Xaml");
- LogLine(1, "\nAssembly: {0}", Assembly);
+ Logger = Logger ?? new Logger(null, Verbosity);
+ Logger.LogLine(1, "Compiling Xaml");
+ Logger.LogLine(1, "\nAssembly: {0}", Assembly);
if (!string.IsNullOrEmpty(DependencyPaths))
- LogLine(1, "DependencyPaths: \t{0}", DependencyPaths);
+ Logger.LogLine(1, "DependencyPaths: \t{0}", DependencyPaths);
if (!string.IsNullOrEmpty(ReferencePath))
- LogLine(1, "ReferencePath: \t{0}", ReferencePath.Replace("//", "/"));
- LogLine(3, "DebugSymbols:\"{0}\"", DebugSymbols);
+ Logger.LogLine(1, "ReferencePath: \t{0}", ReferencePath.Replace("//", "/"));
+ Logger.LogLine(3, "DebugSymbols:\"{0}\"", DebugSymbols);
var skipassembly = true; //change this to false to enable XamlC by default
bool success = true;
if (!File.Exists(Assembly))
{
- LogLine(1, "Assembly file not found. Skipping XamlC.");
+ Logger.LogLine(1, "Assembly file not found. Skipping XamlC.");
return true;
}
@@ -151,7 +44,7 @@ namespace Xamarin.Forms.Build.Tasks
{
foreach (var dep in DependencyPaths.Split(';'))
{
- LogLine(3, "Adding searchpath {0}", dep);
+ Logger.LogLine(3, "Adding searchpath {0}", dep);
resolver.AddSearchDirectory(dep);
}
}
@@ -162,7 +55,7 @@ namespace Xamarin.Forms.Build.Tasks
foreach (var p in paths)
{
var searchpath = Path.GetDirectoryName(p);
- LogLine(3, "Adding searchpath {0}", searchpath);
+ Logger.LogLine(3, "Adding searchpath {0}", searchpath);
resolver.AddSearchDirectory(searchpath);
}
}
@@ -201,21 +94,21 @@ namespace Xamarin.Forms.Build.Tasks
skipmodule = false;
}
- LogLine(2, " Module: {0}", module.Name);
+ Logger.LogLine(2, " Module: {0}", module.Name);
var resourcesToPrune = new List<EmbeddedResource>();
foreach (var resource in module.Resources.OfType<EmbeddedResource>())
{
- LogString(2, " Resource: {0}... ", resource.Name);
+ Logger.LogString(2, " Resource: {0}... ", resource.Name);
string classname;
if (!resource.IsXaml(out classname))
{
- LogLine(2, "skipped.");
+ Logger.LogLine(2, "skipped.");
continue;
}
TypeDefinition typeDef = module.GetType(classname);
if (typeDef == null)
{
- LogLine(2, "no type found... skipped.");
+ Logger.LogLine(2, "no type found... skipped.");
continue;
}
var skiptype = skipmodule;
@@ -236,61 +129,61 @@ namespace Xamarin.Forms.Build.Tasks
if (skiptype)
{
- LogLine(2, "Has XamlCompilationAttribute set to Skip and not Compile... skipped");
+ 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)
{
- LogLine(2, "no InitializeComponent found... skipped.");
+ Logger.LogLine(2, "no InitializeComponent found... skipped.");
continue;
}
- LogLine(2, "");
+ Logger.LogLine(2, "");
var initCompRuntime = typeDef.Methods.FirstOrDefault(md => md.Name == "__InitComponentRuntime");
if (initCompRuntime != null)
- LogLine(2, " __InitComponentRuntime already exists... not duplicating");
+ Logger.LogLine(2, " __InitComponentRuntime already exists... not duplicating");
else {
- LogString(2, " Duplicating {0}.InitializeComponent () into {0}.__InitComponentRuntime ... ", typeDef.Name);
+ Logger.LogString(2, " Duplicating {0}.InitializeComponent () into {0}.__InitComponentRuntime ... ", typeDef.Name);
initCompRuntime = DuplicateMethodDef(typeDef, initComp, "__InitComponentRuntime");
- LogLine(2, "done.");
+ Logger.LogLine(2, "done.");
}
- LogString(2, " Parsing Xaml... ");
+ Logger.LogString(2, " Parsing Xaml... ");
var rootnode = ParseXaml(resource.GetResourceStream(), typeDef);
if (rootnode == null)
{
- LogLine(2, "failed.");
+ Logger.LogLine(2, "failed.");
continue;
}
- LogLine(2, "done.");
+ Logger.LogLine(2, "done.");
hasCompiledXamlResources = true;
- LogString(2, " Replacing {0}.InitializeComponent ()... ", typeDef.Name);
+ Logger.LogString(2, " Replacing {0}.InitializeComponent ()... ", typeDef.Name);
Exception e;
if (!TryCoreCompile(initComp, initCompRuntime, rootnode, out e)) {
success = false;
- LogLine(2, "failed.");
+ Logger.LogLine(2, "failed.");
thrownExceptions?.Add(e);
- LogException(null, null, null, resource.Name, e);
- LogLine(4, e.StackTrace);
+ Logger.LogException(null, null, null, resource.Name, e);
+ Logger.LogLine(4, e.StackTrace);
continue;
}
- LogLine(2, "done.");
+ Logger.LogLine(2, "done.");
if (OptimizeIL)
{
- LogString(2, " Optimizing IL... ");
+ Logger.LogString(2, " Optimizing IL... ");
initComp.Body.OptimizeMacros();
- LogLine(2, "done");
+ Logger.LogLine(2, "done");
}
if (OutputGeneratedILAsCode)
{
var filepath = Path.Combine(Path.GetDirectoryName(Assembly), typeDef.FullName + ".decompiled.cs");
- LogString(2, " Decompiling {0} into {1}...", typeDef.FullName, filepath);
+ Logger.LogString(2, " Decompiling {0} into {1}...", typeDef.FullName, filepath);
var decompilerContext = new DecompilerContext(module);
using (var writer = new StreamWriter(filepath))
{
@@ -301,46 +194,46 @@ namespace Xamarin.Forms.Build.Tasks
codeDomBuilder.GenerateCode(output);
}
- LogLine(2, "done");
+ Logger.LogLine(2, "done");
}
resourcesToPrune.Add(resource);
}
if (!KeepXamlResources)
{
if (resourcesToPrune.Any())
- LogLine(2, " Removing compiled xaml resources");
+ Logger.LogLine(2, " Removing compiled xaml resources");
foreach (var resource in resourcesToPrune)
{
- LogString(2, " Removing {0}... ", resource.Name);
+ Logger.LogString(2, " Removing {0}... ", resource.Name);
module.Resources.Remove(resource);
- LogLine(2, "done");
+ Logger.LogLine(2, "done");
}
}
- LogLine(2, "");
+ Logger.LogLine(2, "");
}
if (!hasCompiledXamlResources)
{
- LogLine(1, "No compiled resources. Skipping writing assembly.");
+ Logger.LogLine(1, "No compiled resources. Skipping writing assembly.");
return success;
}
- LogString(1, "Writing the assembly... ");
+ Logger.LogString(1, "Writing the assembly... ");
try
{
assemblyDefinition.Write(Assembly, new WriterParameters
{
WriteSymbols = DebugSymbols
});
- LogLine(1, "done.");
+ Logger.LogLine(1, "done.");
}
catch (Exception e)
{
- LogLine(1, "failed.");
- LogException(null, null, null, null, e);
+ Logger.LogLine(1, "failed.");
+ Logger.LogException(null, null, null, null, e);
thrownExceptions?.Add(e);
- LogLine(4, e.StackTrace);
+ Logger.LogLine(4, e.StackTrace);
success = false;
}
@@ -393,7 +286,7 @@ namespace Xamarin.Forms.Build.Tasks
il.Append(nop);
}
- var visitorContext = new ILContext(il, body);
+ var visitorContext = new ILContext(il, body, body.Method.Module);
rootnode.Accept(new XamlNodeVisitor((node, parent) => node.Parent = parent), null);
rootnode.Accept(new ExpandMarkupsVisitor(visitorContext), null);
@@ -413,72 +306,5 @@ namespace Xamarin.Forms.Build.Tasks
return false;
}
}
-
- protected static MethodDefinition DuplicateMethodDef(TypeDefinition typeDef, MethodDefinition methodDef, string newName)
- {
- var dup = new MethodDefinition(newName, methodDef.Attributes, methodDef.ReturnType);
- dup.Body = methodDef.Body;
- typeDef.Methods.Add(dup);
- return dup;
- }
-
- static ILRootNode ParseXaml(Stream stream, TypeReference typeReference)
- {
- ILRootNode rootnode = null;
- using (var reader = XmlReader.Create(stream))
- {
- while (reader.Read())
- {
- //Skip until element
- if (reader.NodeType == XmlNodeType.Whitespace)
- continue;
- if (reader.NodeType != XmlNodeType.Element)
- {
- Debug.WriteLine("Unhandled node {0} {1} {2}", reader.NodeType, reader.Name, reader.Value);
- continue;
- }
-
- XamlParser.ParseXaml(
- rootnode = new ILRootNode(new XmlType(reader.NamespaceURI, reader.Name, null), typeReference, reader as IXmlNamespaceResolver), reader);
- break;
- }
- }
- return rootnode;
- }
- }
-
- static class CecilExtensions
- {
- public static bool IsXaml(this EmbeddedResource resource, out string classname)
- {
- classname = null;
- if (!resource.Name.EndsWith(".xaml", StringComparison.InvariantCulture))
- return false;
-
- using (var resourceStream = resource.GetResourceStream())
- {
- var xmlDoc = new XmlDocument();
- xmlDoc.Load(resourceStream);
-
- var nsmgr = new XmlNamespaceManager(xmlDoc.NameTable);
-
- var root = xmlDoc.SelectSingleNode("/*", nsmgr);
- if (root == null)
- {
- // Log (2, "No root found... ");
- return false;
- }
-
- var rootClass = root.Attributes["Class", "http://schemas.microsoft.com/winfx/2006/xaml"] ??
- root.Attributes["Class", "http://schemas.microsoft.com/winfx/2009/xaml"];
- if (rootClass == null)
- {
- // Log (2, "no x:Class found... ");
- return false;
- }
- classname = rootClass.Value;
- return true;
- }
- }
}
} \ No newline at end of file
diff --git a/Xamarin.Forms.Build.Tasks/XamlTask.cs b/Xamarin.Forms.Build.Tasks/XamlTask.cs
new file mode 100644
index 00000000..d4864ac3
--- /dev/null
+++ b/Xamarin.Forms.Build.Tasks/XamlTask.cs
@@ -0,0 +1,96 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Xml;
+
+using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
+
+using Mono.Cecil;
+
+using Xamarin.Forms.Xaml;
+
+namespace Xamarin.Forms.Build.Tasks
+{
+ public abstract class XamlTask : AppDomainIsolatedTask
+ {
+ [Required]
+ public string Assembly { get; set; }
+ public string DependencyPaths { get; set; }
+ public string ReferencePath { get; set; }
+ public int Verbosity { get; set; }
+ public bool DebugSymbols { get; set; }
+
+ internal XamlTask()
+ {
+ }
+
+ protected Logger Logger { get; set; }
+
+ public override bool Execute()
+ {
+ Logger = new Logger(Log, Verbosity);
+ return Execute(null);
+ }
+
+ public abstract bool Execute(IList<Exception> thrownExceptions);
+
+ protected static MethodDefinition DuplicateMethodDef(TypeDefinition typeDef, MethodDefinition methodDef, string newName)
+ {
+ var dup = new MethodDefinition(newName, methodDef.Attributes, methodDef.ReturnType);
+ dup.Body = methodDef.Body;
+ typeDef.Methods.Add(dup);
+ return dup;
+ }
+
+ internal static ILRootNode ParseXaml(Stream stream, TypeReference typeReference)
+ {
+ ILRootNode rootnode = null;
+ using (var reader = XmlReader.Create(stream)) {
+ while (reader.Read()) {
+ //Skip until element
+ if (reader.NodeType == XmlNodeType.Whitespace)
+ continue;
+ if (reader.NodeType != XmlNodeType.Element) {
+ Debug.WriteLine("Unhandled node {0} {1} {2}", reader.NodeType, reader.Name, reader.Value);
+ continue;
+ }
+
+ XamlParser.ParseXaml(
+ rootnode = new ILRootNode(new XmlType(reader.NamespaceURI, reader.Name, null), typeReference, reader as IXmlNamespaceResolver), reader);
+ break;
+ }
+ }
+ return rootnode;
+ }
+ }
+
+ static class CecilExtensions
+ {
+ public static bool IsXaml(this EmbeddedResource resource, out string classname)
+ {
+ classname = null;
+ if (!resource.Name.EndsWith(".xaml", StringComparison.InvariantCulture))
+ return false;
+
+ using (var resourceStream = resource.GetResourceStream()) {
+ var xmlDoc = new XmlDocument();
+ xmlDoc.Load(resourceStream);
+
+ var nsmgr = new XmlNamespaceManager(xmlDoc.NameTable);
+
+ var root = xmlDoc.SelectSingleNode("/*", nsmgr);
+ if (root == null)
+ return false;
+
+ var rootClass = root.Attributes ["Class", "http://schemas.microsoft.com/winfx/2006/xaml"] ??
+ root.Attributes ["Class", "http://schemas.microsoft.com/winfx/2009/xaml"];
+ if (rootClass == null)
+ return false;
+ classname = rootClass.Value;
+ return true;
+ }
+ }
+ }
+} \ No newline at end of file