summaryrefslogtreecommitdiff
path: root/Xamarin.Forms.Build.Tasks/SetPropertiesVisitor.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Xamarin.Forms.Build.Tasks/SetPropertiesVisitor.cs')
-rw-r--r--Xamarin.Forms.Build.Tasks/SetPropertiesVisitor.cs681
1 files changed, 681 insertions, 0 deletions
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