summaryrefslogtreecommitdiff
path: root/Xamarin.Forms.Build.Tasks
diff options
context:
space:
mode:
authorJason Smith <jason.smith@xamarin.com>2016-03-22 13:02:25 -0700
committerJason Smith <jason.smith@xamarin.com>2016-03-22 16:13:41 -0700
commit17fdde66d94155fc62a034fa6658995bef6fd6e5 (patch)
treeb5e5073a2a7b15cdbe826faa5c763e270a505729 /Xamarin.Forms.Build.Tasks
downloadxamarin-forms-17fdde66d94155fc62a034fa6658995bef6fd6e5.tar.gz
xamarin-forms-17fdde66d94155fc62a034fa6658995bef6fd6e5.tar.bz2
xamarin-forms-17fdde66d94155fc62a034fa6658995bef6fd6e5.zip
Initial import
Diffstat (limited to 'Xamarin.Forms.Build.Tasks')
-rw-r--r--Xamarin.Forms.Build.Tasks/CreateObjectVisitor.cs544
-rw-r--r--Xamarin.Forms.Build.Tasks/DebugXamlCTask.cs152
-rw-r--r--Xamarin.Forms.Build.Tasks/ExpandMarkupsVisitor.cs207
-rw-r--r--Xamarin.Forms.Build.Tasks/FieldReferenceExtensions.cs22
-rw-r--r--Xamarin.Forms.Build.Tasks/FixedCreateCSharpManifestResourceName.cs27
-rw-r--r--Xamarin.Forms.Build.Tasks/ILContext.cs37
-rw-r--r--Xamarin.Forms.Build.Tasks/ILProcessorExtensions.cs14
-rw-r--r--Xamarin.Forms.Build.Tasks/ILRootNode.cs15
-rw-r--r--Xamarin.Forms.Build.Tasks/MethodDefinitionExtensions.cs38
-rw-r--r--Xamarin.Forms.Build.Tasks/MethodReferenceExtensions.cs63
-rw-r--r--Xamarin.Forms.Build.Tasks/NodeILExtensions.cs480
-rw-r--r--Xamarin.Forms.Build.Tasks/Properties/AssemblyInfo.cs30
-rw-r--r--Xamarin.Forms.Build.Tasks/PropertyDefinitionExtensions.cs33
-rw-r--r--Xamarin.Forms.Build.Tasks/SetFieldVisitor.cs72
-rw-r--r--Xamarin.Forms.Build.Tasks/SetNamescopesAndRegisterNamesVisitor.cs142
-rw-r--r--Xamarin.Forms.Build.Tasks/SetPropertiesVisitor.cs681
-rw-r--r--Xamarin.Forms.Build.Tasks/SetResourcesVisitor.cs141
-rw-r--r--Xamarin.Forms.Build.Tasks/TypeDefinitionExtensions.cs66
-rw-r--r--Xamarin.Forms.Build.Tasks/TypeReferenceExtensions.cs222
-rw-r--r--Xamarin.Forms.Build.Tasks/Xamarin.Forms.Build.Tasks.csproj120
-rw-r--r--Xamarin.Forms.Build.Tasks/XamlCAssemblyResolver.cs15
-rw-r--r--Xamarin.Forms.Build.Tasks/XamlCTask.cs417
-rw-r--r--Xamarin.Forms.Build.Tasks/XamlGTask.cs261
-rw-r--r--Xamarin.Forms.Build.Tasks/XmlTypeExtensions.cs96
-rw-r--r--Xamarin.Forms.Build.Tasks/packages.config4
25 files changed, 3899 insertions, 0 deletions
diff --git a/Xamarin.Forms.Build.Tasks/CreateObjectVisitor.cs b/Xamarin.Forms.Build.Tasks/CreateObjectVisitor.cs
new file mode 100644
index 00000000..0dabb5d9
--- /dev/null
+++ b/Xamarin.Forms.Build.Tasks/CreateObjectVisitor.cs
@@ -0,0 +1,544 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using Mono.Cecil;
+using Mono.Cecil.Cil;
+using Xamarin.Forms.Xaml;
+
+namespace Xamarin.Forms.Build.Tasks
+{
+ class CreateObjectVisitor : IXamlNodeVisitor
+ {
+ public CreateObjectVisitor(ILContext context)
+ {
+ Context = context;
+ Module = context.Body.Method.Module;
+ }
+
+ public ILContext Context { get; }
+
+ ModuleDefinition Module { get; }
+
+ public bool VisitChildrenFirst
+ {
+ get { return true; }
+ }
+
+ public bool StopOnDataTemplate
+ {
+ get { return true; }
+ }
+
+ public bool StopOnResourceDictionary
+ {
+ get { return false; }
+ }
+
+ public void Visit(ValueNode node, INode parentNode)
+ {
+ Context.Values[node] = node.Value;
+
+ XmlName propertyName;
+ if (SetPropertiesVisitor.TryGetPropertyName(node, parentNode, out propertyName))
+ {
+ if (propertyName.NamespaceURI == "http://schemas.openxmlformats.org/markup-compatibility/2006" &&
+ propertyName.LocalName == "Ignorable")
+ {
+ (parentNode.IgnorablePrefixes ?? (parentNode.IgnorablePrefixes = new List<string>())).AddRange(
+ (node.Value as string).Split(','));
+ }
+ }
+ }
+
+ public void Visit(MarkupNode node, INode parentNode)
+ {
+ //At this point, all MarkupNodes are expanded to ElementNodes
+ }
+
+ public void Visit(ElementNode node, INode parentNode)
+ {
+ if (node.SkipPrefix((node.NamespaceResolver ?? parentNode.NamespaceResolver).LookupPrefix(node.NamespaceURI)))
+ return;
+
+ var typeref = node.XmlType.GetTypeReference(Module, node);
+ TypeDefinition typedef = typeref.Resolve();
+
+ if (IsXaml2009LanguagePrimitive(node))
+ {
+ var vardef = new VariableDefinition(typeref);
+ Context.Variables[node] = vardef;
+ Context.Body.Variables.Add(vardef);
+
+ Context.IL.Append(PushValueFromLanguagePrimitive(typedef, node));
+ Context.IL.Emit(OpCodes.Stloc, vardef);
+ }
+ else
+ {
+ MethodDefinition factoryCtorInfo = null;
+ MethodDefinition factoryMethodInfo = null;
+ MethodDefinition parameterizedCtorInfo = null;
+ MethodDefinition ctorInfo = null;
+
+ if (node.Properties.ContainsKey(XmlName.xArguments) && !node.Properties.ContainsKey(XmlName.xFactoryMethod))
+ {
+ factoryCtorInfo = typedef.AllMethods().FirstOrDefault(md => md.IsConstructor &&
+ !md.IsStatic &&
+ md.HasParameters &&
+ md.MatchXArguments(node, Module));
+ if (factoryCtorInfo == null)
+ {
+ throw new XamlParseException(
+ string.Format("No constructors found for {0} with matching x:Arguments", typedef.FullName), node);
+ }
+ ctorInfo = factoryCtorInfo;
+ if (!typedef.IsValueType) //for ctor'ing typedefs, we first have to ldloca before the params
+ Context.IL.Append(PushCtorXArguments(factoryCtorInfo, node));
+ }
+ else if (node.Properties.ContainsKey(XmlName.xFactoryMethod))
+ {
+ var factoryMethod = (string)(node.Properties[XmlName.xFactoryMethod] as ValueNode).Value;
+ factoryMethodInfo = typedef.AllMethods().FirstOrDefault(md => !md.IsConstructor &&
+ md.Name == factoryMethod &&
+ md.IsStatic &&
+ md.MatchXArguments(node, Module));
+ if (factoryMethodInfo == null)
+ {
+ throw new XamlParseException(
+ String.Format("No static method found for {0}::{1} ({2})", typedef.FullName, factoryMethod, null), node);
+ }
+ Context.IL.Append(PushCtorXArguments(factoryMethodInfo, node));
+ }
+ if (ctorInfo == null && factoryMethodInfo == null)
+ {
+ parameterizedCtorInfo = typedef.Methods.FirstOrDefault(md => md.IsConstructor &&
+ !md.IsStatic &&
+ md.HasParameters &&
+ md.Parameters.All(
+ pd =>
+ pd.CustomAttributes.Any(
+ ca =>
+ ca.AttributeType.FullName ==
+ "Xamarin.Forms.ParameterAttribute")));
+ }
+ if (parameterizedCtorInfo != null && ValidateCtorArguments(parameterizedCtorInfo, node))
+ {
+ ctorInfo = parameterizedCtorInfo;
+ // IL_0000: ldstr "foo"
+ Context.IL.Append(PushCtorArguments(parameterizedCtorInfo, node));
+ }
+ ctorInfo = ctorInfo ?? typedef.Methods.FirstOrDefault(md => md.IsConstructor && !md.HasParameters && !md.IsStatic);
+
+ var ctorinforef = ctorInfo?.ResolveGenericParameters(typeref, Module);
+ var factorymethodinforef = factoryMethodInfo?.ResolveGenericParameters(typeref, Module);
+ var implicitOperatorref = typedef.Methods.FirstOrDefault(md =>
+ md.IsPublic &&
+ md.IsStatic &&
+ md.IsSpecialName &&
+ md.Name == "op_Implicit" && md.Parameters[0].ParameterType.FullName == "System.String");
+
+ if (ctorinforef != null || factorymethodinforef != null || typedef.IsValueType)
+ {
+ VariableDefinition vardef = new VariableDefinition(typeref);
+ Context.Variables[node] = vardef;
+ Context.Body.Variables.Add(vardef);
+
+ ValueNode vnode = null;
+ if (node.CollectionItems.Count == 1 && (vnode = node.CollectionItems.First() as ValueNode) != null &&
+ vardef.VariableType.IsValueType)
+ {
+ //<Color>Purple</Color>
+ Context.IL.Append(vnode.PushConvertedValue(Context, typeref, new ICustomAttributeProvider[] { typedef },
+ node.PushServiceProvider(Context), false, true));
+ Context.IL.Emit(OpCodes.Stloc, vardef);
+ }
+ else if (node.CollectionItems.Count == 1 && (vnode = node.CollectionItems.First() as ValueNode) != null &&
+ implicitOperatorref != null)
+ {
+ //<FileImageSource>path.png</FileImageSource>
+ var implicitOperator = Module.Import(implicitOperatorref);
+ Context.IL.Emit(OpCodes.Ldstr, ((ValueNode)(node.CollectionItems.First())).Value as string);
+ Context.IL.Emit(OpCodes.Call, implicitOperator);
+ Context.IL.Emit(OpCodes.Stloc, vardef);
+ }
+ else if (factorymethodinforef != null)
+ {
+ var factory = Module.Import(factorymethodinforef);
+ Context.IL.Emit(OpCodes.Call, factory);
+ Context.IL.Emit(OpCodes.Stloc, vardef);
+ }
+ else if (!typedef.IsValueType)
+ {
+ var ctor = Module.Import(ctorinforef);
+ // IL_0001: newobj instance void class [Xamarin.Forms.Core]Xamarin.Forms.Button::'.ctor'()
+ // IL_0006: stloc.0
+ Context.IL.Emit(OpCodes.Newobj, ctor);
+ Context.IL.Emit(OpCodes.Stloc, vardef);
+ }
+ else if (ctorInfo != null && node.Properties.ContainsKey(XmlName.xArguments) &&
+ !node.Properties.ContainsKey(XmlName.xFactoryMethod) && ctorInfo.MatchXArguments(node, Module))
+ {
+ // IL_0008: ldloca.s 1
+ // IL_000a: ldc.i4.1
+ // IL_000b: call instance void valuetype Test/Foo::'.ctor'(bool)
+
+ var ctor = Module.Import(ctorinforef);
+ Context.IL.Emit(OpCodes.Ldloca, vardef);
+ Context.IL.Append(PushCtorXArguments(factoryCtorInfo, node));
+ Context.IL.Emit(OpCodes.Call, ctor);
+ }
+ else
+ {
+ // IL_0000: ldloca.s 0
+ // IL_0002: initobj Test/Foo
+ Context.IL.Emit(OpCodes.Ldloca, vardef);
+ Context.IL.Emit(OpCodes.Initobj, Module.Import(typedef));
+ }
+
+ if (typeref.FullName == "Xamarin.Forms.Xaml.TypeExtension")
+ {
+ var visitor = new SetPropertiesVisitor(Context);
+ foreach (var cnode in node.Properties.Values.ToList())
+ cnode.Accept(visitor, node);
+ foreach (var cnode in node.CollectionItems)
+ cnode.Accept(visitor, node);
+
+ //As we're stripping the TypeExtension bare, keep the type if we need it later (hint: we do need it)
+ INode ntype;
+ if (!node.Properties.TryGetValue(new XmlName("", "TypeName"), out ntype))
+ ntype = node.CollectionItems[0];
+
+ var type = ((ValueNode)ntype).Value as string;
+ var namespaceuri = type.Contains(":") ? node.NamespaceResolver.LookupNamespace(type.Split(':')[0].Trim()) : "";
+ type = type.Contains(":") ? type.Split(':')[1].Trim() : type;
+ Context.TypeExtensions[node] = new XmlType(namespaceuri, type, null).GetTypeReference(Module, node);
+
+ node.Properties.Clear();
+ node.CollectionItems.Clear();
+
+ var vardefref = new VariableDefinitionReference(vardef);
+ Context.IL.Append(SetPropertiesVisitor.ProvideValue(vardefref, Context, Module, node));
+ if (vardef != vardefref.VariableDefinition)
+ {
+ Context.Variables[node] = vardefref.VariableDefinition;
+ Context.Body.Variables.Add(vardefref.VariableDefinition);
+ }
+ }
+ }
+ }
+ }
+
+ public void Visit(RootNode node, INode parentNode)
+ {
+ // IL_0013: ldarg.0
+ // IL_0014: stloc.3
+
+ var ilnode = (ILRootNode)node;
+ var typeref = ilnode.TypeReference;
+ var vardef = new VariableDefinition(typeref);
+ Context.Variables[node] = vardef;
+ Context.Root = vardef;
+ Context.Body.Variables.Add(vardef);
+ Context.IL.Emit(OpCodes.Ldarg_0);
+ Context.IL.Emit(OpCodes.Stloc, vardef);
+ }
+
+ public void Visit(ListNode node, INode parentNode)
+ {
+ XmlName name;
+ if (SetPropertiesVisitor.TryGetPropertyName(node, parentNode, out name))
+ node.XmlName = name;
+ }
+
+ bool ValidateCtorArguments(MethodDefinition ctorinfo, ElementNode enode)
+ {
+ foreach (var parameter in ctorinfo.Parameters)
+ {
+ var propname =
+ parameter.CustomAttributes.First(ca => ca.AttributeType.FullName == "Xamarin.Forms.ParameterAttribute")
+ .ConstructorArguments.First()
+ .Value as string;
+ if (!enode.Properties.ContainsKey(new XmlName("", propname)))
+ return false;
+ }
+ return true;
+ }
+
+ IEnumerable<Instruction> PushCtorArguments(MethodDefinition ctorinfo, ElementNode enode)
+ {
+ foreach (var parameter in ctorinfo.Parameters)
+ {
+ var propname =
+ parameter.CustomAttributes.First(ca => ca.AttributeType.FullName == "Xamarin.Forms.ParameterAttribute")
+ .ConstructorArguments.First()
+ .Value as string;
+ var node = enode.Properties[new XmlName("", propname)];
+ enode.Properties.Remove(new XmlName("", propname));
+ VariableDefinition vardef;
+ ValueNode vnode = null;
+
+ if (node is IElementNode && (vardef = Context.Variables[node as IElementNode]) != null)
+ yield return Instruction.Create(OpCodes.Ldloc, vardef);
+ else if ((vnode = node as ValueNode) != null)
+ {
+ foreach (var instruction in vnode.PushConvertedValue(Context,
+ parameter.ParameterType,
+ new ICustomAttributeProvider[] { parameter, parameter.ParameterType.Resolve() },
+ enode.PushServiceProvider(Context), false, true))
+ yield return instruction;
+ }
+ }
+ }
+
+ IEnumerable<Instruction> PushCtorXArguments(MethodDefinition factoryCtorInfo, ElementNode enode)
+ {
+ if (!enode.Properties.ContainsKey(XmlName.xArguments))
+ yield break;
+
+ var arguments = new List<INode>();
+ var node = enode.Properties[XmlName.xArguments] as ElementNode;
+ if (node != null)
+ arguments.Add(node);
+ var list = enode.Properties[XmlName.xArguments] as ListNode;
+ if (list != null)
+ {
+ foreach (var n in list.CollectionItems)
+ arguments.Add(n);
+ }
+
+ for (var i = 0; i < factoryCtorInfo.Parameters.Count; i++)
+ {
+ var parameter = factoryCtorInfo.Parameters[i];
+ var arg = arguments[i];
+ VariableDefinition vardef;
+ ValueNode vnode = null;
+
+ if (arg is IElementNode && (vardef = Context.Variables[arg as IElementNode]) != null)
+ yield return Instruction.Create(OpCodes.Ldloc, vardef);
+ else if ((vnode = arg as ValueNode) != null)
+ {
+ foreach (var instruction in vnode.PushConvertedValue(Context,
+ parameter.ParameterType,
+ new ICustomAttributeProvider[] { parameter, parameter.ParameterType.Resolve() },
+ enode.PushServiceProvider(Context), false, true))
+ yield return instruction;
+ }
+ }
+ }
+
+ static bool IsXaml2009LanguagePrimitive(IElementNode node)
+ {
+ if (node.NamespaceURI == "http://schemas.microsoft.com/winfx/2009/xaml")
+ return true;
+ if (node.NamespaceURI != "clr-namespace:System;assembly=mscorlib")
+ return false;
+ var name = node.XmlType.Name.Split(':')[1];
+ if (name == "Boolean" ||
+ name == "String" ||
+ name == "Char" ||
+ name == "Decimal" ||
+ name == "Single" ||
+ name == "Double" ||
+ name == "Byte" ||
+ name == "Int16" ||
+ name == "Int32" ||
+ name == "Int64" ||
+ name == "TimeSpan" ||
+ name == "Uri")
+ return true;
+ return false;
+ }
+
+ IEnumerable<Instruction> PushValueFromLanguagePrimitive(TypeDefinition typedef, ElementNode node)
+ {
+ var hasValue = node.CollectionItems.Count == 1 && node.CollectionItems[0] is ValueNode &&
+ ((ValueNode)node.CollectionItems[0]).Value is string;
+ var valueString = hasValue ? ((ValueNode)node.CollectionItems[0]).Value as string : string.Empty;
+ switch (typedef.FullName)
+ {
+ case "System.Boolean":
+ bool outbool;
+ if (hasValue && bool.TryParse(valueString, out outbool))
+ yield return Instruction.Create(outbool ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0);
+ else
+ yield return Instruction.Create(OpCodes.Ldc_I4_0);
+ break;
+ case "System.String":
+ yield return Instruction.Create(OpCodes.Ldstr, valueString);
+ break;
+ case "System.Object":
+ var ctorinfo =
+ Context.Body.Method.Module.TypeSystem.Object.Resolve()
+ .Methods.FirstOrDefault(md => md.IsConstructor && !md.HasParameters);
+ var ctor = Context.Body.Method.Module.Import(ctorinfo);
+ yield return Instruction.Create(OpCodes.Newobj, ctor);
+ break;
+ case "System.Char":
+ char outchar;
+ if (hasValue && char.TryParse(valueString, out outchar))
+ yield return Instruction.Create(OpCodes.Ldc_I4, outchar);
+ else
+ yield return Instruction.Create(OpCodes.Ldc_I4, 0x00);
+ break;
+ case "System.Decimal":
+ decimal outdecimal;
+ if (hasValue && decimal.TryParse(valueString, NumberStyles.Number, CultureInfo.InvariantCulture, out outdecimal))
+ {
+ var vardef = new VariableDefinition(Context.Body.Method.Module.Import(typeof (decimal)));
+ Context.Body.Variables.Add(vardef);
+ //Use an extra temp var so we can push the value to the stack, just like other cases
+ // IL_0003: ldstr "adecimal"
+ // IL_0008: ldc.i4.s 0x6f
+ // IL_000a: call class [mscorlib]System.Globalization.CultureInfo class [mscorlib]System.Globalization.CultureInfo::get_InvariantCulture()
+ // IL_000f: ldloca.s 0
+ // IL_0011: call bool valuetype [mscorlib]System.Decimal::TryParse(string, valuetype [mscorlib]System.Globalization.NumberStyles, class [mscorlib]System.IFormatProvider, [out] valuetype [mscorlib]System.Decimal&)
+ // IL_0016: pop
+ yield return Instruction.Create(OpCodes.Ldstr, valueString);
+ yield return Instruction.Create(OpCodes.Ldc_I4, 0x6f); //NumberStyles.Number
+ var getInvariantInfo =
+ Context.Body.Method.Module.Import(typeof (CultureInfo))
+ .Resolve()
+ .Properties.FirstOrDefault(pd => pd.Name == "InvariantCulture")
+ .GetMethod;
+ var getInvariant = Context.Body.Method.Module.Import(getInvariantInfo);
+ yield return Instruction.Create(OpCodes.Call, getInvariant);
+ yield return Instruction.Create(OpCodes.Ldloca, vardef);
+ var tryParseInfo =
+ Context.Body.Method.Module.Import(typeof (decimal))
+ .Resolve()
+ .Methods.FirstOrDefault(md => md.Name == "TryParse" && md.Parameters.Count == 4);
+ var tryParse = Context.Body.Method.Module.Import(tryParseInfo);
+ yield return Instruction.Create(OpCodes.Call, tryParse);
+ yield return Instruction.Create(OpCodes.Pop);
+ yield return Instruction.Create(OpCodes.Ldloc, vardef);
+ }
+ else
+ {
+ yield return Instruction.Create(OpCodes.Ldc_I4_0);
+ var decimalctorinfo =
+ Context.Body.Method.Module.Import(typeof (decimal))
+ .Resolve()
+ .Methods.FirstOrDefault(
+ md => md.IsConstructor && md.Parameters.Count == 1 && md.Parameters[0].ParameterType.FullName == "System.Int32");
+ var decimalctor = Context.Body.Method.Module.Import(decimalctorinfo);
+ yield return Instruction.Create(OpCodes.Newobj, decimalctor);
+ }
+ break;
+ case "System.Single":
+ float outfloat;
+ if (hasValue && float.TryParse(valueString, NumberStyles.Number, CultureInfo.InvariantCulture, out outfloat))
+ yield return Instruction.Create(OpCodes.Ldc_R4, outfloat);
+ else
+ yield return Instruction.Create(OpCodes.Ldc_R4, 0f);
+ break;
+ case "System.Double":
+ double outdouble;
+ if (hasValue && double.TryParse(valueString, NumberStyles.Number, CultureInfo.InvariantCulture, out outdouble))
+ yield return Instruction.Create(OpCodes.Ldc_R8, outdouble);
+ else
+ yield return Instruction.Create(OpCodes.Ldc_R8, 0d);
+ break;
+ case "System.Byte":
+ byte outbyte;
+ if (hasValue && byte.TryParse(valueString, NumberStyles.Number, CultureInfo.InvariantCulture, out outbyte))
+ yield return Instruction.Create(OpCodes.Ldc_I4, (int)outbyte);
+ else
+ yield return Instruction.Create(OpCodes.Ldc_I4, 0x00);
+ break;
+ case "System.Int16":
+ short outshort;
+ if (hasValue && short.TryParse(valueString, NumberStyles.Number, CultureInfo.InvariantCulture, out outshort))
+ yield return Instruction.Create(OpCodes.Ldc_I4, outshort);
+ else
+ yield return Instruction.Create(OpCodes.Ldc_I4, 0x00);
+ break;
+ case "System.Int32":
+ int outint;
+ if (hasValue && int.TryParse(valueString, NumberStyles.Number, CultureInfo.InvariantCulture, out outint))
+ yield return Instruction.Create(OpCodes.Ldc_I4, outint);
+ else
+ yield return Instruction.Create(OpCodes.Ldc_I4, 0x00);
+ break;
+ case "System.Int64":
+ long outlong;
+ if (hasValue && long.TryParse(valueString, NumberStyles.Number, CultureInfo.InvariantCulture, out outlong))
+ yield return Instruction.Create(OpCodes.Ldc_I8, outlong);
+ else
+ yield return Instruction.Create(OpCodes.Ldc_I8, 0L);
+ break;
+ case "System.TimeSpan":
+ TimeSpan outspan;
+ if (hasValue && TimeSpan.TryParse(valueString, CultureInfo.InvariantCulture, out outspan))
+ {
+ var vardef = new VariableDefinition(Context.Body.Method.Module.Import(typeof (TimeSpan)));
+ Context.Body.Variables.Add(vardef);
+ //Use an extra temp var so we can push the value to the stack, just like other cases
+ yield return Instruction.Create(OpCodes.Ldstr, valueString);
+ var getInvariantInfo =
+ Context.Body.Method.Module.Import(typeof (CultureInfo))
+ .Resolve()
+ .Properties.FirstOrDefault(pd => pd.Name == "InvariantCulture")
+ .GetMethod;
+ var getInvariant = Context.Body.Method.Module.Import(getInvariantInfo);
+ yield return Instruction.Create(OpCodes.Call, getInvariant);
+ yield return Instruction.Create(OpCodes.Ldloca, vardef);
+ var tryParseInfo =
+ Context.Body.Method.Module.Import(typeof (TimeSpan))
+ .Resolve()
+ .Methods.FirstOrDefault(md => md.Name == "TryParse" && md.Parameters.Count == 3);
+ var tryParse = Context.Body.Method.Module.Import(tryParseInfo);
+ yield return Instruction.Create(OpCodes.Call, tryParse);
+ yield return Instruction.Create(OpCodes.Pop);
+ yield return Instruction.Create(OpCodes.Ldloc, vardef);
+ }
+ else
+ {
+ yield return Instruction.Create(OpCodes.Ldc_I8, 0L);
+ var timespanctorinfo =
+ Context.Body.Method.Module.Import(typeof (TimeSpan))
+ .Resolve()
+ .Methods.FirstOrDefault(
+ md => md.IsConstructor && md.Parameters.Count == 1 && md.Parameters[0].ParameterType.FullName == "System.Int64");
+ var timespanctor = Context.Body.Method.Module.Import(timespanctorinfo);
+ yield return Instruction.Create(OpCodes.Newobj, timespanctor);
+ }
+ break;
+ case "System.Uri":
+ Uri outuri;
+ if (hasValue && Uri.TryCreate(valueString, UriKind.RelativeOrAbsolute, out outuri))
+ {
+ var vardef = new VariableDefinition(Context.Body.Method.Module.Import(typeof (Uri)));
+ Context.Body.Variables.Add(vardef);
+ //Use an extra temp var so we can push the value to the stack, just like other cases
+ yield return Instruction.Create(OpCodes.Ldstr, valueString);
+ yield return Instruction.Create(OpCodes.Ldc_I4, (int)UriKind.RelativeOrAbsolute);
+ yield return Instruction.Create(OpCodes.Ldloca, vardef);
+ var tryCreateInfo =
+ Context.Body.Method.Module.Import(typeof (Uri))
+ .Resolve()
+ .Methods.FirstOrDefault(md => md.Name == "TryCreate" && md.Parameters.Count == 3);
+ var tryCreate = Context.Body.Method.Module.Import(tryCreateInfo);
+ yield return Instruction.Create(OpCodes.Call, tryCreate);
+ yield return Instruction.Create(OpCodes.Pop);
+ yield return Instruction.Create(OpCodes.Ldloc, vardef);
+ }
+ else
+ yield return Instruction.Create(OpCodes.Ldnull);
+ break;
+ default:
+ var defaultctorinfo = typedef.Methods.FirstOrDefault(md => md.IsConstructor && !md.HasParameters);
+ if (defaultctorinfo != null)
+ {
+ var defaultctor = Context.Body.Method.Module.Import(defaultctorinfo);
+ yield return Instruction.Create(OpCodes.Newobj, defaultctor);
+ }
+ else
+ {
+ //should never happen. but if it does, this prevents corrupting the IL stack
+ yield return Instruction.Create(OpCodes.Ldnull);
+ }
+ break;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Build.Tasks/DebugXamlCTask.cs b/Xamarin.Forms.Build.Tasks/DebugXamlCTask.cs
new file mode 100644
index 00000000..f4b2f52c
--- /dev/null
+++ b/Xamarin.Forms.Build.Tasks/DebugXamlCTask.cs
@@ -0,0 +1,152 @@
+using System;
+using System.IO;
+using System.Linq;
+using Mono.Cecil;
+using Mono.Cecil.Cil;
+using Mono.Cecil.Rocks;
+
+namespace Xamarin.Forms.Build.Tasks
+{
+ public class DebugXamlCTask : XamlCTask
+ {
+ public override bool Execute()
+ {
+ InMsBuild = true;
+ Verbosity = Int32.MaxValue;
+ LogLine(1, "Preparing debug code for xamlc");
+ 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);
+ resolver.AddSearchDirectory(dep);
+ }
+ }
+ if (!string.IsNullOrEmpty(ReferencePath))
+ {
+ var paths = ReferencePath.Replace("//", "/").Split(';');
+ foreach (var p in paths)
+ {
+ var searchpath = Path.GetDirectoryName(p);
+ LogLine(3, "Adding searchpath {0}", searchpath);
+ resolver.AddSearchDirectory(searchpath);
+ // LogLine (3, "Referencing {0}", p);
+ // resolver.AddAssembly (p);
+ }
+ }
+ var assemblyDefinition = AssemblyDefinition.ReadAssembly(Assembly, new ReaderParameters
+ {
+ //ReadSymbols = DebugSymbols,
+ AssemblyResolver = resolver
+ });
+
+ foreach (var module in assemblyDefinition.Modules)
+ {
+ LogLine(2, " Module: {0}", module.Name);
+ foreach (var resource in module.Resources.OfType<EmbeddedResource>())
+ {
+ Log(2, " Resource: {0}... ", resource.Name);
+ string classname;
+ if (!resource.IsXaml(out classname))
+ {
+ LogLine(2, "skipped.");
+ continue;
+ }
+ TypeDefinition typeDef = module.GetType(classname);
+ if (typeDef == null)
+ {
+ 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.");
+ continue;
+ }
+ if (typeDef.Methods.FirstOrDefault(md => md.Name == "InitCompRuntime") != null)
+ {
+ LogLine(2, "InitCompRuntime already exists... skipped");
+ continue;
+ }
+ LogLine(2, "");
+
+ Log(2, " Duplicating {0}.InitializeComponent () into {0}.InitCompRuntime ... ", typeDef.Name);
+ var initCompRuntime = new MethodDefinition("InitCompRuntime", initComp.Attributes, initComp.ReturnType);
+ initCompRuntime.Body = initComp.Body;
+ typeDef.Methods.Add(initCompRuntime);
+ 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::InitCompRuntime()
+ // 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)
+ Log(2, " Replacing body of {0}.{0} (bool {1}) ... ", typeDef.Name, altCtor.Parameters[0].Name);
+ else
+ {
+ Log(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);
+ 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.Import(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);
+ LogLine(2, "done.");
+ }
+
+ LogLine(2, "");
+ }
+ Log(1, "Writing the assembly... ");
+ assemblyDefinition.Write(Assembly, new WriterParameters
+ {
+ WriteSymbols = DebugSymbols
+ });
+ LogLine(1, "done.");
+
+ return true;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Build.Tasks/ExpandMarkupsVisitor.cs b/Xamarin.Forms.Build.Tasks/ExpandMarkupsVisitor.cs
new file mode 100644
index 00000000..e63ae426
--- /dev/null
+++ b/Xamarin.Forms.Build.Tasks/ExpandMarkupsVisitor.cs
@@ -0,0 +1,207 @@
+using System;
+using System.Collections.Generic;
+using System.Xml;
+using Xamarin.Forms.Xaml;
+using Xamarin.Forms.Xaml.Internals;
+
+namespace Xamarin.Forms.Build.Tasks
+{
+ class ExpandMarkupsVisitor : IXamlNodeVisitor
+ {
+ readonly IList<XmlName> skips = new List<XmlName>
+ {
+ XmlName.xKey,
+ XmlName.xTypeArguments,
+ XmlName.xArguments,
+ XmlName.xFactoryMethod,
+ XmlName.xName
+ };
+
+ public ExpandMarkupsVisitor(ILContext context)
+ {
+ Context = context;
+ }
+
+ ILContext Context { get; }
+
+ public bool VisitChildrenFirst
+ {
+ get { return true; }
+ }
+
+ public bool StopOnDataTemplate
+ {
+ get { return false; }
+ }
+
+ public bool StopOnResourceDictionary
+ {
+ get { return false; }
+ }
+
+ public void Visit(ValueNode node, INode parentNode)
+ {
+ }
+
+ public void Visit(MarkupNode markupnode, INode parentNode)
+ {
+ XmlName propertyName;
+ if (!TryGetProperyName(markupnode, parentNode, out propertyName))
+ return;
+ if (skips.Contains(propertyName))
+ return;
+ var markupString = markupnode.MarkupString;
+ var node = ParseExpression(ref markupString, Context, markupnode.NamespaceResolver, markupnode) as IElementNode;
+ if (node != null)
+ {
+ ((IElementNode)parentNode).Properties[propertyName] = node;
+ node.Accept(new XamlNodeVisitor((n, parent) => n.Parent = parent), parentNode);
+ }
+ }
+
+ public void Visit(ElementNode node, INode parentNode)
+ {
+ }
+
+ public void Visit(RootNode node, INode parentNode)
+ {
+ }
+
+ public void Visit(ListNode node, INode parentNode)
+ {
+ }
+
+ public static bool TryGetProperyName(INode node, INode parentNode, out XmlName name)
+ {
+ name = default(XmlName);
+ var parentElement = parentNode as IElementNode;
+ if (parentElement == null)
+ return false;
+ foreach (var kvp in parentElement.Properties)
+ {
+ if (kvp.Value != node)
+ continue;
+ name = kvp.Key;
+ return true;
+ }
+ return false;
+ }
+
+ static INode ParseExpression(ref string expression, ILContext context, IXmlNamespaceResolver nsResolver,
+ IXmlLineInfo xmlLineInfo)
+ {
+ if (expression.StartsWith("{}", StringComparison.Ordinal))
+ return new ValueNode(expression.Substring(2), null);
+
+ if (expression[expression.Length - 1] != '}')
+ throw new XamlParseException("Markup expression missing its closing tag", xmlLineInfo);
+
+ int len;
+ string match;
+ if (!MarkupExpressionParser.MatchMarkup(out match, expression, out len))
+ throw new XamlParseException("Error while parsing markup expression", xmlLineInfo);
+ expression = expression.Substring(len).TrimStart();
+ if (expression.Length == 0)
+ throw new XamlParseException("Markup expression not closed", xmlLineInfo);
+
+ var provider = new XamlServiceProvider(null, null);
+ provider.Add(typeof (ILContextProvider), new ILContextProvider(context));
+ provider.Add(typeof (IXmlNamespaceResolver), nsResolver);
+ provider.Add(typeof (IXmlLineInfoProvider), new XmlLineInfoProvider(xmlLineInfo));
+
+ return new MarkupExpansionParser().Parse(match, ref expression, provider);
+ }
+
+ class ILContextProvider
+ {
+ public ILContextProvider(ILContext context)
+ {
+ Context = context;
+ }
+
+ public ILContext Context { get; }
+ }
+
+ class MarkupExpansionParser : MarkupExpressionParser, IExpressionParser<INode>
+ {
+ IElementNode node;
+
+ object IExpressionParser.Parse(string match, ref string remaining, IServiceProvider serviceProvider)
+ {
+ return Parse(match, ref remaining, serviceProvider);
+ }
+
+ public INode Parse(string match, ref string remaining, IServiceProvider serviceProvider)
+ {
+ var nsResolver = serviceProvider.GetService(typeof (IXmlNamespaceResolver)) as IXmlNamespaceResolver;
+ if (nsResolver == null)
+ throw new ArgumentException();
+ IXmlLineInfo xmlLineInfo = null;
+ var xmlLineInfoProvider = serviceProvider.GetService(typeof (IXmlLineInfoProvider)) as IXmlLineInfoProvider;
+ if (xmlLineInfoProvider != null)
+ xmlLineInfo = xmlLineInfoProvider.XmlLineInfo;
+ var contextProvider = serviceProvider.GetService(typeof (ILContextProvider)) as ILContextProvider;
+
+ var split = match.Split(':');
+ if (split.Length > 2)
+ throw new ArgumentException();
+
+ string prefix, name;
+ if (split.Length == 2)
+ {
+ prefix = split[0];
+ name = split[1];
+ }
+ else
+ {
+ prefix = "";
+ name = split[0];
+ }
+
+ var namespaceuri = nsResolver.LookupNamespace(prefix) ?? "";
+ //The order of lookup is to look for the Extension-suffixed class name first and then look for the class name without the Extension suffix.
+ XmlType type;
+ try
+ {
+ type = new XmlType(namespaceuri, name + "Extension", null);
+ var typeref = type.GetTypeReference(contextProvider.Context.Body.Method.Module, null);
+ }
+ catch (XamlParseException)
+ {
+ type = new XmlType(namespaceuri, name, null);
+ }
+
+ if (type == null)
+ throw new NotSupportedException();
+
+ node = xmlLineInfo == null
+ ? new ElementNode(type, null, nsResolver)
+ : new ElementNode(type, null, nsResolver, xmlLineInfo.LineNumber, xmlLineInfo.LinePosition);
+
+ if (remaining.StartsWith("}", StringComparison.Ordinal))
+ {
+ remaining = remaining.Substring(1);
+ return node;
+ }
+
+ char next;
+ string piece;
+ while ((piece = GetNextPiece(ref remaining, out next)) != null)
+ HandleProperty(piece, serviceProvider, ref remaining, next != '=');
+
+ return node;
+ }
+
+ protected override void SetPropertyValue(string prop, string strValue, object value, IServiceProvider serviceProvider)
+ {
+ if (prop != null)
+ {
+ var name = new XmlName(node.NamespaceURI, prop);
+ node.Properties[name] = value as INode ?? new ValueNode(strValue, null);
+ }
+ else //ContentProperty
+ node.CollectionItems.Add(value as INode ?? new ValueNode(strValue, null));
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Build.Tasks/FieldReferenceExtensions.cs b/Xamarin.Forms.Build.Tasks/FieldReferenceExtensions.cs
new file mode 100644
index 00000000..3876b9c8
--- /dev/null
+++ b/Xamarin.Forms.Build.Tasks/FieldReferenceExtensions.cs
@@ -0,0 +1,22 @@
+using Mono.Cecil;
+
+namespace Xamarin.Forms.Build.Tasks
+{
+ static class FieldReferenceExtensions
+ {
+ public static FieldReference ResolveGenericParameters(this FieldReference self, TypeReference declaringTypeRef)
+ {
+ var fieldType = self.FieldType;
+ if (fieldType.IsGenericParameter)
+ {
+ var genericParameter = (GenericParameter)fieldType;
+ fieldType = ((GenericInstanceType)declaringTypeRef).GenericArguments[genericParameter.Position];
+ }
+ var fieldReference = new FieldReference(self.Name, fieldType)
+ {
+ DeclaringType = declaringTypeRef
+ };
+ return fieldReference;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Build.Tasks/FixedCreateCSharpManifestResourceName.cs b/Xamarin.Forms.Build.Tasks/FixedCreateCSharpManifestResourceName.cs
new file mode 100644
index 00000000..a508a558
--- /dev/null
+++ b/Xamarin.Forms.Build.Tasks/FixedCreateCSharpManifestResourceName.cs
@@ -0,0 +1,27 @@
+using Microsoft.Build.Framework;
+using Microsoft.Build.Tasks;
+using Microsoft.Build.Utilities;
+
+namespace Xamarin.Forms.Build.Tasks
+{
+ public class FixedCreateCSharpManifestResourceName : CreateCSharpManifestResourceName
+ {
+ [Output]
+ public ITaskItem[] ResourceFilesWithManifestResourceNames { get; set; }
+
+ public override bool Execute()
+ {
+ var ret = base.Execute();
+
+ ResourceFilesWithManifestResourceNames = new TaskItem[ResourceFiles.Length];
+
+ for (var i = 0; i < ResourceFiles.Length; i++)
+ {
+ var copy = new TaskItem(ResourceFiles[i]);
+ copy.SetMetadata("ManifestResourceName", ManifestResourceNames[i].ItemSpec);
+ ResourceFilesWithManifestResourceNames[i] = copy;
+ }
+ return ret;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Build.Tasks/ILContext.cs b/Xamarin.Forms.Build.Tasks/ILContext.cs
new file mode 100644
index 00000000..14ade391
--- /dev/null
+++ b/Xamarin.Forms.Build.Tasks/ILContext.cs
@@ -0,0 +1,37 @@
+using System.Collections.Generic;
+using Mono.Cecil;
+using Mono.Cecil.Cil;
+using Xamarin.Forms.Xaml;
+
+namespace Xamarin.Forms.Build.Tasks
+{
+ class ILContext
+ {
+ public ILContext(ILProcessor il, MethodBody body, FieldDefinition parentContextValues = null)
+ {
+ IL = il;
+ Body = body;
+ Values = new Dictionary<IValueNode, object>();
+ Variables = new Dictionary<IElementNode, VariableDefinition>();
+ Scopes = new Dictionary<INode, VariableDefinition>();
+ TypeExtensions = new Dictionary<INode, TypeReference>();
+ ParentContextValues = parentContextValues;
+ }
+
+ public Dictionary<IValueNode, object> Values { get; private set; }
+
+ public Dictionary<IElementNode, VariableDefinition> Variables { get; private set; }
+
+ public Dictionary<INode, VariableDefinition> Scopes { get; private set; }
+
+ public Dictionary<INode, TypeReference> TypeExtensions { get; }
+
+ public FieldDefinition ParentContextValues { get; private set; }
+
+ public object Root { get; set; } //FieldDefinition or VariableDefinition
+
+ public ILProcessor IL { get; private set; }
+
+ public MethodBody Body { get; private set; }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Build.Tasks/ILProcessorExtensions.cs b/Xamarin.Forms.Build.Tasks/ILProcessorExtensions.cs
new file mode 100644
index 00000000..6bcedc78
--- /dev/null
+++ b/Xamarin.Forms.Build.Tasks/ILProcessorExtensions.cs
@@ -0,0 +1,14 @@
+using System.Collections.Generic;
+using Mono.Cecil.Cil;
+
+namespace Xamarin.Forms.Build.Tasks
+{
+ static class ILProcessorExtensions
+ {
+ public static void Append(this ILProcessor processor, IEnumerable<Instruction> instructions)
+ {
+ foreach (var instruction in instructions)
+ processor.Append(instruction);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Build.Tasks/ILRootNode.cs b/Xamarin.Forms.Build.Tasks/ILRootNode.cs
new file mode 100644
index 00000000..b50d6204
--- /dev/null
+++ b/Xamarin.Forms.Build.Tasks/ILRootNode.cs
@@ -0,0 +1,15 @@
+using Mono.Cecil;
+using Xamarin.Forms.Xaml;
+
+namespace Xamarin.Forms.Build.Tasks
+{
+ class ILRootNode : RootNode
+ {
+ public ILRootNode(XmlType xmlType, TypeReference typeReference) : base(xmlType)
+ {
+ TypeReference = typeReference;
+ }
+
+ public TypeReference TypeReference { get; private set; }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Build.Tasks/MethodDefinitionExtensions.cs b/Xamarin.Forms.Build.Tasks/MethodDefinitionExtensions.cs
new file mode 100644
index 00000000..41d7cb20
--- /dev/null
+++ b/Xamarin.Forms.Build.Tasks/MethodDefinitionExtensions.cs
@@ -0,0 +1,38 @@
+using System.Collections.Generic;
+using Mono.Cecil;
+using Xamarin.Forms.Xaml;
+
+namespace Xamarin.Forms.Build.Tasks
+{
+ static class MethodDefinitionExtensions
+ {
+ public static bool MatchXArguments(this MethodDefinition methodDefinition, ElementNode enode, ModuleDefinition module)
+ {
+ if (!enode.Properties.ContainsKey(XmlName.xArguments))
+ return !methodDefinition.HasParameters;
+
+ var arguments = new List<INode>();
+ var node = enode.Properties[XmlName.xArguments] as ElementNode;
+ if (node != null)
+ arguments.Add(node);
+ var list = enode.Properties[XmlName.xArguments] as ListNode;
+ if (list != null)
+ {
+ foreach (var n in list.CollectionItems)
+ arguments.Add(n);
+ }
+
+ if (methodDefinition.Parameters.Count != arguments.Count)
+ return false;
+
+ for (var i = 0; i < methodDefinition.Parameters.Count; i++)
+ {
+ var paramType = methodDefinition.Parameters[i].ParameterType;
+ var argType = ((IElementNode)arguments[i]).XmlType.GetTypeReference(module, null);
+ if (!argType.InheritsFromOrImplements(paramType))
+ return false;
+ }
+ return true;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Build.Tasks/MethodReferenceExtensions.cs b/Xamarin.Forms.Build.Tasks/MethodReferenceExtensions.cs
new file mode 100644
index 00000000..e377a0c6
--- /dev/null
+++ b/Xamarin.Forms.Build.Tasks/MethodReferenceExtensions.cs
@@ -0,0 +1,63 @@
+using System;
+using Mono.Cecil;
+using Mono.Cecil.Rocks;
+
+namespace Xamarin.Forms.Build.Tasks
+{
+ static class MethodReferenceExtensions
+ {
+ [Obsolete]
+ public static MethodReference MakeGeneric(this MethodReference self, params TypeReference[] arguments)
+ {
+ var reference = new MethodReference(self.Name, self.ReturnType)
+ {
+ DeclaringType = self.DeclaringType.MakeGenericInstanceType(arguments),
+ 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;
+ }
+
+ public static MethodReference ResolveGenericParameters(this MethodReference self, TypeReference declaringTypeRef,
+ ModuleDefinition module)
+ {
+ if (self == null)
+ throw new ArgumentNullException("self");
+ if (declaringTypeRef == null)
+ throw new ArgumentNullException("declaringTypeRef");
+
+ var reference = new MethodReference(self.Name, self.ReturnType)
+ {
+ DeclaringType = declaringTypeRef,
+ HasThis = self.HasThis,
+ ExplicitThis = self.ExplicitThis,
+ CallingConvention = self.CallingConvention
+ };
+
+ foreach (var parameter in self.Parameters)
+ reference.Parameters.Add(new ParameterDefinition(module.Import(parameter.ParameterType)));
+
+ foreach (var generic_parameter in self.GenericParameters)
+ reference.GenericParameters.Add(new GenericParameter(generic_parameter.Name, reference));
+
+ return reference;
+ }
+
+ public static void ImportTypes(this MethodReference self, ModuleDefinition module)
+ {
+ if (self.HasParameters)
+ {
+ for (var i = 0; i < self.Parameters.Count; i++)
+ self.Parameters[i].ParameterType = module.Import(self.Parameters[i].ParameterType);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Build.Tasks/NodeILExtensions.cs b/Xamarin.Forms.Build.Tasks/NodeILExtensions.cs
new file mode 100644
index 00000000..30b01061
--- /dev/null
+++ b/Xamarin.Forms.Build.Tasks/NodeILExtensions.cs
@@ -0,0 +1,480 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Xml;
+using Mono.Cecil;
+using Mono.Cecil.Cil;
+using Xamarin.Forms.Xaml;
+using Xamarin.Forms.Xaml.Internals;
+
+namespace Xamarin.Forms.Build.Tasks
+{
+ static class NodeILExtensions
+ {
+ public static IEnumerable<Instruction> PushConvertedValue(this ValueNode node, ILContext context,
+ TypeReference targetTypeRef, IEnumerable<ICustomAttributeProvider> attributeProviders,
+ IEnumerable<Instruction> pushServiceProvider, bool boxValueTypes, bool unboxValueTypes)
+ {
+ TypeReference typeConverter = null;
+ foreach (var attributeProvider in attributeProviders)
+ {
+ CustomAttribute typeConverterAttribute;
+ if (
+ (typeConverterAttribute =
+ attributeProvider.CustomAttributes.FirstOrDefault(
+ cad => TypeConverterAttribute.TypeConvertersType.Contains(cad.AttributeType.FullName))) != null)
+ {
+ typeConverter = typeConverterAttribute.ConstructorArguments[0].Value as TypeReference;
+ break;
+ }
+ }
+ return node.PushConvertedValue(context, targetTypeRef, typeConverter, pushServiceProvider, boxValueTypes,
+ unboxValueTypes);
+ }
+
+ public static IEnumerable<Instruction> PushConvertedValue(this ValueNode node, ILContext context, FieldReference bpRef,
+ IEnumerable<Instruction> pushServiceProvider, bool boxValueTypes, bool unboxValueTypes)
+ {
+ var module = context.Body.Method.Module;
+ var targetTypeRef = GetBPReturnType(context, bpRef, node);
+
+ TypeReference typeConverter;
+ bpRef.HasTypeConverter(module, out typeConverter);
+
+ return node.PushConvertedValue(context, targetTypeRef, typeConverter, pushServiceProvider, boxValueTypes,
+ unboxValueTypes);
+ }
+
+ public static IEnumerable<Instruction> PushConvertedValue(this ValueNode node, ILContext context,
+ TypeReference targetTypeRef, TypeReference typeConverter, IEnumerable<Instruction> pushServiceProvider,
+ bool boxValueTypes, bool unboxValueTypes)
+ {
+ var module = context.Body.Method.Module;
+ var str = (string)node.Value;
+
+ //If there's a [TypeConverter], use it
+ if (typeConverter != null)
+ {
+ var isExtendedConverter = typeConverter.ImplementsInterface(module.Import(typeof (IExtendedTypeConverter)));
+ var typeConverterCtor = typeConverter.Resolve().Methods.Single(md => md.IsConstructor && md.Parameters.Count == 0);
+ var typeConverterCtorRef = module.Import(typeConverterCtor);
+ var convertFromInvariantStringDefinition = isExtendedConverter
+ ? module.Import(typeof (IExtendedTypeConverter))
+ .Resolve()
+ .Methods.FirstOrDefault(md => md.Name == "ConvertFromInvariantString" && md.Parameters.Count == 2)
+ : typeConverter.Resolve()
+ .AllMethods()
+ .FirstOrDefault(md => md.Name == "ConvertFromInvariantString" && md.Parameters.Count == 1);
+ var convertFromInvariantStringReference = module.Import(convertFromInvariantStringDefinition);
+
+ yield return Instruction.Create(OpCodes.Newobj, typeConverterCtorRef);
+ yield return Instruction.Create(OpCodes.Ldstr, node.Value as string);
+
+ if (isExtendedConverter)
+ {
+ foreach (var instruction in pushServiceProvider)
+ yield return instruction;
+ }
+
+ yield return Instruction.Create(OpCodes.Callvirt, convertFromInvariantStringReference);
+
+ if (targetTypeRef.IsValueType && unboxValueTypes)
+ yield return Instruction.Create(OpCodes.Unbox_Any, module.Import(targetTypeRef));
+
+ //ConvertFrom returns an object, no need to Box
+ yield break;
+ }
+ var originalTypeRef = targetTypeRef;
+ var isNullable = false;
+ MethodReference nullableCtor = null;
+ if (targetTypeRef.Resolve().FullName == "System.Nullable`1")
+ {
+ targetTypeRef = ((GenericInstanceType)targetTypeRef).GenericArguments[0];
+ isNullable = true;
+ nullableCtor = originalTypeRef.GetMethods(md => md.IsConstructor && md.Parameters.Count == 1, module).Single().Item1;
+ nullableCtor = nullableCtor.MakeGeneric(targetTypeRef);
+ }
+ //Obvious Built-in conversions
+ if (targetTypeRef.Resolve().BaseType != null && targetTypeRef.Resolve().BaseType.FullName == "System.Enum")
+ yield return Instruction.Create(OpCodes.Ldc_I4, ParseEnum(targetTypeRef, str, node));
+ else if (targetTypeRef.FullName == "System.Char")
+ yield return Instruction.Create(OpCodes.Ldc_I4, Char.Parse(str));
+ else if (targetTypeRef.FullName == "System.Byte")
+ yield return Instruction.Create(OpCodes.Ldc_I4, Byte.Parse(str, CultureInfo.InvariantCulture));
+ else if (targetTypeRef.FullName == "System.Int16")
+ yield return Instruction.Create(OpCodes.Ldc_I4, Int16.Parse(str, CultureInfo.InvariantCulture));
+ else if (targetTypeRef.FullName == "System.Int32")
+ yield return Instruction.Create(OpCodes.Ldc_I4, Int32.Parse(str, CultureInfo.InvariantCulture));
+ else if (targetTypeRef.FullName == "System.Int64")
+ yield return Instruction.Create(OpCodes.Ldc_I8, Int64.Parse(str, CultureInfo.InvariantCulture));
+ else if (targetTypeRef.FullName == "System.Single")
+ yield return Instruction.Create(OpCodes.Ldc_R4, Single.Parse(str, CultureInfo.InvariantCulture));
+ else if (targetTypeRef.FullName == "System.Double")
+ yield return Instruction.Create(OpCodes.Ldc_R8, Double.Parse(str, CultureInfo.InvariantCulture));
+ else if (targetTypeRef.FullName == "System.Boolean")
+ {
+ if (Boolean.Parse(str))
+ yield return Instruction.Create(OpCodes.Ldc_I4_1);
+ else
+ yield return Instruction.Create(OpCodes.Ldc_I4_0);
+ }
+ else if (targetTypeRef.FullName == "System.TimeSpan")
+ {
+ var ts = TimeSpan.Parse(str, CultureInfo.InvariantCulture);
+ var ticks = ts.Ticks;
+ var timeSpanCtor =
+ module.Import(typeof (TimeSpan))
+ .Resolve()
+ .Methods.FirstOrDefault(md => md.IsConstructor && md.Parameters.Count == 1);
+ var timeSpanCtorRef = module.Import(timeSpanCtor);
+
+ yield return Instruction.Create(OpCodes.Ldc_I8, ticks);
+ yield return Instruction.Create(OpCodes.Newobj, timeSpanCtorRef);
+ }
+ else if (targetTypeRef.FullName == "System.DateTime")
+ {
+ var dt = DateTime.Parse(str, CultureInfo.InvariantCulture);
+ var ticks = dt.Ticks;
+ var dateTimeCtor =
+ module.Import(typeof (DateTime))
+ .Resolve()
+ .Methods.FirstOrDefault(md => md.IsConstructor && md.Parameters.Count == 1);
+ var dateTimeCtorRef = module.Import(dateTimeCtor);
+
+ yield return Instruction.Create(OpCodes.Ldc_I8, ticks);
+ yield return Instruction.Create(OpCodes.Newobj, dateTimeCtorRef);
+ }
+ else if (targetTypeRef.FullName == "System.String" && str.StartsWith("{}", StringComparison.Ordinal))
+ yield return Instruction.Create(OpCodes.Ldstr, str.Substring(2));
+ else if (targetTypeRef.FullName == "System.String")
+ yield return Instruction.Create(OpCodes.Ldstr, str);
+ else if (targetTypeRef.FullName == "System.Object")
+ yield return Instruction.Create(OpCodes.Ldstr, str);
+ else
+ yield return Instruction.Create(OpCodes.Ldnull);
+
+ if (isNullable)
+ yield return Instruction.Create(OpCodes.Newobj, module.Import(nullableCtor));
+ if (originalTypeRef.IsValueType && boxValueTypes)
+ yield return Instruction.Create(OpCodes.Box, module.Import(originalTypeRef));
+ }
+
+ static int ParseEnum(TypeReference enumRef, string value, IXmlLineInfo lineInfo)
+ {
+ var enumDef = enumRef.Resolve();
+ if (!enumDef.IsEnum)
+ throw new InvalidOperationException();
+
+ int? result = null;
+
+ foreach (var v in value.Split(','))
+ {
+ foreach (var field in enumDef.Fields)
+ {
+ if (field.Name == "value__")
+ continue;
+ if (field.Name == v.Trim())
+ result = (result ?? 0) | (int)field.Constant;
+ }
+ }
+
+ if (result.HasValue)
+ return result.Value;
+
+ throw new XamlParseException(string.Format("Enum value not found for {0}", value), lineInfo);
+ }
+
+ static bool HasTypeConverter(this FieldReference bpRef, ModuleDefinition module,
+ out TypeReference typeConverterReference)
+ {
+ typeConverterReference = null;
+
+ var declaringType = bpRef.DeclaringType;
+ var bpName = bpRef.Name;
+ var pName = bpName.EndsWith("Property", StringComparison.Ordinal) ? bpName.Substring(0, bpName.Length - 8) : bpName;
+ var property = declaringType.Resolve().Properties.FirstOrDefault(p => p.Name == pName);
+ CustomAttribute attr = null;
+
+ if (property != null)
+ {
+ if (property.HasCustomAttributes)
+ {
+ attr =
+ property.CustomAttributes.FirstOrDefault(
+ cad => TypeConverterAttribute.TypeConvertersType.Contains(cad.AttributeType.FullName));
+ }
+ if (attr == null && property.PropertyType.Resolve().HasCustomAttributes)
+ {
+ attr =
+ property.PropertyType.Resolve()
+ .CustomAttributes.FirstOrDefault(
+ cad => TypeConverterAttribute.TypeConvertersType.Contains(cad.AttributeType.FullName));
+ }
+
+ if (attr == null)
+ return false;
+
+ typeConverterReference = attr.ConstructorArguments[0].Value as TypeReference;
+ return true;
+ }
+
+ var getters = bpRef.DeclaringType.GetMethods(md => md.Name == "Get" + pName && md.IsStatic, module).SingleOrDefault();
+ if (getters != null)
+ {
+ if (getters.Item1.HasCustomAttributes)
+ {
+ attr =
+ getters.Item1.CustomAttributes.FirstOrDefault(
+ cad => TypeConverterAttribute.TypeConvertersType.Contains(cad.AttributeType.FullName));
+ }
+ else if (getters.Item1.ReturnType.Resolve().HasCustomAttributes)
+ {
+ attr =
+ getters.Item1.ReturnType.Resolve()
+ .CustomAttributes.FirstOrDefault(
+ cad => TypeConverterAttribute.TypeConvertersType.Contains(cad.AttributeType.FullName));
+ }
+
+ if (attr == null)
+ return false;
+
+ typeConverterReference = attr.ConstructorArguments[0].Value as TypeReference;
+ return true;
+ }
+
+ return false;
+ }
+
+ static TypeReference GetBPReturnType(ILContext context, FieldReference bpRef, IXmlLineInfo lineInfo)
+ {
+ //Find a property with a matching name
+ var name = bpRef.Name;
+ if (!name.EndsWith("Property", StringComparison.Ordinal))
+ return context.Body.Method.Module.TypeSystem.Object;
+ name = name.Substring(0, name.Length - 8);
+
+ //First, check for a property
+ TypeReference declaringTypeRef;
+ var property = bpRef.DeclaringType.GetProperty(pd => pd.Name == name, out declaringTypeRef);
+ if (property != null)
+ return property.PropertyType;
+
+ //Then check for getter or setter (attached BPs)
+ var getters =
+ bpRef.DeclaringType.GetMethods(md => md.Name == "Get" + name && md.IsStatic, context.Body.Method.Module)
+ .SingleOrDefault();
+ if (getters != null)
+ return getters.Item1.ReturnType;
+
+ //throws
+ throw new XamlParseException(
+ string.Format(
+ "Can not find a Property named \"{0}\" or a static method named \"Get{0}\" for BindableProperty \"{1}\"", name,
+ bpRef.Name), lineInfo);
+ }
+
+ public static IEnumerable<Instruction> PushXmlLineInfo(this INode node, ILContext context)
+ {
+ var module = context.Body.Method.Module;
+
+ var xmlLineInfo = node as IXmlLineInfo;
+ if (xmlLineInfo == null)
+ {
+ yield return Instruction.Create(OpCodes.Ldnull);
+ yield break;
+ }
+ MethodReference ctor;
+ if (xmlLineInfo.HasLineInfo())
+ {
+ yield return Instruction.Create(OpCodes.Ldc_I4, xmlLineInfo.LineNumber);
+ yield return Instruction.Create(OpCodes.Ldc_I4, xmlLineInfo.LinePosition);
+ ctor = module.Import(typeof (XmlLineInfo).GetConstructor(new[] { typeof (int), typeof (int) }));
+ }
+ else
+ ctor = module.Import(typeof (XmlLineInfo).GetConstructor(new Type[] { }));
+ yield return Instruction.Create(OpCodes.Newobj, ctor);
+ }
+
+ public static IEnumerable<Instruction> PushParentObjectsArray(this INode node, ILContext context)
+ {
+ var module = context.Body.Method.Module;
+
+ var nodes = new List<IElementNode>();
+ INode n = node.Parent;
+ while (n != null)
+ {
+ var en = n as IElementNode;
+ if (en != null && context.Variables.ContainsKey(en))
+ nodes.Add(en);
+ n = n.Parent;
+ }
+
+ if (nodes.Count == 0 && context.ParentContextValues == null)
+ {
+ yield return Instruction.Create(OpCodes.Ldnull);
+ yield break;
+ }
+
+ if (nodes.Count == 0)
+ {
+ yield return Instruction.Create(OpCodes.Ldarg_0);
+ yield return Instruction.Create(OpCodes.Ldfld, context.ParentContextValues);
+ yield break;
+ }
+
+ //Compute parent object length
+ if (context.ParentContextValues != null)
+ {
+ yield return Instruction.Create(OpCodes.Ldarg_0);
+ yield return Instruction.Create(OpCodes.Ldfld, context.ParentContextValues);
+ yield return Instruction.Create(OpCodes.Ldlen);
+ yield return Instruction.Create(OpCodes.Conv_I4);
+ }
+ else
+ yield return Instruction.Create(OpCodes.Ldc_I4_0);
+ var parentObjectLength = new VariableDefinition(module.TypeSystem.Int32);
+ context.Body.Variables.Add(parentObjectLength);
+ yield return Instruction.Create(OpCodes.Stloc, parentObjectLength);
+
+ //Create the final array
+ yield return Instruction.Create(OpCodes.Ldloc, parentObjectLength);
+ yield return Instruction.Create(OpCodes.Ldc_I4, nodes.Count);
+ yield return Instruction.Create(OpCodes.Add);
+ yield return Instruction.Create(OpCodes.Newarr, module.TypeSystem.Object);
+ var finalArray = new VariableDefinition(module.Import(typeof (object[])));
+ context.Body.Variables.Add(finalArray);
+ yield return Instruction.Create(OpCodes.Stloc, finalArray);
+
+ //Copy original array to final
+ if (context.ParentContextValues != null)
+ {
+ yield return Instruction.Create(OpCodes.Ldarg_0);
+ yield return Instruction.Create(OpCodes.Ldfld, context.ParentContextValues); //sourceArray
+ yield return Instruction.Create(OpCodes.Ldc_I4_0); //sourceIndex
+ yield return Instruction.Create(OpCodes.Ldloc, finalArray); //destinationArray
+ yield return Instruction.Create(OpCodes.Ldc_I4, nodes.Count); //destinationIndex
+ yield return Instruction.Create(OpCodes.Ldloc, parentObjectLength); //length
+ var arrayCopy =
+ module.Import(typeof (Array))
+ .Resolve()
+ .Methods.First(
+ md =>
+ md.Name == "Copy" && md.Parameters.Count == 5 &&
+ md.Parameters[1].ParameterType.FullName == module.TypeSystem.Int32.FullName);
+ yield return Instruction.Create(OpCodes.Call, module.Import(arrayCopy));
+ }
+
+ //Add nodes to array
+ yield return Instruction.Create(OpCodes.Ldloc, finalArray);
+ if (nodes.Count > 0)
+ {
+ for (var i = 0; i < nodes.Count; i++)
+ {
+ var en = nodes[i];
+ yield return Instruction.Create(OpCodes.Dup);
+ yield return Instruction.Create(OpCodes.Ldc_I4, i);
+ yield return Instruction.Create(OpCodes.Ldloc, context.Variables[en]);
+ if (context.Variables[en].VariableType.IsValueType)
+ yield return Instruction.Create(OpCodes.Box, module.Import(context.Variables[en].VariableType));
+ yield return Instruction.Create(OpCodes.Stelem_Ref);
+ }
+ }
+ }
+
+ public static IEnumerable<Instruction> PushServiceProvider(this INode node, ILContext context)
+ {
+ var module = context.Body.Method.Module;
+
+#if NOSERVICEPROVIDER
+ yield return Instruction.Create (OpCodes.Ldnull);
+ yield break;
+ #endif
+
+ var ctorinfo = typeof (XamlServiceProvider).GetConstructor(new Type[] { });
+ var ctor = module.Import(ctorinfo);
+
+ var addServiceInfo = typeof (XamlServiceProvider).GetMethod("Add", new[] { typeof (Type), typeof (object) });
+ var addService = module.Import(addServiceInfo);
+
+ var getTypeFromHandle =
+ module.Import(typeof (Type).GetMethod("GetTypeFromHandle", new[] { typeof (RuntimeTypeHandle) }));
+ var getAssembly = module.Import(typeof (Type).GetProperty("Assembly").GetMethod);
+
+ yield return Instruction.Create(OpCodes.Newobj, ctor);
+
+ //Add a SimpleValueTargetProvider
+ var pushParentIl = node.PushParentObjectsArray(context).ToList();
+ if (pushParentIl[pushParentIl.Count - 1].OpCode != OpCodes.Ldnull)
+ {
+ yield return Instruction.Create(OpCodes.Dup); //Keep the serviceProvider on the stack
+ yield return Instruction.Create(OpCodes.Ldtoken, module.Import(typeof (IProvideValueTarget)));
+ yield return Instruction.Create(OpCodes.Call, module.Import(getTypeFromHandle));
+
+ foreach (var instruction in pushParentIl)
+ yield return instruction;
+
+ var targetProviderCtor =
+ module.Import(typeof (SimpleValueTargetProvider).GetConstructor(new[] { typeof (object[]) }));
+ yield return Instruction.Create(OpCodes.Newobj, targetProviderCtor);
+ yield return Instruction.Create(OpCodes.Callvirt, addService);
+ }
+
+ //Add a NamescopeProvider
+ if (context.Scopes.ContainsKey(node))
+ {
+ yield return Instruction.Create(OpCodes.Dup); //Dupicate the serviceProvider
+ yield return Instruction.Create(OpCodes.Ldtoken, module.Import(typeof (INameScopeProvider)));
+ yield return Instruction.Create(OpCodes.Call, module.Import(getTypeFromHandle));
+ var namescopeProviderCtor = module.Import(typeof (NameScopeProvider).GetConstructor(new Type[] { }));
+ yield return Instruction.Create(OpCodes.Newobj, namescopeProviderCtor);
+ yield return Instruction.Create(OpCodes.Dup); //Duplicate the namescopeProvider
+ var setNamescope = module.Import(typeof (NameScopeProvider).GetProperty("NameScope").GetSetMethod());
+
+ yield return Instruction.Create(OpCodes.Ldloc, context.Scopes[node]);
+ yield return Instruction.Create(OpCodes.Callvirt, setNamescope);
+ yield return Instruction.Create(OpCodes.Callvirt, addService);
+ }
+
+ //Add a XamlTypeResolver
+ if (node.NamespaceResolver != null)
+ {
+ yield return Instruction.Create(OpCodes.Dup); //Dupicate the serviceProvider
+ yield return Instruction.Create(OpCodes.Ldtoken, module.Import(typeof (IXamlTypeResolver)));
+ yield return Instruction.Create(OpCodes.Call, module.Import(getTypeFromHandle));
+ var xmlNamespaceResolverCtor = module.Import(typeof (XmlNamespaceResolver).GetConstructor(new Type[] { }));
+ var addNamespace = module.Import(typeof (XmlNamespaceResolver).GetMethod("Add"));
+ yield return Instruction.Create(OpCodes.Newobj, xmlNamespaceResolverCtor);
+ foreach (var kvp in node.NamespaceResolver.GetNamespacesInScope(XmlNamespaceScope.ExcludeXml))
+ {
+ yield return Instruction.Create(OpCodes.Dup); //dup the resolver
+ yield return Instruction.Create(OpCodes.Ldstr, kvp.Key);
+ yield return Instruction.Create(OpCodes.Ldstr, kvp.Value);
+ yield return Instruction.Create(OpCodes.Callvirt, addNamespace);
+ }
+ yield return Instruction.Create(OpCodes.Ldtoken, context.Body.Method.DeclaringType);
+ yield return Instruction.Create(OpCodes.Call, module.Import(getTypeFromHandle));
+ yield return Instruction.Create(OpCodes.Callvirt, getAssembly);
+ var xtr = module.Import(typeof (XamlTypeResolver)).Resolve();
+ var xamlTypeResolverCtor = module.Import(xtr.Methods.First(md => md.IsConstructor && md.Parameters.Count == 2));
+ yield return Instruction.Create(OpCodes.Newobj, xamlTypeResolverCtor);
+ yield return Instruction.Create(OpCodes.Callvirt, addService);
+ }
+
+ if (node is IXmlLineInfo)
+ {
+ yield return Instruction.Create(OpCodes.Dup); //Dupicate the serviceProvider
+ yield return Instruction.Create(OpCodes.Ldtoken, module.Import(typeof (IXmlLineInfoProvider)));
+ yield return Instruction.Create(OpCodes.Call, module.Import(getTypeFromHandle));
+
+ foreach (var instruction in node.PushXmlLineInfo(context))
+ yield return instruction;
+
+ var lip = module.Import(typeof (XmlLineInfoProvider)).Resolve();
+ var lineInfoProviderCtor = module.Import(lip.Methods.First(md => md.IsConstructor && md.Parameters.Count == 1));
+ yield return Instruction.Create(OpCodes.Newobj, lineInfoProviderCtor);
+ yield return Instruction.Create(OpCodes.Callvirt, addService);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Build.Tasks/Properties/AssemblyInfo.cs b/Xamarin.Forms.Build.Tasks/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000..ea4bda34
--- /dev/null
+++ b/Xamarin.Forms.Build.Tasks/Properties/AssemblyInfo.cs
@@ -0,0 +1,30 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+
+// Information about this assembly is defined by the following attributes.
+// Change them to the values specific to your project.
+//
+
+[assembly: AssemblyTitle("Xamarin.Forms.Build.Tasks")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Xamarin Inc.")]
+[assembly: AssemblyProduct("")]
+[assembly: AssemblyCopyright("Xamarin Inc.")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
+// The Page "{Major}.{Minor}.*" will automatically update the build and revision,
+// and "{Major}.{Minor}.{Build}.*" will update just the revision.
+
+//[assembly: AssemblyVersion("1.0.*")]
+
+// The following attributes are used to specify the signing key for the assembly,
+// if desired. See the Mono documentation for more information about signing.
+
+//[assembly: AssemblyDelaySign(false)]
+//[assembly: AssemblyKeyFile("")]
+
+[assembly: InternalsVisibleTo("xamlg")]
+[assembly: InternalsVisibleTo("Xamarin.Forms.Xaml.UnitTests")] \ No newline at end of file
diff --git a/Xamarin.Forms.Build.Tasks/PropertyDefinitionExtensions.cs b/Xamarin.Forms.Build.Tasks/PropertyDefinitionExtensions.cs
new file mode 100644
index 00000000..472a5658
--- /dev/null
+++ b/Xamarin.Forms.Build.Tasks/PropertyDefinitionExtensions.cs
@@ -0,0 +1,33 @@
+using Mono.Cecil;
+
+namespace Xamarin.Forms.Build.Tasks
+{
+ static class PropertyDefinitionExtensions
+ {
+ // public static PropertyDefinition MakeGeneric (this PropertyDefinition self, GenericInstanceType declaringTypeReference)
+ // {
+ // if (declaringTypeReference == null)
+ // throw new ArgumentNullException ("declaringTypeReference");
+ // if (self == null)
+ // throw new ArgumentNullException ("self");
+ //
+ // var propertyType = declaringTypeReference.GenericArguments[((GenericParameter)self.PropertyType).Position];
+ // self.PropertyType = propertyType;
+ // self.SetMethod = self.SetMethod.MakeGeneric (propertyType).Resolve ();
+ // self.GetMethod.ReturnType = propertyType;
+ //
+ // return self;
+ // }
+
+ public static TypeReference ResolveGenericPropertyType(this PropertyDefinition self,
+ TypeReference declaringTypeReference)
+ {
+ if (self.PropertyType.IsGenericParameter)
+ {
+ return
+ ((GenericInstanceType)declaringTypeReference).GenericArguments[((GenericParameter)self.PropertyType).Position];
+ }
+ return self.PropertyType;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Build.Tasks/SetFieldVisitor.cs b/Xamarin.Forms.Build.Tasks/SetFieldVisitor.cs
new file mode 100644
index 00000000..1839cf7b
--- /dev/null
+++ b/Xamarin.Forms.Build.Tasks/SetFieldVisitor.cs
@@ -0,0 +1,72 @@
+using System.Linq;
+using Mono.Cecil.Cil;
+using Xamarin.Forms.Xaml;
+
+namespace Xamarin.Forms.Build.Tasks
+{
+ class SetFieldVisitor : IXamlNodeVisitor
+ {
+ public SetFieldVisitor(ILContext context)
+ {
+ Context = context;
+ }
+
+ public ILContext Context { get; }
+
+ public bool VisitChildrenFirst
+ {
+ get { return false; }
+ }
+
+ public bool StopOnDataTemplate
+ {
+ get { return true; }
+ }
+
+ public bool StopOnResourceDictionary
+ {
+ get { return false; }
+ }
+
+ public void Visit(ValueNode node, INode parentNode)
+ {
+ if (!IsXNameProperty(node, parentNode))
+ return;
+ if (!(parentNode is RootNode))
+ {
+ //no variable assigned for root
+ var field = Context.Body.Method.DeclaringType.Fields.SingleOrDefault(fd => fd.Name == (string)node.Value);
+ if (field == null)
+ return;
+ Context.IL.Emit(OpCodes.Ldarg_0);
+ Context.IL.Emit(OpCodes.Ldloc, Context.Variables[(IElementNode)parentNode]);
+ Context.IL.Emit(OpCodes.Stfld, field);
+ }
+ }
+
+ public void Visit(MarkupNode node, INode parentNode)
+ {
+ }
+
+ public void Visit(ElementNode node, INode parentNode)
+ {
+ }
+
+ public void Visit(RootNode node, INode parentNode)
+ {
+ }
+
+ public void Visit(ListNode node, INode parentNode)
+ {
+ }
+
+ static bool IsXNameProperty(ValueNode node, INode parentNode)
+ {
+ var parentElement = parentNode as IElementNode;
+ INode xNameNode;
+ if (parentElement != null && parentElement.Properties.TryGetValue(XmlName.xName, out xNameNode) && xNameNode == node)
+ return true;
+ return false;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Build.Tasks/SetNamescopesAndRegisterNamesVisitor.cs b/Xamarin.Forms.Build.Tasks/SetNamescopesAndRegisterNamesVisitor.cs
new file mode 100644
index 00000000..477328e2
--- /dev/null
+++ b/Xamarin.Forms.Build.Tasks/SetNamescopesAndRegisterNamesVisitor.cs
@@ -0,0 +1,142 @@
+using System.Linq;
+using Mono.Cecil.Cil;
+using Xamarin.Forms.Internals;
+using Xamarin.Forms.Xaml;
+
+namespace Xamarin.Forms.Build.Tasks
+{
+ class SetNamescopesAndRegisterNamesVisitor : IXamlNodeVisitor
+ {
+ public SetNamescopesAndRegisterNamesVisitor(ILContext context)
+ {
+ Context = context;
+ }
+
+ ILContext Context { get; }
+
+ public bool VisitChildrenFirst
+ {
+ get { return false; }
+ }
+
+ public bool StopOnDataTemplate
+ {
+ get { return true; }
+ }
+
+ public bool StopOnResourceDictionary
+ {
+ get { return false; }
+ }
+
+ public void Visit(ValueNode node, INode parentNode)
+ {
+ Context.Scopes[node] = Context.Scopes[parentNode];
+ if (IsXNameProperty(node, parentNode))
+ RegisterName((string)node.Value, Context.Scopes[node], Context.Variables[(IElementNode)parentNode], node);
+ }
+
+ public void Visit(MarkupNode node, INode parentNode)
+ {
+ Context.Scopes[node] = Context.Scopes[parentNode];
+ }
+
+ public void Visit(ElementNode node, INode parentNode)
+ {
+ if (node.SkipPrefix((node.NamespaceResolver ?? parentNode.NamespaceResolver).LookupPrefix(node.NamespaceURI)))
+ return;
+
+ VariableDefinition ns;
+ if (parentNode == null || IsDataTemplate(node, parentNode) || IsStyle(node, parentNode))
+ ns = CreateNamescope();
+ else
+ ns = Context.Scopes[parentNode];
+ if (
+ Context.Variables[node].VariableType.InheritsFromOrImplements(
+ Context.Body.Method.Module.Import(typeof (BindableObject))))
+ SetNameScope(node, ns);
+ Context.Scopes[node] = ns;
+ }
+
+ public void Visit(RootNode node, INode parentNode)
+ {
+ var ns = CreateNamescope();
+ if (
+ Context.Variables[node].VariableType.InheritsFromOrImplements(
+ Context.Body.Method.Module.Import(typeof (BindableObject))))
+ SetNameScope(node, ns);
+ Context.Scopes[node] = ns;
+ }
+
+ public void Visit(ListNode node, INode parentNode)
+ {
+ Context.Scopes[node] = Context.Scopes[parentNode];
+ }
+
+ static bool IsDataTemplate(INode node, INode parentNode)
+ {
+ var parentElement = parentNode as IElementNode;
+ INode createContent;
+ if (parentElement != null && parentElement.Properties.TryGetValue(XmlName._CreateContent, out createContent) &&
+ createContent == node)
+ return true;
+ return false;
+ }
+
+ static bool IsStyle(INode node, INode parentNode)
+ {
+ var pnode = parentNode as ElementNode;
+ return pnode != null && pnode.XmlType.Name == "Style";
+ }
+
+ static bool IsXNameProperty(ValueNode node, INode parentNode)
+ {
+ var parentElement = parentNode as IElementNode;
+ INode xNameNode;
+ if (parentElement != null && parentElement.Properties.TryGetValue(XmlName.xName, out xNameNode) && xNameNode == node)
+ return true;
+ return false;
+ }
+
+ VariableDefinition CreateNamescope()
+ {
+ var module = Context.Body.Method.Module;
+ var nsRef = module.Import(typeof (NameScope));
+ var vardef = new VariableDefinition(nsRef);
+ Context.Body.Variables.Add(vardef);
+ var nsDef = nsRef.Resolve();
+ var ctorinfo = nsDef.Methods.First(md => md.IsConstructor && !md.HasParameters);
+ var ctor = module.Import(ctorinfo);
+ Context.IL.Emit(OpCodes.Newobj, ctor);
+ Context.IL.Emit(OpCodes.Stloc, vardef);
+ return vardef;
+ }
+
+ void SetNameScope(ElementNode node, VariableDefinition ns)
+ {
+ var module = Context.Body.Method.Module;
+ var nsRef = module.Import(typeof (NameScope));
+ var nsDef = nsRef.Resolve();
+ var setNSInfo = nsDef.Methods.First(md => md.Name == "SetNameScope" && md.IsStatic);
+ var setNS = module.Import(setNSInfo);
+ Context.IL.Emit(OpCodes.Ldloc, Context.Variables[node]);
+ Context.IL.Emit(OpCodes.Ldloc, ns);
+ Context.IL.Emit(OpCodes.Call, setNS);
+ }
+
+ void RegisterName(string str, VariableDefinition scope, VariableDefinition element, INode node)
+ {
+ var module = Context.Body.Method.Module;
+ var nsRef = module.Import(typeof (INameScope));
+ var nsDef = nsRef.Resolve();
+ var registerInfo = nsDef.Methods.First(md => md.Name == "RegisterName" && md.Parameters.Count == 3);
+ var register = module.Import(registerInfo);
+
+ Context.IL.Emit(OpCodes.Ldloc, scope);
+ Context.IL.Emit(OpCodes.Ldstr, str);
+ Context.IL.Emit(OpCodes.Ldloc, element);
+ Context.IL.Append(node.PushXmlLineInfo(Context));
+ Context.IL.Emit(OpCodes.Callvirt, register);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Build.Tasks/SetPropertiesVisitor.cs b/Xamarin.Forms.Build.Tasks/SetPropertiesVisitor.cs
new file mode 100644
index 00000000..5470a3c8
--- /dev/null
+++ b/Xamarin.Forms.Build.Tasks/SetPropertiesVisitor.cs
@@ -0,0 +1,681 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Xml;
+using Mono.Cecil;
+using Mono.Cecil.Cil;
+using Mono.Cecil.Rocks;
+using Xamarin.Forms.Internals;
+using Xamarin.Forms.Xaml;
+
+namespace Xamarin.Forms.Build.Tasks
+{
+ class SetPropertiesVisitor : IXamlNodeVisitor
+ {
+ static int dtcount;
+
+ readonly IList<XmlName> skips = new List<XmlName>
+ {
+ XmlName.xKey,
+ XmlName.xTypeArguments,
+ XmlName.xArguments,
+ XmlName.xFactoryMethod,
+ XmlName.xName
+ };
+
+ public SetPropertiesVisitor(ILContext context, bool stopOnResourceDictionary = false)
+ {
+ Context = context;
+ Module = context.Body.Method.Module;
+ StopOnResourceDictionary = stopOnResourceDictionary;
+ }
+
+ public ILContext Context { get; }
+
+ ModuleDefinition Module { get; }
+
+ public bool VisitChildrenFirst
+ {
+ get { return true; }
+ }
+
+ public bool StopOnDataTemplate
+ {
+ get { return true; }
+ }
+
+ public bool StopOnResourceDictionary { get; }
+
+ public void Visit(ValueNode node, INode parentNode)
+ {
+ //TODO support Label text as element
+ XmlName propertyName;
+ if (!TryGetPropertyName(node, parentNode, out propertyName))
+ {
+ if (!IsCollectionItem(node, parentNode))
+ return;
+ string contentProperty;
+ if (!Context.Variables.ContainsKey((IElementNode)parentNode))
+ return;
+ var parentVar = Context.Variables[(IElementNode)parentNode];
+ if ((contentProperty = GetContentProperty(parentVar.VariableType)) != null)
+ propertyName = new XmlName(((IElementNode)parentNode).NamespaceURI, contentProperty);
+ else
+ return;
+ }
+
+ if (skips.Contains(propertyName))
+ return;
+ if (node.SkipPrefix((node.NamespaceResolver ?? parentNode.NamespaceResolver)?.LookupPrefix(propertyName.NamespaceURI)))
+ return;
+ if (propertyName.NamespaceURI == "http://schemas.openxmlformats.org/markup-compatibility/2006" &&
+ propertyName.LocalName == "Ignorable")
+ {
+ (parentNode.IgnorablePrefixes ?? (parentNode.IgnorablePrefixes = new List<string>())).AddRange(
+ (node.Value as string).Split(','));
+ return;
+ }
+ SetPropertyValue(Context.Variables[(IElementNode)parentNode], propertyName, node, Context, node);
+ }
+
+ public void Visit(MarkupNode node, INode parentNode)
+ {
+ }
+
+ public void Visit(ElementNode node, INode parentNode)
+ {
+ if (node.SkipPrefix((node.NamespaceResolver ?? parentNode.NamespaceResolver)?.LookupPrefix(node.NamespaceURI)))
+ return;
+
+ //if this node is an IMarkupExtension, invoke ProvideValue() and replace the variable
+ var vardef = Context.Variables[node];
+ var vardefref = new VariableDefinitionReference(vardef);
+ Context.IL.Append(ProvideValue(vardefref, Context, Module, node));
+ if (vardef != vardefref.VariableDefinition)
+ {
+ vardef = vardefref.VariableDefinition;
+ Context.Body.Variables.Add(vardef);
+ Context.Variables[node] = vardef;
+ }
+
+ XmlName propertyName;
+ if (TryGetPropertyName(node, parentNode, out propertyName))
+ {
+ if (skips.Contains(propertyName))
+ return;
+
+ if (propertyName == XmlName._CreateContent)
+ SetDataTemplate((IElementNode)parentNode, node, Context, node);
+ else
+ SetPropertyValue(Context.Variables[(IElementNode)parentNode], propertyName, node, Context, node);
+ }
+ else if (IsCollectionItem(node, parentNode) && parentNode is IElementNode)
+ {
+ // Collection element, implicit content, or implicit collection element.
+ string contentProperty;
+ var parentVar = Context.Variables[(IElementNode)parentNode];
+ if (parentVar.VariableType.ImplementsInterface(Module.Import(typeof (IEnumerable))))
+ {
+ var elementType = parentVar.VariableType;
+ if (elementType.FullName != "Xamarin.Forms.ResourceDictionary")
+ {
+ var adderTuple = elementType.GetMethods(md => md.Name == "Add" && md.Parameters.Count == 1, Module).First();
+ var adderRef = Module.Import(adderTuple.Item1);
+ adderRef = Module.Import(adderRef.ResolveGenericParameters(adderTuple.Item2, Module));
+
+ Context.IL.Emit(OpCodes.Ldloc, parentVar);
+ Context.IL.Emit(OpCodes.Ldloc, vardef);
+ Context.IL.Emit(OpCodes.Callvirt, adderRef);
+ if (adderRef.ReturnType.FullName != "System.Void")
+ Context.IL.Emit(OpCodes.Pop);
+ }
+ }
+ else if ((contentProperty = GetContentProperty(parentVar.VariableType)) != null)
+ {
+ var name = new XmlName(node.NamespaceURI, contentProperty);
+ if (skips.Contains(name))
+ return;
+ SetPropertyValue(Context.Variables[(IElementNode)parentNode], name, node, Context, node);
+ }
+ }
+ else if (IsCollectionItem(node, parentNode) && parentNode is ListNode)
+ {
+ // IL_000d: ldloc.2
+ // IL_000e: callvirt instance class [mscorlib]System.Collections.Generic.IList`1<!0> class [Xamarin.Forms.Core]Xamarin.Forms.Layout`1<class [Xamarin.Forms.Core]Xamarin.Forms.View>::get_Children()
+ // IL_0013: ldloc.0
+ // IL_0014: callvirt instance void class [mscorlib]System.Collections.Generic.ICollection`1<class [Xamarin.Forms.Core]Xamarin.Forms.View>::Add(!0)
+
+ var parentList = (ListNode)parentNode;
+ var parent = Context.Variables[((IElementNode)parentNode.Parent)];
+
+ if (skips.Contains(parentList.XmlName))
+ return;
+
+ var elementType = parent.VariableType;
+ var localname = parentList.XmlName.LocalName;
+
+ GetNameAndTypeRef(ref elementType, parentList.XmlName.NamespaceURI, ref localname, Context, node);
+
+ TypeReference propertyDeclaringType;
+ var property = elementType.GetProperty(pd => pd.Name == localname, out propertyDeclaringType);
+ MethodDefinition propertyGetter;
+
+ if (property != null && (propertyGetter = property.GetMethod) != null && propertyGetter.IsPublic)
+ {
+ var propertyGetterRef = Module.Import(propertyGetter);
+ propertyGetterRef = Module.Import(propertyGetterRef.ResolveGenericParameters(propertyDeclaringType, Module));
+ var propertyType = propertyGetterRef.ReturnType.ResolveGenericParameters(propertyDeclaringType);
+
+ var adderTuple = propertyType.GetMethods(md => md.Name == "Add" && md.Parameters.Count == 1, Module).First();
+ var adderRef = Module.Import(adderTuple.Item1);
+ adderRef = Module.Import(adderRef.ResolveGenericParameters(adderTuple.Item2, Module));
+
+ Context.IL.Emit(OpCodes.Ldloc, parent);
+ Context.IL.Emit(OpCodes.Callvirt, propertyGetterRef);
+ Context.IL.Emit(OpCodes.Ldloc, vardef);
+ Context.IL.Emit(OpCodes.Callvirt, adderRef);
+ if (adderRef.ReturnType.FullName != "System.Void")
+ Context.IL.Emit(OpCodes.Pop);
+ }
+ }
+ }
+
+ public void Visit(RootNode node, INode parentNode)
+ {
+ }
+
+ public void Visit(ListNode node, INode parentNode)
+ {
+ }
+
+ public static bool TryGetPropertyName(INode node, INode parentNode, out XmlName name)
+ {
+ name = default(XmlName);
+ var parentElement = parentNode as IElementNode;
+ if (parentElement == null)
+ return false;
+ foreach (var kvp in parentElement.Properties)
+ {
+ if (kvp.Value != node)
+ continue;
+ name = kvp.Key;
+ return true;
+ }
+ return false;
+ }
+
+ static bool IsCollectionItem(INode node, INode parentNode)
+ {
+ var parentList = parentNode as IListNode;
+ if (parentList == null)
+ return false;
+ return parentList.CollectionItems.Contains(node);
+ }
+
+ static string GetContentProperty(TypeReference typeRef)
+ {
+ var typeDef = typeRef.Resolve();
+ var attributes = typeDef.CustomAttributes;
+ var attr =
+ attributes.FirstOrDefault(cad => ContentPropertyAttribute.ContentPropertyTypes.Contains(cad.AttributeType.FullName));
+ if (attr != null)
+ return attr.ConstructorArguments[0].Value as string;
+ if (typeDef.BaseType == null)
+ return null;
+ return GetContentProperty(typeDef.BaseType);
+ }
+
+ public static IEnumerable<Instruction> ProvideValue(VariableDefinitionReference vardefref, ILContext context,
+ ModuleDefinition module, ElementNode node)
+ {
+ GenericInstanceType markupExtension;
+ IList<TypeReference> genericArguments;
+ if (vardefref.VariableDefinition.VariableType.FullName == "Xamarin.Forms.Xaml.ArrayExtension" &&
+ vardefref.VariableDefinition.VariableType.ImplementsGenericInterface("Xamarin.Forms.Xaml.IMarkupExtension`1",
+ out markupExtension, out genericArguments))
+ {
+ var markExt = markupExtension.Resolve();
+ var provideValueInfo = markExt.Methods.First(md => md.Name == "ProvideValue");
+ var provideValue = module.Import(provideValueInfo);
+ provideValue =
+ module.Import(provideValue.MakeGeneric(markupExtension.GenericArguments.Select(tr => module.Import(tr)).ToArray()));
+
+ var typeNode = node.Properties[new XmlName("", "Type")];
+ TypeReference arrayTypeRef;
+ if (context.TypeExtensions.TryGetValue(typeNode, out arrayTypeRef))
+ vardefref.VariableDefinition = new VariableDefinition(module.Import(arrayTypeRef.MakeArrayType()));
+ else
+ vardefref.VariableDefinition = new VariableDefinition(module.Import(genericArguments.First()));
+ yield return Instruction.Create(OpCodes.Ldloc, context.Variables[node]);
+ foreach (var instruction in node.PushServiceProvider(context))
+ yield return instruction;
+ yield return Instruction.Create(OpCodes.Callvirt, provideValue);
+
+ if (arrayTypeRef != null)
+ yield return Instruction.Create(OpCodes.Castclass, module.Import(arrayTypeRef.MakeArrayType()));
+ yield return Instruction.Create(OpCodes.Stloc, vardefref.VariableDefinition);
+ }
+ else if (vardefref.VariableDefinition.VariableType.ImplementsGenericInterface("Xamarin.Forms.Xaml.IMarkupExtension`1",
+ out markupExtension, out genericArguments))
+ {
+ var markExt = markupExtension.Resolve();
+ var provideValueInfo = markExt.Methods.First(md => md.Name == "ProvideValue");
+ var provideValue = module.Import(provideValueInfo);
+ provideValue =
+ module.Import(provideValue.MakeGeneric(markupExtension.GenericArguments.Select(tr => module.Import(tr)).ToArray()));
+
+ vardefref.VariableDefinition = new VariableDefinition(module.Import(genericArguments.First()));
+ yield return Instruction.Create(OpCodes.Ldloc, context.Variables[node]);
+ foreach (var instruction in node.PushServiceProvider(context))
+ yield return instruction;
+ yield return Instruction.Create(OpCodes.Callvirt, provideValue);
+ yield return Instruction.Create(OpCodes.Stloc, vardefref.VariableDefinition);
+ }
+ else if (context.Variables[node].VariableType.ImplementsInterface(module.Import(typeof (IMarkupExtension))))
+ {
+ var markExt = module.Import(typeof (IMarkupExtension)).Resolve();
+ var provideValueInfo = markExt.Methods.First(md => md.Name == "ProvideValue");
+ var provideValue = module.Import(provideValueInfo);
+
+ vardefref.VariableDefinition = new VariableDefinition(module.TypeSystem.Object);
+ yield return Instruction.Create(OpCodes.Ldloc, context.Variables[node]);
+ foreach (var instruction in node.PushServiceProvider(context))
+ yield return instruction;
+ yield return Instruction.Create(OpCodes.Callvirt, provideValue);
+ yield return Instruction.Create(OpCodes.Stloc, vardefref.VariableDefinition);
+ }
+ else if (context.Variables[node].VariableType.ImplementsInterface(module.Import(typeof (IValueProvider))))
+ {
+ var markExt = module.Import(typeof (IValueProvider)).Resolve();
+ var provideValueInfo = markExt.Methods.First(md => md.Name == "ProvideValue");
+ var provideValue = module.Import(provideValueInfo);
+
+ vardefref.VariableDefinition = new VariableDefinition(module.TypeSystem.Object);
+ yield return Instruction.Create(OpCodes.Ldloc, context.Variables[node]);
+ foreach (var instruction in node.PushServiceProvider(context))
+ yield return instruction;
+ yield return Instruction.Create(OpCodes.Callvirt, provideValue);
+ yield return Instruction.Create(OpCodes.Stloc, vardefref.VariableDefinition);
+ }
+ }
+
+ public static void SetPropertyValue(VariableDefinition parent, XmlName propertyName, INode valueNode,
+ ILContext context, IXmlLineInfo iXmlLineInfo)
+ {
+ var elementType = parent.VariableType;
+ var localName = propertyName.LocalName;
+ var module = context.Body.Method.Module;
+ var br = Instruction.Create(OpCodes.Nop);
+ TypeReference declaringTypeReference;
+ var handled = false;
+
+ //If it's an attached BP, update elementType and propertyName
+ var attached = GetNameAndTypeRef(ref elementType, propertyName.NamespaceURI, ref localName, context, iXmlLineInfo);
+
+ //If the target is an event, connect
+ // IL_0007: ldloc.0
+ // IL_0008: ldarg.0
+ // IL_0009: ldftn instance void class Xamarin.Forms.Xaml.XamlcTests.MyPage::OnButtonClicked(object, class [mscorlib]System.EventArgs)
+ // IL_000f: newobj instance void class [mscorlib]System.EventHandler::'.ctor'(object, native int)
+ // IL_0014: callvirt instance void class [Xamarin.Forms.Core]Xamarin.Forms.Button::add_Clicked(class [mscorlib]System.EventHandler)
+
+ var eventinfo = elementType.GetEvent(ed => ed.Name == localName);
+ if (eventinfo != null)
+ {
+ var value = ((ValueNode)valueNode).Value;
+
+ context.IL.Emit(OpCodes.Ldloc, parent);
+ if (context.Root is VariableDefinition)
+ context.IL.Emit(OpCodes.Ldloc, context.Root as VariableDefinition);
+ else if (context.Root is FieldDefinition)
+ {
+ context.IL.Emit(OpCodes.Ldarg_0);
+ context.IL.Emit(OpCodes.Ldfld, context.Root as FieldDefinition);
+ }
+ else
+ throw new InvalidProgramException();
+ var declaringType = context.Body.Method.DeclaringType;
+ if (declaringType.IsNested)
+ declaringType = declaringType.DeclaringType;
+ var handler = declaringType.AllMethods().FirstOrDefault(md => md.Name == value as string);
+ if (handler == null)
+ {
+ throw new XamlParseException(
+ string.Format("EventHandler \"{0}\" not found in type \"{1}\"", value, context.Body.Method.DeclaringType.FullName),
+ iXmlLineInfo);
+ }
+ context.IL.Emit(OpCodes.Ldftn, handler);
+ //FIXME: eventually get the right ctor instead fo the First() one, just in case another one could exists (not even sure it's possible).
+ var ctor = module.Import(eventinfo.EventType.Resolve().GetConstructors().First());
+ ctor = ctor.ResolveGenericParameters(eventinfo.EventType, module);
+ context.IL.Emit(OpCodes.Newobj, module.Import(ctor));
+ context.IL.Emit(OpCodes.Callvirt, module.Import(eventinfo.AddMethod));
+ return;
+ }
+
+ FieldReference bpRef = elementType.GetField(fd => fd.Name == localName + "Property" && fd.IsStatic && fd.IsPublic,
+ out declaringTypeReference);
+ if (bpRef != null)
+ {
+ bpRef = module.Import(bpRef.ResolveGenericParameters(declaringTypeReference));
+ bpRef.FieldType = module.Import(bpRef.FieldType);
+ }
+
+ //If Value is DynamicResource, SetDynamicResource
+ VariableDefinition varValue;
+ if (bpRef != null && valueNode is IElementNode &&
+ context.Variables.TryGetValue(valueNode as IElementNode, out varValue) &&
+ varValue.VariableType.FullName == typeof (DynamicResource).FullName)
+ {
+ var setDynamicResource =
+ module.Import(typeof (IDynamicResourceHandler)).Resolve().Methods.First(m => m.Name == "SetDynamicResource");
+ var getKey = typeof (DynamicResource).GetProperty("Key").GetMethod;
+
+ context.IL.Emit(OpCodes.Ldloc, parent);
+ context.IL.Emit(OpCodes.Ldsfld, bpRef);
+ context.IL.Emit(OpCodes.Ldloc, varValue);
+ context.IL.Emit(OpCodes.Callvirt, module.Import(getKey));
+ context.IL.Emit(OpCodes.Callvirt, module.Import(setDynamicResource));
+ return;
+ }
+
+ //If Value is a BindingBase and target is a BP, SetBinding
+ if (bpRef != null && valueNode is IElementNode &&
+ context.Variables.TryGetValue(valueNode as IElementNode, out varValue) &&
+ varValue.VariableType.InheritsFromOrImplements(module.Import(typeof (BindingBase))))
+ {
+ //TODO: check if parent is a BP
+ var setBinding = typeof (BindableObject).GetMethod("SetBinding",
+ new[] { typeof (BindableProperty), typeof (BindingBase) });
+
+ context.IL.Emit(OpCodes.Ldloc, parent);
+ context.IL.Emit(OpCodes.Ldsfld, bpRef);
+ context.IL.Emit(OpCodes.Ldloc, varValue);
+ context.IL.Emit(OpCodes.Callvirt, module.Import(setBinding));
+ return;
+ }
+
+ //If it's a BP, SetValue ()
+ // IL_0007: ldloc.0
+ // IL_0008: ldsfld class [Xamarin.Forms.Core]Xamarin.Forms.BindableProperty [Xamarin.Forms.Core]Xamarin.Forms.Label::TextProperty
+ // IL_000d: ldstr "foo"
+ // IL_0012: callvirt instance void class [Xamarin.Forms.Core]Xamarin.Forms.BindableObject::SetValue(class [Xamarin.Forms.Core]Xamarin.Forms.BindableProperty, object)
+ if (bpRef != null)
+ {
+ //TODO: check if parent is a BP
+ var setValue = typeof (BindableObject).GetMethod("SetValue", new[] { typeof (BindableProperty), typeof (object) });
+
+ if (valueNode is ValueNode)
+ {
+ context.IL.Emit(OpCodes.Ldloc, parent);
+ context.IL.Emit(OpCodes.Ldsfld, bpRef);
+ context.IL.Append(((ValueNode)valueNode).PushConvertedValue(context, bpRef, valueNode.PushServiceProvider(context),
+ true, false));
+ context.IL.Emit(OpCodes.Callvirt, module.Import(setValue));
+ return;
+ }
+ if (valueNode is IElementNode)
+ {
+ var getPropertyReturnType = module.Import(typeof (BindableProperty).GetProperty("ReturnType").GetGetMethod());
+ //FIXME: this should check for inheritance too
+ var isInstanceOfType = module.Import(typeof (Type).GetMethod("IsInstanceOfType", new[] { typeof (object) }));
+ var brfalse = Instruction.Create(OpCodes.Nop);
+
+ context.IL.Emit(OpCodes.Ldsfld, bpRef);
+ context.IL.Emit(OpCodes.Call, getPropertyReturnType);
+ context.IL.Emit(OpCodes.Ldloc, context.Variables[valueNode as IElementNode]);
+ if (context.Variables[valueNode as IElementNode].VariableType.IsValueType)
+ context.IL.Emit(OpCodes.Box, context.Variables[valueNode as IElementNode].VariableType);
+ context.IL.Emit(OpCodes.Callvirt, isInstanceOfType);
+ context.IL.Emit(OpCodes.Brfalse, brfalse);
+ context.IL.Emit(OpCodes.Ldloc, parent);
+ context.IL.Emit(OpCodes.Ldsfld, bpRef);
+ context.IL.Emit(OpCodes.Ldloc, context.Variables[(IElementNode)valueNode]);
+ if (context.Variables[valueNode as IElementNode].VariableType.IsValueType)
+ context.IL.Emit(OpCodes.Box, context.Variables[valueNode as IElementNode].VariableType);
+ context.IL.Emit(OpCodes.Callvirt, module.Import(setValue));
+ context.IL.Emit(OpCodes.Br, br);
+ context.IL.Append(brfalse);
+ handled = true;
+ }
+ }
+
+ //If it's a property, set it
+ // IL_0007: ldloc.0
+ // IL_0008: ldstr "foo"
+ // IL_000d: callvirt instance void class [Xamarin.Forms.Core]Xamarin.Forms.Label::set_Text(string)
+ PropertyDefinition property = elementType.GetProperty(pd => pd.Name == localName, out declaringTypeReference);
+ MethodDefinition propertySetter;
+ if (property != null && (propertySetter = property.SetMethod) != null && propertySetter.IsPublic)
+ {
+ module.Import(elementType.Resolve());
+ var propertySetterRef =
+ module.Import(module.Import(propertySetter).ResolveGenericParameters(declaringTypeReference, module));
+ propertySetterRef.ImportTypes(module);
+ var propertyType = property.ResolveGenericPropertyType(declaringTypeReference);
+ ValueNode vnode = null;
+
+ if ((vnode = valueNode as ValueNode) != null)
+ {
+ context.IL.Emit(OpCodes.Ldloc, parent);
+ context.IL.Append(vnode.PushConvertedValue(context,
+ propertyType,
+ new ICustomAttributeProvider[] { propertyType.Resolve() },
+ valueNode.PushServiceProvider(context), false, true));
+ context.IL.Emit(OpCodes.Callvirt, propertySetterRef);
+
+ context.IL.Append(br);
+ return;
+ }
+ if (valueNode is IElementNode)
+ {
+ var vardef = context.Variables[(ElementNode)valueNode];
+ var implicitOperators =
+ vardef.VariableType.GetMethods(md => md.IsPublic && md.IsStatic && md.IsSpecialName && md.Name == "op_Implicit",
+ module).ToList();
+ MethodReference implicitOperator = null;
+ if (implicitOperators.Any())
+ {
+ foreach (var op in implicitOperators)
+ {
+ var cast = op.Item1;
+ var opDeclTypeRef = op.Item2;
+ var castDef = module.Import(cast).ResolveGenericParameters(opDeclTypeRef, module);
+ var returnType = castDef.ReturnType;
+ if (returnType.IsGenericParameter)
+ returnType = ((GenericInstanceType)opDeclTypeRef).GenericArguments[((GenericParameter)returnType).Position];
+ if (returnType.FullName == propertyType.FullName &&
+ cast.Parameters[0].ParameterType.Name == vardef.VariableType.Name)
+ {
+ implicitOperator = castDef;
+ break;
+ }
+ }
+ }
+
+ //TODO replace latest check by a runtime type check
+ if (implicitOperator != null || vardef.VariableType.InheritsFromOrImplements(propertyType) ||
+ propertyType.FullName == "System.Object" || vardef.VariableType.FullName == "System.Object")
+ {
+ context.IL.Emit(OpCodes.Ldloc, parent);
+ context.IL.Emit(OpCodes.Ldloc, vardef);
+ if (implicitOperator != null)
+ {
+ // IL_000f: call !0 class [Xamarin.Forms.Core]Xamarin.Forms.OnPlatform`1<bool>::op_Implicit(class [Xamarin.Forms.Core]Xamarin.Forms.OnPlatform`1<!0>)
+ context.IL.Emit(OpCodes.Call, module.Import(implicitOperator));
+ }
+ else if (!vardef.VariableType.IsValueType && propertyType.IsValueType)
+ context.IL.Emit(OpCodes.Unbox_Any, module.Import(propertyType));
+ else if (vardef.VariableType.IsValueType && propertyType.FullName == "System.Object")
+ context.IL.Emit(OpCodes.Box, vardef.VariableType);
+ context.IL.Emit(OpCodes.Callvirt, propertySetterRef);
+ context.IL.Append(br);
+ return;
+ }
+ handled = true;
+ }
+ }
+
+ //If it's an already initialized property, add to it
+ MethodDefinition propertyGetter;
+ //TODO: check if property is assigned
+ if (property != null && (propertyGetter = property.GetMethod) != null && propertyGetter.IsPublic)
+ {
+ var propertyGetterRef = module.Import(propertyGetter);
+ propertyGetterRef = module.Import(propertyGetterRef.ResolveGenericParameters(declaringTypeReference, module));
+ var propertyType = propertyGetterRef.ReturnType.ResolveGenericParameters(declaringTypeReference);
+
+ //TODO check md.Parameters[0] type
+ var adderTuple =
+ propertyType.GetMethods(md => md.Name == "Add" && md.Parameters.Count == 1, module).FirstOrDefault();
+ if (adderTuple != null)
+ {
+ var adderRef = module.Import(adderTuple.Item1);
+ adderRef = module.Import(adderRef.ResolveGenericParameters(adderTuple.Item2, module));
+
+ if (valueNode is IElementNode)
+ {
+ context.IL.Emit(OpCodes.Ldloc, parent);
+ context.IL.Emit(OpCodes.Callvirt, propertyGetterRef);
+ context.IL.Emit(OpCodes.Ldloc, context.Variables[(ElementNode)valueNode]);
+ context.IL.Emit(OpCodes.Callvirt, adderRef);
+ if (adderRef.ReturnType.FullName != "System.Void")
+ context.IL.Emit(OpCodes.Pop);
+ context.IL.Append(br);
+ return;
+ }
+ }
+ }
+ if (!handled)
+ {
+ throw new XamlParseException(string.Format("No property, bindable property, or event found for '{0}'", localName),
+ iXmlLineInfo);
+ }
+ context.IL.Append(br);
+ }
+
+ static bool GetNameAndTypeRef(ref TypeReference elementType, string namespaceURI, ref string localname,
+ ILContext context, IXmlLineInfo lineInfo)
+ {
+ var dotIdx = localname.IndexOf('.');
+ if (dotIdx > 0)
+ {
+ var typename = localname.Substring(0, dotIdx);
+ localname = localname.Substring(dotIdx + 1);
+ elementType = new XmlType(namespaceURI, typename, null).GetTypeReference(context.Body.Method.Module, lineInfo);
+ return true;
+ }
+ return false;
+ }
+
+ static void SetDataTemplate(IElementNode parentNode, ElementNode node, ILContext parentContext,
+ IXmlLineInfo xmlLineInfo)
+ {
+ var parentVar = parentContext.Variables[parentNode];
+ //Push the DataTemplate to the stack, for setting the template
+ parentContext.IL.Emit(OpCodes.Ldloc, parentVar);
+
+ //Create nested class
+ // .class nested private auto ansi sealed beforefieldinit '<Main>c__AnonStorey0'
+ // extends [mscorlib]System.Object
+
+ var module = parentContext.Body.Method.Module;
+ var anonType = new TypeDefinition(
+ null,
+ "<" + parentContext.Body.Method.Name + ">_anonXamlCDataTemplate_" + dtcount++,
+ TypeAttributes.BeforeFieldInit |
+ TypeAttributes.Sealed |
+ TypeAttributes.NestedPrivate)
+ {
+ BaseType = module.TypeSystem.Object
+ };
+
+ parentContext.Body.Method.DeclaringType.NestedTypes.Add(anonType);
+ var ctor = anonType.AddDefaultConstructor();
+
+ var loadTemplate = new MethodDefinition("LoadDataTemplate",
+ MethodAttributes.Assembly | MethodAttributes.HideBySig,
+ module.TypeSystem.Object);
+ anonType.Methods.Add(loadTemplate);
+
+ var parentValues = new FieldDefinition("parentValues", FieldAttributes.Assembly, module.Import(typeof (object[])));
+ anonType.Fields.Add(parentValues);
+
+ TypeReference rootType = null;
+ var vdefRoot = parentContext.Root as VariableDefinition;
+ if (vdefRoot != null)
+ rootType = vdefRoot.VariableType;
+ var fdefRoot = parentContext.Root as FieldDefinition;
+ if (fdefRoot != null)
+ rootType = fdefRoot.FieldType;
+
+ var root = new FieldDefinition("root", FieldAttributes.Assembly, rootType);
+ anonType.Fields.Add(root);
+
+ //Fill the loadTemplate Body
+ var templateIl = loadTemplate.Body.GetILProcessor();
+ templateIl.Emit(OpCodes.Nop);
+ var templateContext = new ILContext(templateIl, loadTemplate.Body, parentValues)
+ {
+ Root = root
+ };
+ node.Accept(new CreateObjectVisitor(templateContext), null);
+ node.Accept(new SetNamescopesAndRegisterNamesVisitor(templateContext), null);
+ 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);
+
+ //Instanciate nested class
+ var parentIl = parentContext.IL;
+ parentIl.Emit(OpCodes.Newobj, ctor);
+
+ //Copy required local vars
+ parentIl.Emit(OpCodes.Dup); //Duplicate the nestedclass instance
+ parentIl.Append(node.PushParentObjectsArray(parentContext));
+ parentIl.Emit(OpCodes.Stfld, parentValues);
+ parentIl.Emit(OpCodes.Dup); //Duplicate the nestedclass instance
+ if (parentContext.Root is VariableDefinition)
+ parentIl.Emit(OpCodes.Ldloc, parentContext.Root as VariableDefinition);
+ else if (parentContext.Root is FieldDefinition)
+ {
+ parentIl.Emit(OpCodes.Ldarg_0);
+ parentIl.Emit(OpCodes.Ldfld, parentContext.Root as FieldDefinition);
+ }
+ else
+ throw new InvalidProgramException();
+ parentIl.Emit(OpCodes.Stfld, root);
+
+ //SetDataTemplate
+ parentIl.Emit(OpCodes.Ldftn, loadTemplate);
+ var funcCtor =
+ module.Import(typeof (Func<>))
+ .MakeGenericInstanceType(module.TypeSystem.Object)
+ .Resolve()
+ .Methods.First(md => md.IsConstructor && md.Parameters.Count == 2)
+ .MakeGeneric(module.TypeSystem.Object);
+ parentIl.Emit(OpCodes.Newobj, module.Import(funcCtor));
+
+ var propertySetter =
+ module.Import(typeof (IDataTemplate)).Resolve().Properties.First(p => p.Name == "LoadTemplate").SetMethod;
+ parentContext.IL.Emit(OpCodes.Callvirt, module.Import(propertySetter));
+ }
+ }
+
+ class VariableDefinitionReference
+ {
+ public VariableDefinitionReference(VariableDefinition vardef)
+ {
+ VariableDefinition = vardef;
+ }
+
+ public VariableDefinition VariableDefinition { get; set; }
+
+ public static implicit operator VariableDefinition(VariableDefinitionReference vardefref)
+ {
+ return vardefref.VariableDefinition;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Build.Tasks/SetResourcesVisitor.cs b/Xamarin.Forms.Build.Tasks/SetResourcesVisitor.cs
new file mode 100644
index 00000000..c054117c
--- /dev/null
+++ b/Xamarin.Forms.Build.Tasks/SetResourcesVisitor.cs
@@ -0,0 +1,141 @@
+using System;
+using System.Collections;
+using System.Linq;
+using Mono.Cecil;
+using Mono.Cecil.Cil;
+using Xamarin.Forms.Xaml;
+
+namespace Xamarin.Forms.Build.Tasks
+{
+ class SetResourcesVisitor : IXamlNodeVisitor
+ {
+ public SetResourcesVisitor(ILContext context)
+ {
+ Context = context;
+ Module = context.Body.Method.Module;
+ }
+
+ public ILContext Context { get; }
+
+ ModuleDefinition Module { get; }
+
+ public bool VisitChildrenFirst
+ {
+ get { return false; }
+ }
+
+ public bool StopOnDataTemplate
+ {
+ get { return true; }
+ }
+
+ public bool StopOnResourceDictionary
+ {
+ get { return false; }
+ }
+
+ public void Visit(ValueNode node, INode parentNode)
+ {
+ }
+
+ public void Visit(MarkupNode node, INode parentNode)
+ {
+ }
+
+ public void Visit(ElementNode node, INode parentNode)
+ {
+ //Set Resources in ResourcesDictionaries
+ if (IsCollectionItem(node, parentNode) && parentNode is IElementNode)
+ {
+ // Collection element, implicit content, or implicit collection element.
+ CustomAttribute cpAttr;
+ var parentVar = Context.Variables[(IElementNode)parentNode];
+ if (parentVar.VariableType.ImplementsInterface(Module.Import(typeof (IEnumerable))))
+ {
+ if (parentVar.VariableType.FullName == "Xamarin.Forms.ResourceDictionary" &&
+ !node.Properties.ContainsKey(XmlName.xKey))
+ {
+ node.Accept(new SetPropertiesVisitor(Context), parentNode);
+
+ if (node.XmlType.Name != "Style")
+ throw new XamlParseException("resources in ResourceDictionary require a x:Key attribute", node);
+
+ //if this node is an IMarkupExtension, invoke ProvideValue() and replace the variable
+ var vardef = Context.Variables[node];
+ var vardefref = new VariableDefinitionReference(vardef);
+ Context.IL.Append(SetPropertiesVisitor.ProvideValue(vardefref, Context, Module, node));
+ if (vardef != vardefref.VariableDefinition)
+ {
+ vardef = vardefref.VariableDefinition;
+ Context.Body.Variables.Add(vardef);
+ Context.Variables[node] = vardef;
+ }
+
+ Context.IL.Emit(OpCodes.Ldloc, parentVar);
+ Context.IL.Emit(OpCodes.Ldloc, Context.Variables[node]);
+ Context.IL.Emit(OpCodes.Callvirt,
+ Module.Import(
+ Module.Import(typeof (ResourceDictionary))
+ .Resolve()
+ .Methods.Single(md => md.Name == "Add" && md.Parameters.Count == 1)));
+ }
+ else if (parentVar.VariableType.FullName == "Xamarin.Forms.ResourceDictionary" &&
+ node.Properties.ContainsKey(XmlName.xKey))
+ {
+ node.Accept(new SetPropertiesVisitor(Context), parentNode);
+
+ //if this node is an IMarkupExtension, invoke ProvideValue() and replace the variable
+ var vardef = Context.Variables[node];
+ var vardefref = new VariableDefinitionReference(vardef);
+ Context.IL.Append(SetPropertiesVisitor.ProvideValue(vardefref, Context, Module, node));
+ if (vardef != vardefref.VariableDefinition)
+ {
+ vardef = vardefref.VariableDefinition;
+ Context.Body.Variables.Add(vardef);
+ Context.Variables[node] = vardef;
+ }
+
+ // IL_0013: ldloc.0
+ // IL_0014: ldstr "key"
+ // IL_0019: ldstr "foo"
+ // IL_001e: callvirt instance void class [Xamarin.Forms.Core]Xamarin.Forms.ResourceDictionary::Add(string, object)
+ Context.IL.Emit(OpCodes.Ldloc, parentVar);
+ Context.IL.Emit(OpCodes.Ldstr, (node.Properties[XmlName.xKey] as ValueNode).Value as string);
+ var varDef = Context.Variables[node];
+ Context.IL.Emit(OpCodes.Ldloc, varDef);
+ if (varDef.VariableType.IsValueType)
+ Context.IL.Emit(OpCodes.Box, Module.Import(varDef.VariableType));
+ Context.IL.Emit(OpCodes.Callvirt,
+ Module.Import(
+ Module.Import(typeof (ResourceDictionary))
+ .Resolve()
+ .Methods.Single(md => md.Name == "Add" && md.Parameters.Count == 2)));
+ }
+ }
+ }
+
+ //Set ResourcesDictionaries to their parents
+ XmlName propertyName;
+ if (SetPropertiesVisitor.TryGetPropertyName(node, parentNode, out propertyName) &&
+ (propertyName.LocalName == "Resources" || propertyName.LocalName.EndsWith(".Resources", StringComparison.Ordinal)) &&
+ Context.Variables[node].VariableType.FullName == "Xamarin.Forms.ResourceDictionary")
+ SetPropertiesVisitor.SetPropertyValue(Context.Variables[(IElementNode)parentNode], propertyName, node, Context, node);
+ }
+
+ public void Visit(RootNode node, INode parentNode)
+ {
+ }
+
+ public void Visit(ListNode node, INode parentNode)
+ {
+ }
+
+ static bool IsCollectionItem(INode node, INode parentNode)
+ {
+ var parentList = parentNode as IListNode;
+ if (parentList == null)
+ return false;
+ return parentList.CollectionItems.Contains(node);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Build.Tasks/TypeDefinitionExtensions.cs b/Xamarin.Forms.Build.Tasks/TypeDefinitionExtensions.cs
new file mode 100644
index 00000000..51b3fdbf
--- /dev/null
+++ b/Xamarin.Forms.Build.Tasks/TypeDefinitionExtensions.cs
@@ -0,0 +1,66 @@
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using Mono.Cecil;
+using Mono.Cecil.Cil;
+using MethodAttributes = Mono.Cecil.MethodAttributes;
+using MethodImplAttributes = Mono.Cecil.MethodImplAttributes;
+
+namespace Xamarin.Forms.Build.Tasks
+{
+ static class TypeDefinitionExtensions
+ {
+ public static MethodDefinition AddDefaultConstructor(this TypeDefinition targetType)
+ {
+ var parentType = typeof (object);
+
+ return AddDefaultConstructor(targetType, parentType);
+ }
+
+ public static MethodDefinition AddDefaultConstructor(this TypeDefinition targetType, Type parentType)
+ {
+ var module = targetType.Module;
+ var voidType = module.Import(typeof (void));
+ var methodAttributes = MethodAttributes.Public |
+ MethodAttributes.HideBySig |
+ MethodAttributes.SpecialName |
+ MethodAttributes.RTSpecialName;
+
+ var flags = BindingFlags.Public |
+ BindingFlags.NonPublic |
+ BindingFlags.Instance;
+
+ var objectConstructor = parentType.GetConstructor(flags, null, new Type[0], null);
+
+ if (objectConstructor == null)
+ objectConstructor = typeof (object).GetConstructor(new Type[0]);
+
+ var baseConstructor = module.Import(objectConstructor);
+
+ var ctor = new MethodDefinition(".ctor", methodAttributes, voidType)
+ {
+ CallingConvention = MethodCallingConvention.Default,
+ ImplAttributes = (MethodImplAttributes.IL | MethodImplAttributes.Managed)
+ };
+
+ var IL = ctor.Body.GetILProcessor();
+
+ IL.Emit(OpCodes.Ldarg_0);
+ IL.Emit(OpCodes.Call, baseConstructor);
+ IL.Emit(OpCodes.Ret);
+
+ targetType.Methods.Add(ctor);
+ return ctor;
+ }
+
+ public static IEnumerable<MethodDefinition> AllMethods(this TypeDefinition self)
+ {
+ while (self != null)
+ {
+ foreach (var md in self.Methods)
+ yield return md;
+ self = self.BaseType == null ? null : self.BaseType.Resolve();
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Build.Tasks/TypeReferenceExtensions.cs b/Xamarin.Forms.Build.Tasks/TypeReferenceExtensions.cs
new file mode 100644
index 00000000..89a31765
--- /dev/null
+++ b/Xamarin.Forms.Build.Tasks/TypeReferenceExtensions.cs
@@ -0,0 +1,222 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Mono.Cecil;
+using Mono.Cecil.Rocks;
+
+namespace Xamarin.Forms.Build.Tasks
+{
+ static class TypeReferenceExtensions
+ {
+ public static PropertyDefinition GetProperty(this TypeReference typeRef, Func<PropertyDefinition, bool> predicate,
+ out TypeReference declaringTypeRef)
+ {
+ declaringTypeRef = typeRef;
+ var typeDef = typeRef.Resolve();
+ var properties = typeDef.Properties.Where(predicate);
+ if (properties.Any())
+ return properties.Single();
+ if (typeDef.BaseType == null || typeDef.BaseType.FullName == "System.Object")
+ return null;
+ return typeDef.BaseType.GetProperty(predicate, out declaringTypeRef);
+ }
+
+ public static EventDefinition GetEvent(this TypeReference typeRef, Func<EventDefinition, bool> predicate)
+ {
+ var typeDef = typeRef.Resolve();
+ var events = typeDef.Events.Where(predicate);
+ if (events.Any())
+ return events.Single();
+ if (typeDef.BaseType == null || typeDef.BaseType.FullName == "System.Object")
+ return null;
+ return typeDef.BaseType.GetEvent(predicate);
+ }
+
+ public static FieldDefinition GetField(this TypeReference typeRef, Func<FieldDefinition, bool> predicate,
+ out TypeReference declaringTypeRef)
+ {
+ declaringTypeRef = typeRef;
+ var typeDef = typeRef.Resolve();
+ var bp = typeDef.Fields.Where
+ (predicate);
+ if (bp.Any())
+ return bp.Single();
+ if (typeDef.BaseType == null || typeDef.BaseType.FullName == "System.Object")
+ return null;
+ var basetype = typeDef.BaseType.ResolveGenericParameters(typeRef);
+ return basetype.GetField(predicate, out declaringTypeRef);
+ }
+
+ public static bool ImplementsInterface(this TypeReference typeRef, TypeReference @interface)
+ {
+ var typeDef = typeRef.Resolve();
+ if (typeDef.Interfaces.Any(tr => tr.FullName == @interface.FullName))
+ return true;
+ var baseTypeRef = typeDef.BaseType;
+ if (baseTypeRef != null && baseTypeRef.FullName != "System.Object")
+ return baseTypeRef.ImplementsInterface(@interface);
+ return false;
+ }
+
+ public static bool ImplementsGenericInterface(this TypeReference typeRef, string @interface,
+ out GenericInstanceType interfaceReference, out IList<TypeReference> genericArguments)
+ {
+ interfaceReference = null;
+ genericArguments = null;
+ var typeDef = typeRef.Resolve();
+ TypeReference iface;
+ if (
+ (iface =
+ typeDef.Interfaces.FirstOrDefault(
+ tr =>
+ tr.FullName.StartsWith(@interface) && tr.IsGenericInstance && (tr as GenericInstanceType).HasGenericArguments)) !=
+ null)
+ {
+ interfaceReference = iface as GenericInstanceType;
+ genericArguments = (iface as GenericInstanceType).GenericArguments;
+ return true;
+ }
+ var baseTypeRef = typeDef.BaseType;
+ if (baseTypeRef != null && baseTypeRef.FullName != "System.Object")
+ return baseTypeRef.ImplementsGenericInterface(@interface, out interfaceReference, out genericArguments);
+ return false;
+ }
+
+ public static bool InheritsFromOrImplements(this TypeReference typeRef, TypeReference baseClass)
+ {
+ var arrayInterfaces = new[]
+ {
+ "System.IEnumerable",
+ "System.Collections.IList",
+ "System.Collections.Collection"
+ };
+
+ var arrayGenericInterfaces = new[]
+ {
+ "System.IEnumerable`1",
+ "System.Collections.Generic.IList`1",
+ "System.Collections.Generic.IReadOnlyCollection<T>",
+ "System.Collections.Generic.IReadOnlyList<T>",
+ "System.Collections.Generic.Collection<T>"
+ };
+
+ if (typeRef.IsArray)
+ {
+ var arrayType = typeRef.Resolve();
+ if (arrayInterfaces.Contains(baseClass.FullName))
+ return true;
+ if (arrayGenericInterfaces.Contains(baseClass.Resolve().FullName) &&
+ baseClass.IsGenericInstance &&
+ (baseClass as GenericInstanceType).GenericArguments[0].FullName == arrayType.FullName)
+ return true;
+ }
+ var typeDef = typeRef.Resolve();
+ if (typeDef.FullName == baseClass.FullName)
+ return true;
+ if (typeDef.Interfaces.Any(ir => ir.FullName == baseClass.FullName))
+ return true;
+ if (typeDef.FullName == "System.Object")
+ return false;
+ if (typeDef.BaseType == null)
+ return false;
+ return typeDef.BaseType.InheritsFromOrImplements(baseClass);
+ }
+
+ public static CustomAttribute GetCustomAttribute(this TypeReference typeRef, TypeReference attribute)
+ {
+ var typeDef = typeRef.Resolve();
+ //FIXME: avoid string comparison. make sure the attribute TypeRef is the same one
+ var attr = typeDef.CustomAttributes.SingleOrDefault(ca => ca.AttributeType.FullName == attribute.FullName);
+ if (attr != null)
+ return attr;
+ var baseTypeRef = typeDef.BaseType;
+ if (baseTypeRef != null && baseTypeRef.FullName != "System.Object")
+ return baseTypeRef.GetCustomAttribute(attribute);
+ return null;
+ }
+
+ [Obsolete]
+ public static MethodDefinition GetMethod(this TypeReference typeRef, Func<MethodDefinition, bool> predicate)
+ {
+ TypeReference declaringTypeReference;
+ return typeRef.GetMethod(predicate, out declaringTypeReference);
+ }
+
+ [Obsolete]
+ public static MethodDefinition GetMethod(this TypeReference typeRef, Func<MethodDefinition, bool> predicate,
+ out TypeReference declaringTypeRef)
+ {
+ declaringTypeRef = typeRef;
+ var typeDef = typeRef.Resolve();
+ var methods = typeDef.Methods.Where(predicate);
+ if (methods.Any())
+ return methods.Single();
+ if (typeDef.BaseType != null && typeDef.BaseType.FullName == "System.Object")
+ return null;
+ if (typeDef.IsInterface)
+ {
+ foreach (var face in typeDef.Interfaces)
+ {
+ var m = face.GetMethod(predicate);
+ if (m != null)
+ return m;
+ }
+ return null;
+ }
+ return typeDef.BaseType.GetMethod(predicate, out declaringTypeRef);
+ }
+
+ public static IEnumerable<Tuple<MethodDefinition, TypeReference>> GetMethods(this TypeReference typeRef,
+ Func<MethodDefinition, bool> predicate, ModuleDefinition module)
+ {
+ return typeRef.GetMethods((md, tr) => predicate(md), module);
+ }
+
+ public static IEnumerable<Tuple<MethodDefinition, TypeReference>> GetMethods(this TypeReference typeRef,
+ Func<MethodDefinition, TypeReference, bool> predicate, ModuleDefinition module)
+ {
+ var typeDef = typeRef.Resolve();
+ foreach (var method in typeDef.Methods.Where(md => predicate(md, typeRef)))
+ yield return new Tuple<MethodDefinition, TypeReference>(method, typeRef);
+ if (typeDef.IsInterface)
+ {
+ foreach (var face in typeDef.Interfaces)
+ {
+ if (face.IsGenericInstance && typeRef is GenericInstanceType)
+ {
+ int i = 0;
+ foreach (var arg in ((GenericInstanceType)typeRef).GenericArguments)
+ ((GenericInstanceType)face).GenericArguments[i++] = module.Import(arg);
+ }
+ foreach (var tuple in face.GetMethods(predicate, module))
+ yield return tuple;
+ }
+ yield break;
+ }
+ if (typeDef.BaseType == null || typeDef.BaseType.FullName == "System.Object")
+ yield break;
+ var baseType = typeDef.BaseType.ResolveGenericParameters(typeRef);
+ foreach (var tuple in baseType.GetMethods(predicate, module))
+ yield return tuple;
+ }
+
+ public static TypeReference ResolveGenericParameters(this TypeReference self, TypeReference declaringTypeReference)
+ {
+ var genericself = self as GenericInstanceType;
+ if (genericself == null)
+ return self;
+
+ var genericdeclType = declaringTypeReference as GenericInstanceType;
+ if (genericdeclType == null)
+ return self;
+
+ if (!genericself.GenericArguments.Any(arg => arg.IsGenericParameter))
+ return self;
+
+ List<TypeReference> args = new List<TypeReference>();
+ for (var i = 0; i < genericself.GenericArguments.Count; i++)
+ args.Add(genericdeclType.GenericArguments[(genericself.GenericArguments[i] as GenericParameter).Position]);
+ return self.GetElementType().MakeGenericInstanceType(args.ToArray());
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Build.Tasks/Xamarin.Forms.Build.Tasks.csproj b/Xamarin.Forms.Build.Tasks/Xamarin.Forms.Build.Tasks.csproj
new file mode 100644
index 00000000..0808dfdf
--- /dev/null
+++ b/Xamarin.Forms.Build.Tasks/Xamarin.Forms.Build.Tasks.csproj
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProjectGuid>{96D89208-4EB9-4451-BE73-8A9DF3D9D7B7}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <RootNamespace>Xamarin.Forms.Build.Tasks</RootNamespace>
+ <AssemblyName>Xamarin.Forms.Build.Tasks</AssemblyName>
+ <TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
+ <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
+ <RestorePackages>true</RestorePackages>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug</OutputPath>
+ <DefineConstants>DEBUG;</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <ConsolePause>false</ConsolePause>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>full</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release</OutputPath>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <ConsolePause>false</ConsolePause>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Turkey|AnyCPU'">
+ <DebugSymbols>true</DebugSymbols>
+ <OutputPath>bin\Turkey\</OutputPath>
+ <DefineConstants>DEBUG;</DefineConstants>
+ <DebugType>full</DebugType>
+ <PlatformTarget>AnyCPU</PlatformTarget>
+ <ErrorReport>prompt</ErrorReport>
+ <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="System" />
+ <Reference Include="Microsoft.Build.Framework" />
+ <Reference Include="Microsoft.Build.Utilities.v4.0" />
+ <Reference Include="Microsoft.Build" />
+ <Reference Include="System.Xml" />
+ <Reference Include="Microsoft.Build.Tasks.v4.0" />
+ <Reference Include="Mono.Cecil">
+ <HintPath>..\packages\Mono.Cecil.0.9.6.1\lib\net45\Mono.Cecil.dll</HintPath>
+ </Reference>
+ <Reference Include="Mono.Cecil.Mdb">
+ <HintPath>..\packages\Mono.Cecil.0.9.6.1\lib\net45\Mono.Cecil.Mdb.dll</HintPath>
+ </Reference>
+ <Reference Include="Mono.Cecil.Pdb">
+ <HintPath>..\packages\Mono.Cecil.0.9.6.1\lib\net45\Mono.Cecil.Pdb.dll</HintPath>
+ </Reference>
+ <Reference Include="Mono.Cecil.Rocks">
+ <HintPath>..\packages\Mono.Cecil.0.9.6.1\lib\net45\Mono.Cecil.Rocks.dll</HintPath>
+ </Reference>
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="..\Xamarin.Forms.Xaml\XmlnsHelper.cs">
+ <Link>XmlnsHelper.cs</Link>
+ </Compile>
+ <Compile Include="XamlGTask.cs" />
+ <Compile Include="ILContext.cs" />
+ <Compile Include="CreateObjectVisitor.cs" />
+ <Compile Include="SetPropertiesVisitor.cs" />
+ <Compile Include="SetFieldVisitor.cs" />
+ <Compile Include="TypeReferenceExtensions.cs" />
+ <Compile Include="NodeILExtensions.cs" />
+ <Compile Include="ILProcessorExtensions.cs" />
+ <Compile Include="ExpandMarkupsVisitor.cs" />
+ <Compile Include="SetNamescopesAndRegisterNamesVisitor.cs" />
+ <Compile Include="MethodReferenceExtensions.cs" />
+ <Compile Include="XamlCTask.cs" />
+ <Compile Include="DebugXamlCTask.cs" />
+ <Compile Include="ILRootNode.cs" />
+ <Compile Include="XmlTypeExtensions.cs" />
+ <Compile Include="SetResourcesVisitor.cs" />
+ <Compile Include="TypeDefinitionExtensions.cs" />
+ <Compile Include="FieldReferenceExtensions.cs" />
+ <Compile Include="PropertyDefinitionExtensions.cs" />
+ <Compile Include="XamlCAssemblyResolver.cs" />
+ <Compile Include="FixedCreateCSharpManifestResourceName.cs" />
+ <Compile Include="MethodDefinitionExtensions.cs" />
+ </ItemGroup>
+ <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
+ <Target Name="AfterBuild">
+ <ItemGroup>
+ <_CopyItems Include="$(TargetDir)*.dll" />
+ </ItemGroup>
+ <Copy SourceFiles="@(_CopyItems)" DestinationFolder="..\.nuspec\" ContinueOnError="true" />
+ </Target>
+ <ItemGroup>
+ <None Include="packages.config" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\Xamarin.Forms.Xaml\Xamarin.Forms.Xaml.csproj">
+ <Project>{9DB2F292-8034-4E06-89AD-98BBDA4306B9}</Project>
+ <Name>Xamarin.Forms.Xaml</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\Xamarin.Forms.Core\Xamarin.Forms.Core.csproj">
+ <Project>{57B8B73D-C3B5-4C42-869E-7B2F17D354AC}</Project>
+ <Name>Xamarin.Forms.Core</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\ICSharpCode.Decompiler\ICSharpCode.Decompiler.csproj">
+ <Project>{984CC812-9470-4A13-AFF9-CC44068D666C}</Project>
+ <Name>ICSharpCode.Decompiler</Name>
+ </ProjectReference>
+ </ItemGroup>
+ <Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
+ <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
+ <PropertyGroup>
+ <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
+ </PropertyGroup>
+ <Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
+ </Target>
+</Project>
diff --git a/Xamarin.Forms.Build.Tasks/XamlCAssemblyResolver.cs b/Xamarin.Forms.Build.Tasks/XamlCAssemblyResolver.cs
new file mode 100644
index 00000000..04f72c12
--- /dev/null
+++ b/Xamarin.Forms.Build.Tasks/XamlCAssemblyResolver.cs
@@ -0,0 +1,15 @@
+using Mono.Cecil;
+
+namespace Xamarin.Forms.Build.Tasks
+{
+ class XamlCAssemblyResolver : DefaultAssemblyResolver
+ {
+ public void AddAssembly(string p)
+ {
+ RegisterAssembly(AssemblyDefinition.ReadAssembly(p, new ReaderParameters
+ {
+ AssemblyResolver = this
+ }));
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Build.Tasks/XamlCTask.cs b/Xamarin.Forms.Build.Tasks/XamlCTask.cs
new file mode 100644
index 00000000..81f06427
--- /dev/null
+++ b/Xamarin.Forms.Build.Tasks/XamlCTask.cs
@@ -0,0 +1,417 @@
+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;
+using Xamarin.Forms.Xaml;
+
+namespace Xamarin.Forms.Build.Tasks
+{
+ public class XamlCTask : AppDomainIsolatedTask
+ {
+ 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; }
+
+ public override bool Execute()
+ {
+ InMsBuild = true;
+ return Compile();
+ }
+
+ 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 Log(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)
+ {
+ 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()
+ {
+ LogLine(1, "Compiling Xaml");
+ LogLine(1, "\nAssembly: {0}", Assembly);
+ if (!string.IsNullOrEmpty(DependencyPaths))
+ LogLine(1, "DependencyPaths: \t{0}", DependencyPaths);
+ if (!string.IsNullOrEmpty(ReferencePath))
+ LogLine(1, "ReferencePath: \t{0}", ReferencePath.Replace("//", "/"));
+ 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.");
+ return true;
+ }
+
+ var resolver = new XamlCAssemblyResolver();
+ if (!string.IsNullOrEmpty(DependencyPaths))
+ {
+ foreach (var dep in DependencyPaths.Split(';'))
+ {
+ 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);
+ LogLine(3, "Adding searchpath {0}", searchpath);
+ resolver.AddSearchDirectory(searchpath);
+ // LogLine (3, "Referencing {0}", p);
+ // resolver.AddAssembly (p);
+ }
+ }
+
+ var assemblyDefinition = AssemblyDefinition.ReadAssembly(Path.GetFullPath(Assembly), new ReaderParameters
+ {
+ AssemblyResolver = resolver,
+ ReadSymbols = DebugSymbols
+ });
+
+ 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;
+ }
+
+ LogLine(2, " Module: {0}", module.Name);
+ var resourcesToPrune = new List<EmbeddedResource>();
+ foreach (var resource in module.Resources.OfType<EmbeddedResource>())
+ {
+ Log(2, " Resource: {0}... ", resource.Name);
+ string classname;
+ if (!resource.IsXaml(out classname))
+ {
+ LogLine(2, "skipped.");
+ continue;
+ }
+ TypeDefinition typeDef = module.GetType(classname);
+ if (typeDef == null)
+ {
+ 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 (skiptype)
+ {
+ 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.");
+ continue;
+ }
+ LogLine(2, "");
+
+ Log(2, " Parsing Xaml... ");
+ var rootnode = ParseXaml(resource.GetResourceStream(), typeDef);
+ if (rootnode == null)
+ {
+ LogLine(2, "failed.");
+ continue;
+ }
+ LogLine(2, "done.");
+
+ hasCompiledXamlResources = true;
+
+ try
+ {
+ Log(2, " Replacing {0}.InitializeComponent ()... ", typeDef.Name);
+ var body = new MethodBody(initComp);
+ var il = body.GetILProcessor();
+ il.Emit(OpCodes.Nop);
+ var visitorContext = new ILContext(il, body);
+
+ rootnode.Accept(new XamlNodeVisitor((node, parent) => node.Parent = parent), null);
+ rootnode.Accept(new ExpandMarkupsVisitor(visitorContext), 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(OpCodes.Ret);
+ initComp.Body = body;
+ }
+ catch (XamlParseException xpe)
+ {
+ LogLine(2, "failed.");
+ LogError(null, null, null, resource.Name, xpe.XmlInfo.LineNumber, xpe.XmlInfo.LinePosition, 0, 0, xpe.Message,
+ xpe.HelpLink, xpe.Source);
+ LogLine(4, xpe.StackTrace);
+ success = false;
+ continue;
+ }
+ catch (XmlException xe)
+ {
+ LogLine(2, "failed.");
+ LogError(null, null, null, resource.Name, xe.LineNumber, xe.LinePosition, 0, 0, xe.Message, xe.HelpLink, xe.Source);
+ LogLine(4, xe.StackTrace);
+ success = false;
+ continue;
+ }
+ catch (Exception e)
+ {
+ LogLine(2, "failed.");
+ LogError(null, null, null, resource.Name, 0, 0, 0, 0, e.Message, e.HelpLink, e.Source);
+ LogLine(4, e.StackTrace);
+ success = false;
+ continue;
+ }
+ LogLine(2, "done.");
+
+ if (OptimizeIL)
+ {
+ Log(2, " Optimizing IL... ");
+ initComp.Body.OptimizeMacros();
+ LogLine(2, "done");
+ }
+
+ if (OutputGeneratedILAsCode)
+ {
+ var filepath = Path.Combine(Path.GetDirectoryName(Assembly), typeDef.FullName + ".decompiled.cs");
+ Log(2, " Decompiling {0} into {1}...", typeDef.FullName, filepath);
+ var decompilerContext = new DecompilerContext(module);
+ using(var writer = new StreamWriter(filepath))
+ {
+ var output = new PlainTextOutput(writer);
+
+ var codeDomBuilder = new AstBuilder(decompilerContext);
+ codeDomBuilder.AddType(typeDef);
+ codeDomBuilder.GenerateCode(output);
+ }
+
+ LogLine(2, "done");
+ }
+ resourcesToPrune.Add(resource);
+ }
+ if (!KeepXamlResources)
+ {
+ if (resourcesToPrune.Any())
+ LogLine(2, " Removing compiled xaml resources");
+ foreach (var resource in resourcesToPrune)
+ {
+ Log(2, " Removing {0}... ", resource.Name);
+ module.Resources.Remove(resource);
+ LogLine(2, "done");
+ }
+ }
+
+ LogLine(2, "");
+ }
+
+ if (!hasCompiledXamlResources)
+ {
+ LogLine(1, "No compiled resources. Skipping writing assembly.");
+ return success;
+ }
+
+ Log(1, "Writing the assembly... ");
+ try
+ {
+ assemblyDefinition.Write(Assembly, new WriterParameters
+ {
+ WriteSymbols = DebugSymbols
+ });
+ LogLine(1, "done.");
+ }
+ catch (Exception e)
+ {
+ LogLine(1, "failed.");
+ LogError(null, null, null, null, 0, 0, 0, 0, e.Message, e.HelpLink, e.Source);
+ LogLine(4, e.StackTrace);
+ success = false;
+ }
+
+ return success;
+ }
+
+ 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);
+ 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/XamlGTask.cs b/Xamarin.Forms.Build.Tasks/XamlGTask.cs
new file mode 100644
index 00000000..28158e6b
--- /dev/null
+++ b/Xamarin.Forms.Build.Tasks/XamlGTask.cs
@@ -0,0 +1,261 @@
+using System;
+using System.CodeDom;
+using System.CodeDom.Compiler;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Xml;
+using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
+using Microsoft.CSharp;
+using Xamarin.Forms.Xaml;
+
+namespace Xamarin.Forms.Build.Tasks
+{
+ public class XamlGTask : Task
+ {
+ internal static CodeDomProvider Provider = new CSharpCodeProvider();
+
+ [Required]
+ public string Source { get; set; }
+
+ public string Language { get; set; }
+
+ public string AssemblyName { get; set; }
+
+ [Output]
+ public string OutputFile { get; set; }
+
+ public override bool Execute()
+ {
+ if (Source == null || OutputFile == null)
+ {
+ Log.LogMessage("Skipping XamlG");
+ return true;
+ }
+
+ Log.LogMessage("Source: {0}", Source);
+ Log.LogMessage("Language: {0}", Language);
+ Log.LogMessage("AssemblyName: {0}", AssemblyName);
+ Log.LogMessage("OutputFile {0}", OutputFile);
+
+ try
+ {
+ GenerateFile(Source, OutputFile);
+ return true;
+ }
+ catch (XmlException xe)
+ {
+ Log.LogError(null, null, null, Source, xe.LineNumber, xe.LinePosition, 0, 0, xe.Message, xe.HelpLink, xe.Source);
+
+ return false;
+ }
+ catch (Exception e)
+ {
+ Log.LogError(null, null, null, Source, 0, 0, 0, 0, e.Message, e.HelpLink, e.Source);
+ return false;
+ }
+ }
+
+ internal static void ParseXaml(TextReader xaml, out string rootType, out string rootNs, out CodeTypeReference baseType,
+ out IDictionary<string, CodeTypeReference> namesAndTypes)
+ {
+ var xmlDoc = new XmlDocument();
+ xmlDoc.Load(xaml);
+
+ var nsmgr = new XmlNamespaceManager(xmlDoc.NameTable);
+ nsmgr.AddNamespace("x", "http://schemas.microsoft.com/winfx/2006/xaml");
+ nsmgr.AddNamespace("x2009", "http://schemas.microsoft.com/winfx/2009/xaml");
+ nsmgr.AddNamespace("f", "http://xamarin.com/schemas/2014/forms");
+
+ var root = xmlDoc.SelectSingleNode("/*", nsmgr);
+ if (root == null)
+ {
+ Console.Error.WriteLine("{0}: No root node found");
+ rootType = null;
+ rootNs = null;
+ baseType = null;
+ namesAndTypes = null;
+ return;
+ }
+
+ 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)
+ {
+ rootType = null;
+ rootNs = null;
+ baseType = null;
+ namesAndTypes = null;
+ return;
+ }
+
+ string rootAsm;
+ XmlnsHelper.ParseXmlns(rootClass.Value, out rootType, out rootNs, out rootAsm);
+ namesAndTypes = GetNamesAndTypes(root, nsmgr);
+
+ var typeArguments = root.Attributes["TypeArguments", "http://schemas.microsoft.com/winfx/2009/xaml"];
+
+ baseType = GetType(root.NamespaceURI, root.LocalName, typeArguments == null ? null : typeArguments.Value.Split(','),
+ root.GetNamespaceOfPrefix);
+ }
+
+ internal static void GenerateCode(string rootType, string rootNs, CodeTypeReference baseType,
+ IDictionary<string, CodeTypeReference> namesAndTypes, string outFile)
+ {
+ if (rootType == null)
+ {
+ File.WriteAllText(outFile, "");
+ return;
+ }
+
+ var ccu = new CodeCompileUnit();
+ var declNs = new CodeNamespace(rootNs);
+ ccu.Namespaces.Add(declNs);
+
+ declNs.Imports.Add(new CodeNamespaceImport("System"));
+ declNs.Imports.Add(new CodeNamespaceImport("Xamarin.Forms"));
+ declNs.Imports.Add(new CodeNamespaceImport("Xamarin.Forms.Xaml"));
+
+ var declType = new CodeTypeDeclaration(rootType);
+ declType.IsPartial = true;
+ declType.BaseTypes.Add(baseType);
+
+ declNs.Types.Add(declType);
+
+ var initcomp = new CodeMemberMethod
+ {
+ Name = "InitializeComponent",
+ CustomAttributes =
+ {
+ new CodeAttributeDeclaration(new CodeTypeReference(typeof (GeneratedCodeAttribute)),
+ new CodeAttributeArgument(new CodePrimitiveExpression("Xamarin.Forms.Build.Tasks.XamlG")),
+ new CodeAttributeArgument(new CodePrimitiveExpression("0.0.0.0")))
+ }
+ };
+ declType.Members.Add(initcomp);
+
+ initcomp.Statements.Add(new CodeMethodInvokeExpression(
+ new CodeThisReferenceExpression(),
+ "LoadFromXaml", new CodeTypeOfExpression(declType.Name)));
+
+ foreach (var entry in namesAndTypes)
+ {
+ string name = entry.Key;
+ var type = entry.Value;
+
+ var field = new CodeMemberField
+ {
+ Name = name,
+ Type = type,
+ CustomAttributes =
+ {
+ new CodeAttributeDeclaration(new CodeTypeReference(typeof (GeneratedCodeAttribute)),
+ new CodeAttributeArgument(new CodePrimitiveExpression("Xamarin.Forms.Build.Tasks.XamlG")),
+ new CodeAttributeArgument(new CodePrimitiveExpression("0.0.0.0")))
+ }
+ };
+
+ declType.Members.Add(field);
+
+ var find_invoke = new CodeMethodInvokeExpression(
+ new CodeMethodReferenceExpression(
+ new CodeThisReferenceExpression(),
+ "FindByName", type), new CodePrimitiveExpression(name));
+
+ //CodeCastExpression cast = new CodeCastExpression (type, find_invoke);
+
+ CodeAssignStatement assign = new CodeAssignStatement(
+ new CodeVariableReferenceExpression(name), find_invoke);
+
+ initcomp.Statements.Add(assign);
+ }
+
+ using(var writer = new StreamWriter(outFile))
+ Provider.GenerateCodeFromCompileUnit(ccu, writer, new CodeGeneratorOptions());
+ }
+
+ internal static void GenerateFile(string xamlFile, string outFile)
+ {
+ string rootType, rootNs;
+ CodeTypeReference baseType;
+ IDictionary<string, CodeTypeReference> namesAndTypes;
+ using(StreamReader reader = File.OpenText(xamlFile))
+ ParseXaml(reader, out rootType, out rootNs, out baseType, out namesAndTypes);
+ GenerateCode(rootType, rootNs, baseType, namesAndTypes, outFile);
+ }
+
+ static Dictionary<string, CodeTypeReference> GetNamesAndTypes(XmlNode root, XmlNamespaceManager nsmgr)
+ {
+ var res = new Dictionary<string, CodeTypeReference>();
+
+ foreach (string attrib in new[] { "x:Name", "x2009:Name" })
+ {
+ XmlNodeList names =
+ root.SelectNodes(
+ "//*[@" + attrib +
+ "][not(ancestor:: f:DataTemplate) and not(ancestor:: f:ControlTemplate) and not(ancestor:: f:Style)]", nsmgr);
+ foreach (XmlNode node in names)
+ {
+ // Don't take the root canvas
+ if (node == root)
+ continue;
+
+ XmlAttribute attr = node.Attributes["Name", "http://schemas.microsoft.com/winfx/2006/xaml"] ??
+ node.Attributes["Name", "http://schemas.microsoft.com/winfx/2009/xaml"];
+ XmlAttribute typeArgsAttr = node.Attributes["x:TypeArguments"];
+ var typeArgsList = typeArgsAttr == null ? null : typeArgsAttr.Value.Split(',').ToList();
+ string name = attr.Value;
+
+ res[name] = GetType(node.NamespaceURI, node.LocalName, typeArgsList, node.GetNamespaceOfPrefix);
+ }
+ }
+
+ return res;
+ }
+
+ static CodeTypeReference GetType(string nsuri, string type, IList<string> typeArguments = null,
+ Func<string, string> getNamespaceOfPrefix = null)
+ {
+ var ns = GetNamespace(nsuri);
+ if (ns != null)
+ type = String.Concat(ns, ".", type);
+
+ if (typeArguments != null)
+ type = String.Concat(type, "`", typeArguments.Count);
+
+ var returnType = new CodeTypeReference(type);
+ if (ns != null)
+ returnType.Options |= CodeTypeReferenceOptions.GlobalReference;
+
+ if (typeArguments != null)
+ {
+ foreach (var typeArg in typeArguments)
+ {
+ var ns_uri = "";
+ var _type = typeArg;
+ if (typeArg.Contains(":"))
+ {
+ var prefix = typeArg.Split(':')[0].Trim();
+ ns_uri = getNamespaceOfPrefix(prefix);
+ _type = typeArg.Split(':')[1].Trim();
+ }
+ returnType.TypeArguments.Add(GetType(ns_uri, _type, null, getNamespaceOfPrefix));
+ }
+ }
+
+ return returnType;
+ }
+
+ static string GetNamespace(string namespaceuri)
+ {
+ if (!XmlnsHelper.IsCustom(namespaceuri))
+ return "Xamarin.Forms";
+ if (namespaceuri == "http://schemas.microsoft.com/winfx/2009/xaml")
+ return "System";
+ if (namespaceuri != "http://schemas.microsoft.com/winfx/2006/xaml" && !namespaceuri.Contains("clr-namespace"))
+ throw new Exception(String.Format("Can't load types from xmlns {0}", namespaceuri));
+ return XmlnsHelper.ParseNamespaceFromXmlns(namespaceuri);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Build.Tasks/XmlTypeExtensions.cs b/Xamarin.Forms.Build.Tasks/XmlTypeExtensions.cs
new file mode 100644
index 00000000..e0810633
--- /dev/null
+++ b/Xamarin.Forms.Build.Tasks/XmlTypeExtensions.cs
@@ -0,0 +1,96 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Xml;
+using Mono.Cecil;
+using Mono.Cecil.Rocks;
+using Xamarin.Forms.Xaml;
+
+namespace Xamarin.Forms.Build.Tasks
+{
+ static class XmlTypeExtensions
+ {
+ public static TypeReference GetTypeReference(this XmlType xmlType, ModuleDefinition module, IXmlLineInfo xmlInfo)
+ {
+ var namespaceURI = xmlType.NamespaceUri;
+ var elementName = xmlType.Name;
+ var typeArguments = xmlType.TypeArguments;
+
+ List<Tuple<string, string>> lookupAssemblies = new List<Tuple<string, string>>(); //assembly, namespace
+ List<string> lookupNames = new List<string>();
+
+ if (!XmlnsHelper.IsCustom(namespaceURI))
+ {
+ lookupAssemblies.Add(new Tuple<string, string>("Xamarin.Forms.Core", "Xamarin.Forms"));
+ lookupAssemblies.Add(new Tuple<string, string>("Xamarin.Forms.Xaml", "Xamarin.Forms.Xaml"));
+ }
+ else if (namespaceURI == "http://schemas.microsoft.com/winfx/2009/xaml" ||
+ namespaceURI == "http://schemas.microsoft.com/winfx/2006/xaml")
+ {
+ lookupAssemblies.Add(new Tuple<string, string>("Xamarin.Forms.Xaml", "Xamarin.Forms.Xaml"));
+ lookupAssemblies.Add(new Tuple<string, string>("mscorlib", "System"));
+ lookupAssemblies.Add(new Tuple<string, string>("System", "System"));
+ }
+ else
+ {
+ string ns;
+ string typename;
+ string asmstring;
+
+ XmlnsHelper.ParseXmlns(namespaceURI, out typename, out ns, out asmstring);
+ asmstring = asmstring ?? module.Assembly.Name.Name;
+ lookupAssemblies.Add(new Tuple<string, string>(asmstring, ns));
+ }
+
+ lookupNames.Add(elementName);
+ if (namespaceURI == "http://schemas.microsoft.com/winfx/2009/xaml")
+ lookupNames.Add(elementName + "Extension");
+ for (var i = 0; i < lookupNames.Count; i++)
+ {
+ var name = lookupNames[i];
+ if (name.Contains(":"))
+ name = name.Substring(name.LastIndexOf(':') + 1);
+ if (typeArguments != null)
+ name += "`" + typeArguments.Count; //this will return an open generic Type
+ lookupNames[i] = name;
+ }
+
+ TypeReference type = null;
+ foreach (var asm in lookupAssemblies)
+ {
+ if (type != null)
+ break;
+ foreach (var name in lookupNames)
+ {
+ if (type != null)
+ break;
+
+ var assemblydefinition = module.Assembly.Name.Name == asm.Item1
+ ? module.Assembly
+ : module.AssemblyResolver.Resolve(asm.Item1);
+ type = assemblydefinition.MainModule.GetType(asm.Item2, name);
+ if (type == null)
+ {
+ var exportedtype =
+ assemblydefinition.MainModule.ExportedTypes.FirstOrDefault(
+ (ExportedType arg) => arg.IsForwarder && arg.Namespace == asm.Item2 && arg.Name == name);
+ if (exportedtype != null)
+ type = exportedtype.Resolve();
+ }
+ }
+ }
+
+ if (type != null && typeArguments != null && type.HasGenericParameters)
+ {
+ type =
+ module.Import(type)
+ .MakeGenericInstanceType(typeArguments.Select(x => GetTypeReference(x, module, xmlInfo)).ToArray());
+ }
+
+ if (type == null)
+ throw new XamlParseException(string.Format("Type {0} not found in xmlns {1}", elementName, namespaceURI), xmlInfo);
+
+ return module.Import(type);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Build.Tasks/packages.config b/Xamarin.Forms.Build.Tasks/packages.config
new file mode 100644
index 00000000..2821c6e5
--- /dev/null
+++ b/Xamarin.Forms.Build.Tasks/packages.config
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+ <package id="Mono.Cecil" version="0.9.6.1" targetFramework="net451" />
+</packages> \ No newline at end of file