7018f346b197cd507a883f77f4074b0751211ded
[platform/core/csapi/xsf.git] / src / XSF.Build.Tasks / SetPropertiesVisitor.cs
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Xml;
5
6 using Mono.Cecil;
7 using Mono.Cecil.Cil;
8 using Mono.Cecil.Rocks;
9
10 using Xamarin.Forms.Internals;
11 using Xamarin.Forms.Xaml;
12
13 using static Mono.Cecil.Cil.Instruction;
14 using static Mono.Cecil.Cil.OpCodes;
15
16 namespace Xamarin.Forms.Build.Tasks
17 {
18         class SetPropertiesVisitor : IXamlNodeVisitor
19         {
20                 static int dtcount;
21                 static int typedBindingCount;
22
23                 static readonly IList<XmlName> skips = new List<XmlName>
24                 {
25                         XmlName.xKey,
26                         XmlName.xTypeArguments,
27                         XmlName.xArguments,
28                         XmlName.xFactoryMethod,
29                         XmlName.xName,
30                         XmlName.xDataType
31                 };
32
33                 public SetPropertiesVisitor(ILContext context, bool stopOnResourceDictionary = false)
34                 {
35                         Context = context;
36                         Module = context.Body.Method.Module;
37                         StopOnResourceDictionary = stopOnResourceDictionary;
38                 }
39
40                 public ILContext Context { get; }
41                 public bool StopOnResourceDictionary { get; }
42                 public TreeVisitingMode VisitingMode => TreeVisitingMode.BottomUp;
43                 public bool StopOnDataTemplate => true;
44                 public bool VisitNodeOnDataTemplate => true;
45                 public bool SkipChildren(INode node, INode parentNode) => false;
46
47                 public bool IsResourceDictionary(ElementNode node)
48                 {
49                         var parentVar = Context.Variables[(IElementNode)node];
50                         return parentVar.VariableType.FullName == "Xamarin.Forms.ResourceDictionary"
51                                 || parentVar.VariableType.Resolve().BaseType?.FullName == "Xamarin.Forms.ResourceDictionary";
52                 }
53
54                 ModuleDefinition Module { get; }
55
56                 public void Visit(ValueNode node, INode parentNode)
57                 {
58                         //TODO support Label text as element
59                         XmlName propertyName;
60                         if (!TryGetPropertyName(node, parentNode, out propertyName))
61                         {
62                                 if (!IsCollectionItem(node, parentNode))
63                                         return;
64                                 string contentProperty;
65                                 if (!Context.Variables.ContainsKey((IElementNode)parentNode))
66                                         return;
67                                 var parentVar = Context.Variables[(IElementNode)parentNode];
68                                 if ((contentProperty = GetContentProperty(parentVar.VariableType)) != null)
69                                         propertyName = new XmlName(((IElementNode)parentNode).NamespaceURI, contentProperty);
70                                 else
71                                         return;
72                         }
73
74                         if (TrySetRuntimeName(propertyName, Context.Variables[(IElementNode)parentNode], node))
75                                 return;
76                         if (skips.Contains(propertyName))
77                                 return;
78                         if (parentNode is IElementNode && ((IElementNode)parentNode).SkipProperties.Contains (propertyName))
79                                 return;
80                         if (propertyName.Equals(XamlParser.McUri, "Ignorable"))
81                                 return;
82                         Context.IL.Append(SetPropertyValue(Context.Variables [(IElementNode)parentNode], propertyName, node, Context, node));
83                 }
84
85                 public void Visit(MarkupNode node, INode parentNode)
86                 {
87                 }
88
89                 public void Visit(ElementNode node, INode parentNode)
90                 {
91                         XmlName propertyName = XmlName.Empty;
92
93                         //Simplify ListNodes with single elements
94                         var pList = parentNode as ListNode;
95                         if (pList != null && pList.CollectionItems.Count == 1) {
96                                 propertyName = pList.XmlName;
97                                 parentNode = parentNode.Parent;
98                         }
99
100                         if ((propertyName != XmlName.Empty || TryGetPropertyName(node, parentNode, out propertyName)) && skips.Contains(propertyName))
101                                 return;
102
103                         if (propertyName == XmlName._CreateContent) {
104                                 SetDataTemplate((IElementNode)parentNode, node, Context, node);
105                                 return;
106                         }
107
108                         //if this node is an IMarkupExtension, invoke ProvideValue() and replace the variable
109                         var vardef = Context.Variables[node];
110                         var vardefref = new VariableDefinitionReference(vardef);
111                         var localName = propertyName.LocalName;
112                         TypeReference declaringTypeReference = null;
113                         FieldReference bpRef = null;
114                         var _ = false;
115                         PropertyDefinition propertyRef = null;
116                         if (parentNode is IElementNode && propertyName != XmlName.Empty) {
117                                 bpRef = GetBindablePropertyReference(Context.Variables [(IElementNode)parentNode], propertyName.NamespaceURI, ref localName, out _, Context, node);
118                                 propertyRef = Context.Variables [(IElementNode)parentNode].VariableType.GetProperty(pd => pd.Name == localName, out declaringTypeReference);
119                         }
120                         Context.IL.Append(ProvideValue(vardefref, Context, Module, node, bpRef:bpRef, propertyRef:propertyRef, propertyDeclaringTypeRef: declaringTypeReference));
121                         if (vardef != vardefref.VariableDefinition)
122                         {
123                                 vardef = vardefref.VariableDefinition;
124                                 Context.Body.Variables.Add(vardef);
125                                 Context.Variables[node] = vardef;
126                         }
127
128                         if (propertyName != XmlName.Empty) {
129                                 if (skips.Contains(propertyName))
130                                         return;
131                                 if (parentNode is IElementNode && ((IElementNode)parentNode).SkipProperties.Contains (propertyName))
132                                         return;
133
134                                 Context.IL.Append(SetPropertyValue(Context.Variables[(IElementNode)parentNode], propertyName, node, Context, node));
135                         }
136                         else if (IsCollectionItem(node, parentNode) && parentNode is IElementNode) {
137                                 var parentVar = Context.Variables[(IElementNode)parentNode];
138                                 string contentProperty;
139
140                                 if (CanAddToResourceDictionary(parentVar, parentVar.VariableType, node, node, Context)) {
141                                         Context.IL.Append(parentVar.LoadAs(Module.GetTypeDefinition(("XSF", "Xamarin.Forms", "ResourceDictionary")), Module));
142                                         Context.IL.Append(AddToResourceDictionary(node, node, Context));
143                                 }
144                                 // Collection element, implicit content, or implicit collection element.
145                                 else if (   parentVar.VariableType.ImplementsInterface(Module.ImportReference(("mscorlib", "System.Collections", "IEnumerable")))
146                                                  && parentVar.VariableType.GetMethods(md => md.Name == "Add" && md.Parameters.Count == 1, Module).Any()) {
147                                         var elementType = parentVar.VariableType;
148                                         var adderTuple = elementType.GetMethods(md => md.Name == "Add" && md.Parameters.Count == 1, Module).First();
149                                         var adderRef = Module.ImportReference(adderTuple.Item1);
150                                         adderRef = Module.ImportReference(adderRef.ResolveGenericParameters(adderTuple.Item2, Module));
151
152                                         Context.IL.Emit(Ldloc, parentVar);
153                                         Context.IL.Append(vardef.LoadAs(adderRef.Parameters[0].ParameterType.ResolveGenericParameters(adderRef), Module));
154                                         Context.IL.Emit(Callvirt, adderRef);
155                                         if (adderRef.ReturnType.FullName != "System.Void")
156                                                 Context.IL.Emit(Pop);
157                                 }
158                                 else if ((contentProperty = GetContentProperty(parentVar.VariableType)) != null) {
159                                         var name = new XmlName(node.NamespaceURI, contentProperty);
160                                         if (skips.Contains(name))
161                                                 return;
162                                         if (parentNode is IElementNode && ((IElementNode)parentNode).SkipProperties.Contains (propertyName))
163                                                 return;
164                                         Context.IL.Append(SetPropertyValue(Context.Variables[(IElementNode)parentNode], name, node, Context, node));
165                                 }
166                                 else
167                                         throw new XamlParseException($"Can not set the content of {((IElementNode)parentNode).XmlType.Name} as it doesn't have a ContentPropertyAttribute", node);
168                         }
169                         else if (IsCollectionItem(node, parentNode) && parentNode is ListNode)
170                         {
171 //                              IL_000d:  ldloc.2
172 //                              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()
173 //                              IL_0013:  ldloc.0
174 //                              IL_0014:  callvirt instance void class [mscorlib]System.Collections.Generic.ICollection`1<class [Xamarin.Forms.Core]Xamarin.Forms.View>::Add(!0)
175
176                                 var parentList = (ListNode)parentNode;
177                                 var parent = Context.Variables[((IElementNode)parentNode.Parent)];
178
179                                 if (skips.Contains(parentList.XmlName))
180                                         return;
181                                 if (parentNode is IElementNode && ((IElementNode)parentNode).SkipProperties.Contains (propertyName))
182                                         return;
183                                 var elementType = parent.VariableType;
184                                 var localname = parentList.XmlName.LocalName;
185
186                                 TypeReference propertyType;
187                                 Context.IL.Append(GetPropertyValue(parent, parentList.XmlName, Context, node, out propertyType));
188
189                                 if (CanAddToResourceDictionary(parent, propertyType, node, node, Context)) {
190                                         Context.IL.Append(AddToResourceDictionary(node, node, Context));
191                                         return;
192                                 }
193                                 var adderTuple = propertyType.GetMethods(md => md.Name == "Add" && md.Parameters.Count == 1, Module).FirstOrDefault();
194                                 if (adderTuple == null)
195                                         throw new XamlParseException($"Can not Add() elements to {parent.VariableType}.{localname}", node);
196                                 var adderRef = Module.ImportReference(adderTuple.Item1);
197                                 adderRef = Module.ImportReference(adderRef.ResolveGenericParameters(adderTuple.Item2, Module));
198
199                                 Context.IL.Append(vardef.LoadAs(adderRef.Parameters[0].ParameterType.ResolveGenericParameters(adderRef), Module));
200                                 Context.IL.Emit(OpCodes.Callvirt, adderRef);
201                                 if (adderRef.ReturnType.FullName != "System.Void")
202                                                 Context.IL.Emit(OpCodes.Pop);
203                         }
204                 }
205
206                 public void Visit(RootNode node, INode parentNode)
207                 {
208                 }
209
210                 public void Visit(ListNode node, INode parentNode)
211                 {
212                 }
213
214                 public static bool TryGetPropertyName(INode node, INode parentNode, out XmlName name)
215                 {
216                         name = default(XmlName);
217                         if (!(parentNode is IElementNode parentElement))
218                                 return false;
219                         foreach (var kvp in parentElement.Properties)
220                         {
221                                 if (kvp.Value != node)
222                                         continue;
223                                 name = kvp.Key;
224                                 return true;
225                         }
226                         return false;
227                 }
228
229                 static bool IsCollectionItem(INode node, INode parentNode)
230                 {
231                         if (!(parentNode is IListNode parentList))
232                                 return false;
233                         return parentList.CollectionItems.Contains(node);
234                 }
235
236                 internal static string GetContentProperty(TypeReference typeRef)
237                 {
238                         var typeDef = typeRef.ResolveCached();
239                         var attributes = typeDef.CustomAttributes;
240                         var attr =
241                                 attributes.FirstOrDefault(cad => ContentPropertyAttribute.ContentPropertyTypes.Contains(cad.AttributeType.FullName));
242                         if (attr != null)
243                                 return attr.ConstructorArguments[0].Value as string;
244                         if (typeDef.BaseType == null)
245                                 return null;
246                         return GetContentProperty(typeDef.BaseType);
247                 }
248
249                 public static IEnumerable<Instruction> ProvideValue(VariableDefinitionReference vardefref, ILContext context,
250                                                                     ModuleDefinition module, ElementNode node, FieldReference bpRef = null,
251                                                                     PropertyReference propertyRef = null, TypeReference propertyDeclaringTypeRef = null)
252                 {
253                         GenericInstanceType markupExtension;
254                         IList<TypeReference> genericArguments;
255                         if (vardefref.VariableDefinition.VariableType.FullName == "Xamarin.Forms.Xaml.ArrayExtension" &&
256                             vardefref.VariableDefinition.VariableType.ImplementsGenericInterface("Xamarin.Forms.Xaml.IMarkupExtension`1",
257                                     out markupExtension, out genericArguments))
258                         {
259                                 var markExt = markupExtension.ResolveCached();
260                                 var provideValueInfo = markExt.Methods.First(md => md.Name == "ProvideValue");
261                                 var provideValue = module.ImportReference(provideValueInfo);
262                                 provideValue =
263                                         module.ImportReference(provideValue.ResolveGenericParameters(markupExtension, module));
264
265                                 var typeNode = node.Properties[new XmlName("", "Type")];
266                                 TypeReference arrayTypeRef;
267                                 if (context.TypeExtensions.TryGetValue(typeNode, out arrayTypeRef))
268                                         vardefref.VariableDefinition = new VariableDefinition(module.ImportReference(arrayTypeRef.MakeArrayType()));
269                                 else
270                                         vardefref.VariableDefinition = new VariableDefinition(module.ImportReference(genericArguments.First()));
271                                 foreach (var instruction in context.Variables[node].LoadAs(markupExtension, module))
272                                         yield return instruction;
273                                 foreach (var instruction in node.PushServiceProvider(context, bpRef, propertyRef, propertyDeclaringTypeRef))
274                                         yield return instruction;
275                                 yield return Instruction.Create(OpCodes.Callvirt, provideValue);
276
277                                 if (arrayTypeRef != null)
278                                         yield return Instruction.Create(OpCodes.Castclass, module.ImportReference(arrayTypeRef.MakeArrayType()));
279                                 yield return Instruction.Create(OpCodes.Stloc, vardefref.VariableDefinition);
280                         }
281                         else if (vardefref.VariableDefinition.VariableType.ImplementsGenericInterface("Xamarin.Forms.Xaml.IMarkupExtension`1",
282                                 out markupExtension, out genericArguments))
283                         {
284                                 var acceptEmptyServiceProvider = vardefref.VariableDefinition.VariableType.GetCustomAttribute(module, ("XSF", "Xamarin.Forms.Xaml", "AcceptEmptyServiceProviderAttribute")) != null;
285                                 if (   vardefref.VariableDefinition.VariableType.FullName == "Xamarin.Forms.Xaml.BindingExtension"
286                                     && (node.Properties == null || !node.Properties.ContainsKey(new XmlName("", "Source"))) //do not compile bindings if Source is set
287                                     && bpRef != null //do not compile bindings if we're not gonna SetBinding
288                                         )
289                                         foreach (var instruction in CompileBindingPath(node, context, vardefref.VariableDefinition))
290                                                 yield return instruction;
291
292                                 var markExt = markupExtension.ResolveCached();
293                                 var provideValueInfo = markExt.Methods.First(md => md.Name == "ProvideValue");
294                                 var provideValue = module.ImportReference(provideValueInfo);
295                                 provideValue =
296                                         module.ImportReference(provideValue.ResolveGenericParameters(markupExtension, module));
297
298                                 vardefref.VariableDefinition = new VariableDefinition(module.ImportReference(genericArguments.First()));
299                                 foreach (var instruction in context.Variables[node].LoadAs(markupExtension, module))
300                                         yield return instruction;
301                                 if (acceptEmptyServiceProvider)
302                                         yield return Instruction.Create(OpCodes.Ldnull);
303                                 else
304                                         foreach (var instruction in node.PushServiceProvider(context, bpRef, propertyRef, propertyDeclaringTypeRef))
305                                                 yield return instruction;
306                                 yield return Instruction.Create(OpCodes.Callvirt, provideValue);
307                                 yield return Instruction.Create(OpCodes.Stloc, vardefref.VariableDefinition);
308                         }
309                         else if (context.Variables[node].VariableType.ImplementsInterface(module.ImportReference(("XSF", "Xamarin.Forms.Xaml", "IMarkupExtension"))))
310                         {
311                                 var acceptEmptyServiceProvider = context.Variables[node].VariableType.GetCustomAttribute(module, ("XSF", "Xamarin.Forms.Xaml", "AcceptEmptyServiceProviderAttribute")) != null;
312                                 var markupExtensionType = ("XSF", "Xamarin.Forms.Xaml", "IMarkupExtension");
313                                 vardefref.VariableDefinition = new VariableDefinition(module.TypeSystem.Object);
314                                 foreach (var instruction in context.Variables[node].LoadAs(module.GetTypeDefinition(markupExtensionType), module))
315                                         yield return instruction;
316                                 if (acceptEmptyServiceProvider)
317                                         yield return Create(Ldnull);
318                                 else
319                                         foreach (var instruction in node.PushServiceProvider(context, bpRef, propertyRef, propertyDeclaringTypeRef))
320                                                 yield return instruction;
321                                 yield return Create(Callvirt, module.ImportMethodReference(markupExtensionType,
322                                                                                                                                                    methodName: "ProvideValue",
323                                                                                                                                                    parameterTypes: new[] { ("System.ComponentModel", "System", "IServiceProvider") }));
324                                 yield return Create(Stloc, vardefref.VariableDefinition);
325                         }
326                         else if (context.Variables[node].VariableType.ImplementsInterface(module.ImportReference(("XSF", "Xamarin.Forms.Xaml", "IValueProvider"))))
327                         {
328                                 var acceptEmptyServiceProvider = context.Variables[node].VariableType.GetCustomAttribute(module, ("XSF", "Xamarin.Forms.Xaml", "AcceptEmptyServiceProviderAttribute")) != null;
329                                 var valueProviderType = context.Variables[node].VariableType;
330                                 //If the IValueProvider has a ProvideCompiledAttribute that can be resolved, shortcut this
331                                 var compiledValueProviderName = valueProviderType?.GetCustomAttribute(module, ("XSF", "Xamarin.Forms.Xaml", "ProvideCompiledAttribute"))?.ConstructorArguments?[0].Value as string;
332                                 Type compiledValueProviderType;
333                                 if (compiledValueProviderName != null && (compiledValueProviderType = Type.GetType(compiledValueProviderName)) != null) {
334                                         var compiledValueProvider = Activator.CreateInstance(compiledValueProviderType);
335                                         var cProvideValue = typeof(ICompiledValueProvider).GetMethods().FirstOrDefault(md => md.Name == "ProvideValue");
336                                         var instructions = (IEnumerable<Instruction>)cProvideValue.Invoke(compiledValueProvider, new object[] {
337                                                 vardefref,
338                                                 context.Body.Method.Module,
339                                                 node as BaseNode,
340                                                 context});
341                                         foreach (var i in instructions)
342                                                 yield return i;
343                                         yield break;
344                                 }
345
346                                 var valueProviderInterface = ("XSF", "Xamarin.Forms.Xaml", "IValueProvider");
347                                 vardefref.VariableDefinition = new VariableDefinition(module.TypeSystem.Object);
348                                 foreach (var instruction in context.Variables[node].LoadAs(module.GetTypeDefinition(valueProviderInterface), module))
349                                         yield return instruction;
350                                 if (acceptEmptyServiceProvider)
351                                         yield return Create(Ldnull);
352                                 else
353                                         foreach (var instruction in node.PushServiceProvider(context, bpRef, propertyRef, propertyDeclaringTypeRef))
354                                                 yield return instruction;
355                                 yield return Create(Callvirt, module.ImportMethodReference(valueProviderInterface,
356                                                                                                                                                    methodName: "ProvideValue",
357                                                                                                                                                    parameterTypes: new[] { ("System.ComponentModel", "System", "IServiceProvider") }));
358                                 yield return Create(Stloc, vardefref.VariableDefinition);
359                         }
360                 }
361
362                 //Once we get compiled IValueProvider, this will move to the BindingExpression
363                 static IEnumerable<Instruction> CompileBindingPath(ElementNode node, ILContext context, VariableDefinition bindingExt)
364                 {
365                         //TODO support casting operators
366                         var module = context.Module;
367
368                         if (!node.Properties.TryGetValue(new XmlName("", "Path"), out INode pathNode) && node.CollectionItems.Any())
369                                 pathNode = node.CollectionItems[0];
370                         var path = (pathNode as ValueNode)?.Value as string;
371                         if (   !node.Properties.TryGetValue(new XmlName("", "Mode"), out INode modeNode)
372                                 || !Enum.TryParse((modeNode as ValueNode)?.Value as string, true, out BindingMode declaredmode))
373                                 declaredmode = BindingMode.TwoWay;  //meaning the mode isn't specified in the Binding extension. generate getters, setters, handlers
374
375                         INode dataTypeNode = null;
376                         IElementNode n = node;
377                         while (n != null) {
378                                 if (n.Properties.TryGetValue(XmlName.xDataType, out dataTypeNode))
379                                         break;
380                                 n = n.Parent as IElementNode;
381                         }
382
383                         if (dataTypeNode is null)
384                                 yield break;
385
386                         if (   dataTypeNode is ElementNode enode
387                                 && enode.XmlType.NamespaceUri == XamlParser.X2009Uri
388                                 && enode.XmlType.Name == nameof(Xamarin.Forms.Xaml.NullExtension))
389                                 yield break;
390
391                         string dataType = null;
392
393                         if (   dataTypeNode is ElementNode elementNode
394                                 && elementNode.XmlType.NamespaceUri == XamlParser.X2009Uri
395                                 && elementNode.XmlType.Name == nameof(Xamarin.Forms.Xaml.TypeExtension)
396                                 && elementNode.Properties.ContainsKey(new XmlName("", nameof(Xamarin.Forms.Xaml.TypeExtension.TypeName)))
397                                 && (elementNode.Properties[new XmlName("", nameof(Xamarin.Forms.Xaml.TypeExtension.TypeName))] as ValueNode)?.Value is string stringtype)
398                                 dataType = stringtype;
399
400                         if ((dataTypeNode as ValueNode)?.Value is string sType)
401                                 dataType = sType;
402
403                         if (dataType is null)
404                                 throw new XamlParseException("x:DataType expects a string literal, an {x:Type} markup or {x:Null}", dataTypeNode as IXmlLineInfo);
405
406                         var prefix = dataType.Contains(":") ? dataType.Substring(0, dataType.IndexOf(":", StringComparison.Ordinal)) : "";
407                         var namespaceuri = node.NamespaceResolver.LookupNamespace(prefix) ?? "";
408                         if (!string.IsNullOrEmpty(prefix) && string.IsNullOrEmpty(namespaceuri))
409                                 throw new XamlParseException($"Undeclared xmlns prefix '{prefix}'", dataTypeNode as IXmlLineInfo);
410
411                         var dtXType = new XmlType(namespaceuri, dataType, null);
412
413                         var tSourceRef = dtXType.GetTypeReference(module, (IXmlLineInfo)node);
414                         if (tSourceRef == null)
415                                 yield break; //throw
416
417                         var properties = ParsePath(path, tSourceRef, node as IXmlLineInfo, module);
418                         TypeReference tPropertyRef = tSourceRef;
419                         if (properties != null && properties.Count > 0) {
420                                 var lastProp = properties[properties.Count - 1];
421                                 tPropertyRef = lastProp.property.PropertyType.ResolveGenericParameters(lastProp.propDeclTypeRef);
422                         }
423                         tPropertyRef = module.ImportReference(tPropertyRef);
424                         var valuetupleRef = context.Module.ImportReference(module.ImportReference(("mscorlib", "System", "ValueTuple`2")).MakeGenericInstanceType(new[] { tPropertyRef, module.TypeSystem.Boolean }));
425                         var funcRef = module.ImportReference(module.ImportReference(("mscorlib", "System", "Func`2")).MakeGenericInstanceType(new [] { tSourceRef, valuetupleRef }));
426                         var actionRef = module.ImportReference(module.ImportReference(("mscorlib", "System", "Action`2")).MakeGenericInstanceType(new [] { tSourceRef, tPropertyRef }));
427                         var funcObjRef = module.ImportReference(module.ImportReference(("mscorlib", "System", "Func`2")).MakeGenericInstanceType(new [] { tSourceRef, module.TypeSystem.Object }));
428                         var tupleRef = module.ImportReference(module.ImportReference(("mscorlib", "System", "Tuple`2")).MakeGenericInstanceType(new [] { funcObjRef, module.TypeSystem.String}));
429                         var typedBindingRef = module.ImportReference(module.ImportReference(("XSF", "Xamarin.Forms.Internals", "TypedBinding`2")).MakeGenericInstanceType(new [] { tSourceRef, tPropertyRef}));
430
431                         //FIXME: make sure the non-deprecated one is used
432                         var ctorInfo =  module.ImportReference(typedBindingRef.ResolveCached().Methods.FirstOrDefault(md =>
433                                            md.IsConstructor
434                                         && !md.IsStatic
435                                         && md.Parameters.Count == 3
436                                         && !md.HasCustomAttributes (module.ImportReference(("mscorlib", "System", "ObsoleteAttribute")))));
437                         var ctorinforef = ctorInfo.MakeGeneric(typedBindingRef, funcRef, actionRef, tupleRef);
438
439                         var bindingExtensionType = ("XSF", "Xamarin.Forms.Xaml", "BindingExtension");
440
441                         foreach (var instruction in bindingExt.LoadAs(module.GetTypeDefinition(bindingExtensionType), module))
442                                 yield return instruction;
443                         foreach (var instruction in CompiledBindingGetGetter(tSourceRef, tPropertyRef, properties, node, context))
444                                 yield return instruction;
445                         if (declaredmode != BindingMode.OneTime && declaredmode != BindingMode.OneWay) { //if the mode is explicitly 1w, or 1t, no need for setters
446                                 foreach (var instruction in CompiledBindingGetSetter(tSourceRef, tPropertyRef, properties, node, context))
447                                         yield return instruction;
448                         } else
449                                 yield return Create(Ldnull);
450                         if (declaredmode != BindingMode.OneTime) { //if the mode is explicitly 1t, no need for handlers
451                                 foreach (var instruction in CompiledBindingGetHandlers(tSourceRef, tPropertyRef, properties, node, context))
452                                         yield return instruction;
453                         } else
454                                 yield return Create(Ldnull);
455                         yield return Create(Newobj, module.ImportReference(ctorinforef));
456                         yield return Create(Callvirt, module.ImportPropertySetterReference(bindingExtensionType, propertyName: "TypedBinding"));
457                 }
458
459                 static IList<(PropertyDefinition property, TypeReference propDeclTypeRef, string indexArg)> ParsePath(string path, TypeReference tSourceRef, IXmlLineInfo lineInfo, ModuleDefinition module)
460                 {
461                         if (string.IsNullOrWhiteSpace(path))
462                                 return null;
463                         path = path.Trim(' ', '.'); //trim leading or trailing dots
464                         var parts = path.Split(new [] { '.' }, StringSplitOptions.RemoveEmptyEntries);
465                         var properties = new List<(PropertyDefinition property, TypeReference propDeclTypeRef, string indexArg)>();
466
467                         var previousPartTypeRef = tSourceRef;
468                         foreach (var part in parts) {
469                                 var p = part;
470                                 string indexArg = null;
471                                 var lbIndex = p.IndexOf('[');
472                                 if (lbIndex != -1) {
473                                         var rbIndex = p.LastIndexOf(']');
474                                         if (rbIndex == -1)
475                                                 throw new XamlParseException("Binding: Indexer did not contain closing bracket", lineInfo);
476
477                                         var argLength = rbIndex - lbIndex - 1;
478                                         if (argLength == 0)
479                                                 throw new XamlParseException("Binding: Indexer did not contain arguments", lineInfo);
480
481                                         indexArg = p.Substring(lbIndex + 1, argLength).Trim();
482                                         if (indexArg.Length == 0)
483                                                 throw new XamlParseException("Binding: Indexer did not contain arguments", lineInfo);
484
485                                         p = p.Substring(0, lbIndex);
486                                         p = p.Trim();
487                                 }
488
489                                 if (p.Length > 0) {
490                                         var property = previousPartTypeRef.GetProperty(pd => pd.Name == p && pd.GetMethod != null && pd.GetMethod.IsPublic, out var propDeclTypeRef)
491                                                                           ?? throw new XamlParseException($"Binding: Property '{p}' not found on '{previousPartTypeRef}'", lineInfo);
492                                         properties.Add((property, propDeclTypeRef, null));
493                                         previousPartTypeRef = property.PropertyType.ResolveGenericParameters(propDeclTypeRef);
494                                 }
495                                 if (indexArg != null) {
496                                         var defaultMemberAttribute = previousPartTypeRef.GetCustomAttribute(module, ("mscorlib", "System.Reflection", "DefaultMemberAttribute"));
497                                         var indexerName = defaultMemberAttribute?.ConstructorArguments?.FirstOrDefault().Value as string ?? "Item";
498                                         PropertyDefinition indexer = null;
499                                         TypeReference indexerDeclTypeRef = null;
500                                         if (int.TryParse(indexArg, out _))
501                                                 indexer = previousPartTypeRef.GetProperty(pd => pd.Name == indexerName
502                                                                                                                                          && pd.GetMethod != null
503                                                                                                                                          && TypeRefComparer.Default.Equals(pd.GetMethod.Parameters[0].ParameterType.ResolveGenericParameters(previousPartTypeRef), module.ImportReference(("mscorlib", "System", "Int32")))
504                                                                                                                                          && pd.GetMethod.IsPublic, out indexerDeclTypeRef);
505                                         indexer = indexer ?? previousPartTypeRef.GetProperty(pd => pd.Name == indexerName
506                                                                                                                                                         && pd.GetMethod != null
507                                                                                                                                                         && TypeRefComparer.Default.Equals(pd.GetMethod.Parameters[0].ParameterType.ResolveGenericParameters(previousPartTypeRef), module.ImportReference(("mscorlib", "System", "String")))
508                                                                                                                                                         && pd.GetMethod.IsPublic, out indexerDeclTypeRef);
509                                         indexer = indexer ?? previousPartTypeRef.GetProperty(pd => pd.Name == indexerName
510                                                                                                                                                         && pd.GetMethod != null
511                                                                                                                                                         && TypeRefComparer.Default.Equals(pd.GetMethod.Parameters[0].ParameterType.ResolveGenericParameters(previousPartTypeRef), module.ImportReference(("mscorlib", "System", "Object")))
512                                                                                                                                                         && pd.GetMethod.IsPublic, out indexerDeclTypeRef);
513
514                                         properties.Add((indexer, indexerDeclTypeRef, indexArg));
515                                         var indexType = indexer.GetMethod.Parameters[0].ParameterType.ResolveGenericParameters(indexerDeclTypeRef);
516                                         if (!TypeRefComparer.Default.Equals(indexType, module.TypeSystem.String) && !TypeRefComparer.Default.Equals(indexType, module.TypeSystem.Int32))
517                                                 throw new XamlParseException($"Binding: Unsupported indexer index type: {indexType.FullName}", lineInfo);
518                                         previousPartTypeRef = indexer.PropertyType.ResolveGenericParameters(indexerDeclTypeRef);
519                                 }
520                         }
521                         return properties;
522                 }
523
524                 static IEnumerable<Instruction> DigProperties(IEnumerable<(PropertyDefinition property, TypeReference propDeclTypeRef, string indexArg)> properties, Dictionary<TypeReference, VariableDefinition> locs, Func<Instruction> fallback, IXmlLineInfo lineInfo, ModuleDefinition module)
525                 {
526                         var first = true;
527
528                         foreach (var (property, propDeclTypeRef, indexArg) in properties) {
529                                 if (!first && propDeclTypeRef.IsValueType) {
530                                         var importedPropDeclTypeRef = module.ImportReference(propDeclTypeRef);
531
532                                         if (!locs.TryGetValue(importedPropDeclTypeRef, out var loc)) {
533                                                 loc = new VariableDefinition(importedPropDeclTypeRef);
534                                                 locs[importedPropDeclTypeRef] = loc;
535                                         }
536
537                                         yield return Create(Stloc, loc);
538                                         yield return Create(Ldloca, loc);
539                                 }
540
541                                 if (fallback != null && !propDeclTypeRef.IsValueType) {
542                                         yield return Create(Dup);
543                                         yield return Create(Brfalse, fallback());
544                                 }
545
546                                 if (indexArg != null) {
547                                         var indexType = property.GetMethod.Parameters[0].ParameterType.ResolveGenericParameters(propDeclTypeRef);
548                                         if (TypeRefComparer.Default.Equals(indexType, module.TypeSystem.String))
549                                                 yield return Create(Ldstr, indexArg);
550                                         else if (TypeRefComparer.Default.Equals(indexType, module.TypeSystem.Int32) && int.TryParse(indexArg, out int index))
551                                                 yield return Create(Ldc_I4, index);
552                                         else
553                                                 throw new XamlParseException($"Binding: {indexArg} could not be parsed as an index for a {property.Name}", lineInfo);
554                                 }
555
556                                 var getMethod = module.ImportReference((module.ImportReference(property.GetMethod)).ResolveGenericParameters(propDeclTypeRef, module));
557
558                                 if (property.GetMethod.IsVirtual)
559                                         yield return Create(Callvirt, getMethod);
560                                 else
561                                         yield return Create(Call, getMethod);
562
563                                 first = false;
564                         }
565                 }
566
567                 static IEnumerable<Instruction> CompiledBindingGetGetter(TypeReference tSourceRef, TypeReference tPropertyRef, IList<(PropertyDefinition property, TypeReference propDeclTypeRef, string indexArg)> properties, ElementNode node, ILContext context)
568                 {
569 //                              .method private static hidebysig default valuetype[mscorlib] System.ValueTuple`2<string, bool> '<Main>m__0' (class ViewModel A_0)  cil managed
570 //                              {
571 //                                      .custom instance void class [mscorlib] System.Runtime.CompilerServices.CompilerGeneratedAttribute::'.ctor'() =  (01 00 00 00 ) // ....
572 //                                      IL_0000:  ldarg.0
573 //                                      IL_0001:  dup
574 //                                      IL_0002:  ldnull
575 //                                      IL_0003:  ceq
576 //                                      IL_0005:  brfalse IL_0013
577 //                                      IL_000a:  pop
578 //                                      IL_000b:  ldnull
579 //                                      IL_000c:  ldc.i4.0
580 //                                      IL_000d:  newobj instance void valuetype[mscorlib]System.ValueTuple`2<string, bool>::'.ctor'(!0, !1)
581 //                                      IL_0012:  ret
582 //                                      IL_0013:  nop
583 //                                      IL_0014:  call instance string class ViewModel::get_Text()
584 //                                      IL_0019:  ldc.i4.1
585 //                                      IL_001a:  newobj instance void valuetype[mscorlib]System.ValueTuple`2<string, bool>::'.ctor'(!0, !1)
586 //                                      IL_001f:  ret
587 //                              }
588
589                         var module = context.Module;
590                         var tupleRef = module.ImportReference(module.ImportReference(("mscorlib", "System", "ValueTuple`2")).MakeGenericInstanceType(new[] { tPropertyRef, module.TypeSystem.Boolean }));
591                         var tupleCtorRef = module.ImportCtorReference(tupleRef, 2);
592                         tupleCtorRef = module.ImportReference(tupleCtorRef.ResolveGenericParameters(tupleRef, module));
593                         var getter = new MethodDefinition($"<{context.Body.Method.Name}>typedBindingsM__{typedBindingCount++}",
594                                                                                           MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Static,
595                                                                                           tupleRef) {
596                                 Parameters = { new ParameterDefinition(tSourceRef) },
597                                 CustomAttributes = { new CustomAttribute (module.ImportCtorReference(("mscorlib", "System.Runtime.CompilerServices", "CompilerGeneratedAttribute"), parameterTypes: null)) }
598                         };
599
600                         getter.Body.InitLocals = true;
601                         var il = getter.Body.GetILProcessor();
602
603                         if (properties == null || properties.Count == 0) { //return self
604                                 il.Emit(Ldarg_0);
605                                 il.Emit(Ldc_I4_1); //true
606                                 il.Emit(Newobj, tupleCtorRef);
607                                 il.Emit(Ret);
608                         }
609                         else {
610                                 var locs = new Dictionary<TypeReference, VariableDefinition>();
611
612                                 if (tSourceRef.IsValueType)
613                                         il.Emit(Ldarga_S, (byte)0);
614                                 else
615                                         il.Emit(Ldarg_0);
616
617                                 Instruction pop = null;
618                                 il.Append(DigProperties(properties, locs, () => {
619                                         if (pop == null)
620                                                 pop = Create(Pop);
621
622                                         return pop;
623                                 }, node as IXmlLineInfo, module));
624
625                                 foreach (var loc in locs.Values)
626                                         getter.Body.Variables.Add(loc);
627
628                                 il.Emit(Ldc_I4_1); //true
629                                 il.Emit(Newobj, tupleCtorRef);
630                                 il.Emit(Ret);
631
632                                 if (pop != null) {
633                                         if (!locs.TryGetValue(tupleRef, out var defaultValueVarDef)) {
634                                                 defaultValueVarDef = new VariableDefinition(tupleRef);
635                                                 getter.Body.Variables.Add(defaultValueVarDef);
636                                         }
637
638                                         il.Append(pop);
639                                         il.Emit(Ldloca_S, defaultValueVarDef);
640                                         il.Emit(Initobj, tupleRef);
641                                         il.Emit(Ldloc, defaultValueVarDef);
642                                         il.Emit(Ret);
643                                 }
644                         }
645                         context.Body.Method.DeclaringType.Methods.Add(getter);
646
647 //                      IL_02fa:  ldnull
648 //                      IL_02fb:  ldftn valuetype[mscorlib]System.ValueTuple`2 <string,bool> class Test::'<Main>m__0'(class ViewModel)
649 //                      IL_0301:  newobj instance void class [mscorlib] System.Func`2<class ViewModel, valuetype[mscorlib] System.ValueTuple`2<string, bool>>::'.ctor'(object, native int)
650                         yield return Create(Ldnull);
651                         yield return Create(Ldftn, getter);
652                         yield return Create(Newobj, module.ImportCtorReference(("mscorlib", "System", "Func`2"), paramCount: 2, classArguments: new[] { tSourceRef, tupleRef }));
653                 }
654
655                 static IEnumerable<Instruction> CompiledBindingGetSetter(TypeReference tSourceRef, TypeReference tPropertyRef, IList<(PropertyDefinition property, TypeReference propDeclTypeRef, string indexArg)> properties, ElementNode node, ILContext context)
656                 {
657                         if (properties == null || properties.Count == 0) {
658                                 yield return Create(Ldnull);
659                                 yield break;
660                         }
661
662 //                      .method private static hidebysig default void '<Main>m__1' (class ViewModel vm, string s)  cil managed
663 //                      {
664 //                              .custom instance void class [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::'.ctor'() =  (01 00 00 00 ) // ....
665 //
666 //                              IL_0000:  ldarg.0
667 //                              IL_0001:  callvirt instance class ViewModel class ViewModel::get_Model()
668 //                              IL_0006:  ldarg.1
669 //                              IL_0007:  callvirt instance void class ViewModel::set_Text(string)
670 //                              IL_000c:  ret
671 //                      }
672
673                         var module = context.Module;
674                         var setter = new MethodDefinition($"<{context.Body.Method.Name}>typedBindingsM__{typedBindingCount++}",
675                                                                                           MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Static,
676                                                           module.TypeSystem.Void) {
677                                 Parameters = {
678                                         new ParameterDefinition(tSourceRef),
679                                         new ParameterDefinition(tPropertyRef)
680                                 },
681                                 CustomAttributes = {
682                                         new CustomAttribute (module.ImportCtorReference(("mscorlib", "System.Runtime.CompilerServices", "CompilerGeneratedAttribute"), parameterTypes: null))
683                                 }
684                         };
685                         setter.Body.InitLocals = true;
686
687                         var il = setter.Body.GetILProcessor();
688                         if (!properties.Any() || properties.Last().property.SetMethod == null) {
689                                 yield return Create(Ldnull); //throw or not ?
690                                 yield break;
691                         }
692
693                         var setterRef = module.ImportReference(properties.Last().property.SetMethod);
694                         setterRef = module.ImportReference(setterRef.ResolveGenericParameters(properties.Last().propDeclTypeRef, module));
695
696                         if (tSourceRef.IsValueType)
697                                 il.Emit(Ldarga_S, (byte)0);
698                         else
699                                 il.Emit(Ldarg_0);
700                         var locs = new Dictionary<TypeReference, VariableDefinition>();
701                         Instruction pop = null;
702                         il.Append(DigProperties(properties.Take(properties.Count - 1), locs, () => {
703                                 if (pop == null)
704                                         pop = Instruction.Create(Pop);
705
706                                 return pop;
707                         }, node as IXmlLineInfo, module));
708
709                         foreach (var loc in locs.Values)
710                                 setter.Body.Variables.Add(loc);
711
712                         (PropertyDefinition lastProperty, TypeReference lastPropDeclTypeRef, string lastIndexArg) = properties.Last();
713                         if (lastPropDeclTypeRef.IsValueType) {
714                                 var importedPropDeclTypeRef = module.ImportReference(lastPropDeclTypeRef);
715
716                                 if (!locs.TryGetValue(importedPropDeclTypeRef, out var loc)) {
717                                         loc = new VariableDefinition(importedPropDeclTypeRef);
718                                         setter.Body.Variables.Add(loc);
719                                 }
720
721                                 il.Emit(Stloc, loc);
722                                 il.Emit(Ldloca, loc);
723                         } else {
724                                 if (pop == null)
725                                         pop = Instruction.Create(Pop);
726
727                                 il.Emit(Dup);
728                                 il.Emit(Brfalse, pop);
729                         }
730
731                         if (lastIndexArg != null) {
732                                 var indexType = lastProperty.GetMethod.Parameters[0].ParameterType.ResolveGenericParameters(lastPropDeclTypeRef);
733                                 if (TypeRefComparer.Default.Equals(indexType, module.TypeSystem.String))
734                                         il.Emit(Ldstr, lastIndexArg);
735                                 else if (TypeRefComparer.Default.Equals(indexType, module.TypeSystem.Int32)) {
736                                         if (!int.TryParse(lastIndexArg, out int index))
737                                                 throw new XamlParseException($"Binding: {lastIndexArg} could not be parsed as an index for a {lastProperty.Name}", node as IXmlLineInfo);
738                                         il.Emit(Ldc_I4, index);
739                                 }
740                         }
741
742                         il.Emit(Ldarg_1);
743
744                         if (properties.Last().property.SetMethod.IsVirtual)
745                                 il.Emit(Callvirt, setterRef);
746                         else
747                                 il.Emit(Call, setterRef);
748
749                         il.Emit(Ret);
750
751                         if (pop != null) {
752                                 il.Append(pop);
753                                 il.Emit(Ret);
754                         }
755
756                         context.Body.Method.DeclaringType.Methods.Add(setter);
757
758 //                      IL_0024: ldnull
759 //                      IL_0025: ldftn void class Test::'<Main>m__1'(class ViewModel, string)
760 //                      IL_002b: newobj instance void class [mscorlib]System.Action`2<class ViewModel, string>::'.ctor'(object, native int)
761                         yield return Create(Ldnull);
762                         yield return Create(Ldftn, setter);
763                         yield return Create(Newobj, module.ImportCtorReference(("mscorlib", "System", "Action`2"),
764                                                                                                                                    paramCount: 2,
765                                                                                                                                    classArguments:
766                                                                                                                                    new[] { tSourceRef, tPropertyRef }));
767                 }
768
769                 static IEnumerable<Instruction> CompiledBindingGetHandlers(TypeReference tSourceRef, TypeReference tPropertyRef, IList<(PropertyDefinition property, TypeReference propDeclTypeRef, string indexArg)> properties, ElementNode node, ILContext context)
770                 {
771 //                      .method private static hidebysig default object '<Main>m__2'(class ViewModel vm)  cil managed {
772 //                              .custom instance void class [mscorlib] System.Runtime.CompilerServices.CompilerGeneratedAttribute::'.ctor'() =  (01 00 00 00 ) // ....
773 //                              IL_0000:  ldarg.0
774 //                              IL_0001:  ret
775 //                      } // end of method Test::<Main>m__2
776
777 //                      .method private static hidebysig default object '<Main>m__3' (class ViewModel vm)  cil managed {
778 //                              .custom instance void class [mscorlib] System.Runtime.CompilerServices.CompilerGeneratedAttribute::'.ctor'() =  (01 00 00 00 ) // ....
779 //                              IL_0000:  ldarg.0
780 //                              IL_0001:  callvirt instance class ViewModel class ViewModel::get_Model()
781 //                              IL_0006:  ret
782 //                      }
783
784                         var module = context.Module;
785
786                         var partGetters = new List<MethodDefinition>();
787                         if (properties == null || properties.Count == 0) {
788                                 yield return Create(Ldnull);
789                                 yield break;
790                         }
791
792                         for (int i = 0; i < properties.Count; i++) {
793                                 var tuple = properties [i];
794                                 var partGetter = new MethodDefinition($"<{context.Body.Method.Name}>typedBindingsM__{typedBindingCount++}", MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Static, module.TypeSystem.Object) {
795                                         Parameters = {
796                                                 new ParameterDefinition(tSourceRef)
797                                         },
798                                         CustomAttributes = {
799                                                 new CustomAttribute (module.ImportCtorReference(("mscorlib", "System.Runtime.CompilerServices", "CompilerGeneratedAttribute"), parameterTypes: null))
800                                         }
801                                 };
802                                 partGetter.Body.InitLocals = true;
803                                 var il = partGetter.Body.GetILProcessor();
804
805                                 if (i == 0) { //return self
806                                         il.Emit(Ldarg_0);
807                                         if (tSourceRef.IsValueType)
808                                                 il.Emit(Box, module.ImportReference(tSourceRef));
809
810                                         il.Emit(Ret);
811                                         context.Body.Method.DeclaringType.Methods.Add(partGetter);
812                                         partGetters.Add(partGetter);
813                                         continue;
814                                 }
815
816                                 if (tSourceRef.IsValueType)
817                                         il.Emit(Ldarga_S, (byte)0);
818                                 else
819                                         il.Emit(Ldarg_0);
820                                 var lastGetterTypeRef = properties[i - 1].property.PropertyType;
821                                 var locs = new Dictionary<TypeReference, VariableDefinition>();
822                                 il.Append(DigProperties(properties.Take(i), locs, null, node as IXmlLineInfo, module));
823                                 foreach (var loc in locs.Values)
824                                         partGetter.Body.Variables.Add(loc);
825                                 if (lastGetterTypeRef.IsValueType)
826                                         il.Emit(Box, module.ImportReference(lastGetterTypeRef));
827
828                                 il.Emit(Ret);
829                                 context.Body.Method.DeclaringType.Methods.Add(partGetter);
830                                 partGetters.Add(partGetter);
831                         }
832
833                         var funcObjRef = context.Module.ImportReference(module.ImportReference(("mscorlib", "System", "Func`2")).MakeGenericInstanceType(new [] { tSourceRef, module.TypeSystem.Object }));
834                         var tupleRef = context.Module.ImportReference(module.ImportReference(("mscorlib", "System", "Tuple`2")).MakeGenericInstanceType(new [] { funcObjRef, module.TypeSystem.String }));
835                         var funcCtor = module.ImportReference(funcObjRef.ResolveCached().GetConstructors().First());
836                         funcCtor = funcCtor.MakeGeneric(funcObjRef, new [] { tSourceRef, module.TypeSystem.Object });
837                         var tupleCtor = module.ImportReference(tupleRef.ResolveCached().GetConstructors().First());
838                         tupleCtor = tupleCtor.MakeGeneric(tupleRef, new [] { funcObjRef, module.TypeSystem.String});
839
840 //                      IL_003a:  ldc.i4.2
841 //                      IL_003b:  newarr class [mscorlib] System.Tuple`2<class [mscorlib]System.Func`2<class ViewModel,object>,string>
842
843 //                      IL_0040:  dup
844 //                      IL_0041:  ldc.i4.0
845 //                      IL_0049:  ldnull
846 //                      IL_004a:  ldftn object class Test::'<Main>m__2'(class ViewModel)
847 //                      IL_0050:  newobj instance void class [mscorlib]System.Func`2<class ViewModel, object>::'.ctor'(object, native int)
848 //                      IL_005f:  ldstr "Model"
849 //                      IL_0064:  newobj instance void class [mscorlib]System.Tuple`2<class [mscorlib]System.Func`2<class ViewModel, object>, string>::'.ctor'(!0, !1)
850 //                      IL_0069:  stelem.ref
851
852 //                      IL_006a:  dup
853 //                      IL_006b:  ldc.i4.1
854 //                      IL_0073:  ldnull
855 //                      IL_0074:  ldftn object class Test::'<Main>m__3'(class ViewModel)
856 //                      IL_007a:  newobj instance void class [mscorlib]System.Func`2<class ViewModel, object>::'.ctor'(object, native int)
857 //                      IL_0089:  ldstr "Text"
858 //                      IL_008e:  newobj instance void class [mscorlib]System.Tuple`2<class [mscorlib]System.Func`2<class ViewModel, object>, string>::'.ctor'(!0, !1)
859 //                      IL_0093:  stelem.ref
860
861                         yield return Create(Ldc_I4, properties.Count);
862                         yield return Create(Newarr, tupleRef);
863
864                         for (var i = 0; i < properties.Count; i++) {
865                                 yield return Create(Dup);
866                                 yield return Create(Ldc_I4, i);
867                                 yield return Create(Ldnull);
868                                 yield return Create(Ldftn, partGetters [i]);
869                                 yield return Create(Newobj, module.ImportReference(funcCtor));
870                                 yield return Create(Ldstr, properties [i].Item1.Name);
871                                 yield return Create(Newobj, module.ImportReference(tupleCtor));
872                                 yield return Create(Stelem_Ref);
873                         }
874                 }
875
876                 public static IEnumerable<Instruction> SetPropertyValue(VariableDefinition parent, XmlName propertyName, INode valueNode, ILContext context, IXmlLineInfo iXmlLineInfo)
877                 {
878                         var localName = propertyName.LocalName;
879                         var bpRef = GetBindablePropertyReference(parent, propertyName.NamespaceURI, ref localName, out System.Boolean attached, context, iXmlLineInfo);
880
881                         //If the target is an event, connect
882                         if (CanConnectEvent(parent, localName, valueNode, attached))
883                                 return ConnectEvent(parent, localName, valueNode, iXmlLineInfo, context);
884
885                         //If Value is DynamicResource, SetDynamicResource
886                         if (CanSetDynamicResource(bpRef, valueNode, context))
887                                 return SetDynamicResource(parent, bpRef, valueNode as IElementNode, iXmlLineInfo, context);
888
889                         //If Value is a BindingBase and target is a BP, SetBinding
890                         if (CanSetBinding(bpRef, valueNode, context))
891                                 return SetBinding(parent, bpRef, valueNode as IElementNode, iXmlLineInfo, context);
892
893                         //If it's a BP, SetValue ()
894                         if (CanSetValue(bpRef, attached, valueNode, iXmlLineInfo, context))
895                                 return SetValue(parent, bpRef, valueNode, iXmlLineInfo, context).Concat(RegisterSourceInfo(context, valueNode));
896
897                         //If it's a property, set it
898                         if (CanSet(parent, localName, valueNode, context))
899                                 return Set(parent, localName, valueNode, iXmlLineInfo, context).Concat(RegisterSourceInfo(context, valueNode));
900
901                         //If it's an already initialized property, add to it
902                         if (CanAdd(parent, propertyName, valueNode, iXmlLineInfo, context))
903                                 return Add(parent, propertyName, valueNode, iXmlLineInfo, context).Concat(RegisterSourceInfo(context, valueNode));
904
905                         throw new XamlParseException($"No property, bindable property, or event found for '{localName}', or mismatching type between value and property.", iXmlLineInfo);
906                 }
907
908                 internal static IEnumerable<Instruction> RegisterSourceInfo(ILContext context, INode valueNode)
909                 {
910                         if (!context.DefineDebug)
911                                 yield break;
912                         if (!(valueNode is IXmlLineInfo lineInfo))
913                                 yield break;
914                         if (!(valueNode is IElementNode elementNode))
915                                 yield break;
916                         if (context.Variables[elementNode].VariableType.IsValueType)
917                                 yield break;
918
919                         var module = context.Body.Method.Module;
920
921                         yield return Create(Ldloc, context.Variables[elementNode]);             //target
922
923                         yield return Create(Ldstr, context.XamlFilePath);
924                         yield return Create(Ldc_I4, (int)UriKind.RelativeOrAbsolute);
925                         yield return Create(Newobj, module.ImportCtorReference(("System", "System", "Uri"),
926                                                                                                                                    parameterTypes: new[] {
927                                                                                                                                            ("mscorlib", "System", "String"),
928                                                                                                                                            ("System", "System", "UriKind"),
929                                                                                                                                    }));         //uri
930
931                         yield return Create(Ldc_I4, lineInfo.LineNumber);                               //lineNumber
932                         yield return Create(Ldc_I4, lineInfo.LinePosition);             //linePosition
933
934                         yield return Create(Call, module.ImportMethodReference(("XSF", "Xamarin.Forms.Xaml.Diagnostics", "VisualDiagnostics"),
935                                                                                                                                    methodName: "RegisterSourceInfo",
936                                                                                                                                    parameterTypes: new[] {
937                                                                                                                                            ("mscorlib", "System", "Object"),
938                                                                                                                                            ("System", "System", "Uri"),
939                                                                                                                                            ("mscorlib", "System", "Int32"),
940                                                                                                                                            ("mscorlib", "System", "Int32")},
941                                                                                                                                    isStatic: true));
942                 }
943
944                 public static IEnumerable<Instruction> GetPropertyValue(VariableDefinition parent, XmlName propertyName, ILContext context, IXmlLineInfo lineInfo, out TypeReference propertyType)
945                 {
946                         var module = context.Body.Method.Module;
947                         var localName = propertyName.LocalName;
948                         bool attached;
949                         var bpRef = GetBindablePropertyReference(parent, propertyName.NamespaceURI, ref localName, out attached, context, lineInfo);
950
951                         //If it's a BP, GetValue ()
952                         if (CanGetValue(parent, bpRef, attached, lineInfo, context, out _))
953                                 return GetValue(parent, bpRef, lineInfo, context, out propertyType);
954
955                         //If it's a property, set it
956                         if (CanGet(parent, localName, context, out _))
957                                 return Get(parent, localName, lineInfo, context, out propertyType);
958
959                         throw new XamlParseException($"Property {localName} is not found or does not have an accessible getter", lineInfo);
960                 }
961
962                 static FieldReference GetBindablePropertyReference(VariableDefinition parent, string namespaceURI, ref string localName, out bool attached, ILContext context, IXmlLineInfo iXmlLineInfo)
963                 {
964                         var module = context.Body.Method.Module;
965                         TypeReference declaringTypeReference;
966
967                         //If it's an attached BP, update elementType and propertyName
968                         var bpOwnerType = parent.VariableType;
969                         attached = GetNameAndTypeRef(ref bpOwnerType, namespaceURI, ref localName, context, iXmlLineInfo);
970                         var name = $"{localName}Property";
971                         FieldReference bpRef = bpOwnerType.GetField(fd => fd.Name == name &&
972                                                                                                                 fd.IsStatic &&
973                                                                                                                 (fd.IsPublic || fd.IsAssembly), out declaringTypeReference);
974                         if (bpRef != null) {
975                                 bpRef = module.ImportReference(bpRef.ResolveGenericParameters(declaringTypeReference));
976                                 bpRef.FieldType = module.ImportReference(bpRef.FieldType);
977                         }
978                         return bpRef;
979                 }
980
981                 static bool CanConnectEvent(VariableDefinition parent, string localName, INode valueNode, bool attached)
982                 {
983                         return !attached && valueNode is ValueNode && parent.VariableType.GetEvent(ed => ed.Name == localName, out _) != null;
984                 }
985
986                 static IEnumerable<Instruction> ConnectEvent(VariableDefinition parent, string localName, INode valueNode, IXmlLineInfo iXmlLineInfo, ILContext context)
987                 {
988                         var elementType = parent.VariableType;
989                         var module = context.Body.Method.Module;
990                         TypeReference eventDeclaringTypeRef;
991                         var eventinfo = elementType.GetEvent(ed => ed.Name == localName, out eventDeclaringTypeRef);
992                         var adder = module.ImportReference(eventinfo.AddMethod);
993                         adder = adder.ResolveGenericParameters(eventDeclaringTypeRef, module);
994
995 //                      IL_0007:  ldloc.0
996 //                      IL_0008:  ldarg.0
997 //
998 //                      IL_0009:  ldftn instance void class Xamarin.Forms.Xaml.XamlcTests.MyPage::OnButtonClicked(object, class [mscorlib]System.EventArgs)
999 //OR, if the handler is virtual
1000 //                      IL_000x:  ldarg.0
1001 //                      IL_0009:  ldvirtftn instance void class Xamarin.Forms.Xaml.XamlcTests.MyPage::OnButtonClicked(object, class [mscorlib]System.EventArgs)
1002 //
1003 //                      IL_000f:  newobj instance void class [mscorlib]System.EventHandler::'.ctor'(object, native int)
1004 //                      IL_0014:  callvirt instance void class [Xamarin.Forms.Core]Xamarin.Forms.Button::add_Clicked(class [mscorlib]System.EventHandler)
1005
1006                         var value = ((ValueNode)valueNode).Value;
1007
1008                         yield return Create(Ldloc, parent);
1009                         var declaringType = context.Body.Method.DeclaringType;
1010                         while (declaringType.IsNested)
1011                                 declaringType = declaringType.DeclaringType;
1012                         var handler = declaringType.AllMethods().FirstOrDefault(md => {
1013                                 if (md.methodDef.Name != value as string)
1014                                         return false;
1015
1016                                 //check if the handler signature matches the Invoke signature;
1017                                 var invoke = module.ImportReference(eventinfo.EventType.ResolveCached().GetMethods().First(eventmd => eventmd.Name == "Invoke"));
1018                                 invoke = invoke.ResolveGenericParameters(eventinfo.EventType, module);
1019                                 if (!md.methodDef.ReturnType.InheritsFromOrImplements(invoke.ReturnType) || invoke.Parameters.Count != md.methodDef.Parameters.Count)
1020                                         return false;
1021
1022                                 if (!invoke.ContainsGenericParameter)
1023                                         for (var i = 0; i < invoke.Parameters.Count;i++)
1024                                                 if (!invoke.Parameters[i].ParameterType.InheritsFromOrImplements(md.methodDef.Parameters[i].ParameterType))
1025                                                         return false;
1026                                 //TODO check generic parameters if any
1027
1028                                 return true;
1029                         });
1030                         MethodReference handlerRef = null;
1031                         if (handler.methodDef != null)
1032                                 handlerRef = handler.methodDef.ResolveGenericParameters(handler.declTypeRef, module);
1033                         if (handler.methodDef == null)
1034                                 throw new XamlParseException($"EventHandler \"{value}\" with correct signature not found in type \"{declaringType}\"", iXmlLineInfo);
1035
1036                         //FIXME: eventually get the right ctor instead fo the First() one, just in case another one could exists (not even sure it's possible).
1037                         var ctor = module.ImportReference(eventinfo.EventType.ResolveCached().GetConstructors().First());
1038                         ctor = ctor.ResolveGenericParameters(eventinfo.EventType, module);
1039
1040                         if (handler.methodDef.IsStatic) {
1041                                 yield return Create(Ldnull);
1042                         } else {
1043                                 if (context.Root is VariableDefinition)
1044                                         foreach (var instruction in (context.Root as VariableDefinition).LoadAs(ctor.Parameters[0].ParameterType.ResolveGenericParameters(ctor), module))
1045                                                 yield return instruction;
1046                                 else if (context.Root is FieldDefinition) {
1047                                         yield return Create(Ldarg_0);
1048                                         yield return Create(Ldfld, context.Root as FieldDefinition);
1049                                 } else
1050                                         throw new InvalidProgramException();
1051                         }
1052
1053                         if (handler.methodDef.IsVirtual) {
1054                                 yield return Create(Ldarg_0);
1055                                 yield return Create(Ldvirtftn, handlerRef);
1056                         } else
1057                                 yield return Create(Ldftn, handlerRef);
1058
1059                         yield return Create(Newobj, module.ImportReference(ctor));
1060                         //Check if the handler has the same signature as the ctor (it should)
1061                         yield return Create(Callvirt, module.ImportReference(adder));
1062                 }
1063
1064                 static bool CanSetDynamicResource(FieldReference bpRef, INode valueNode, ILContext context)
1065                 {
1066                         if (bpRef == null)
1067                                 return false;
1068                         var elementNode = valueNode as IElementNode;
1069                         if (elementNode == null)
1070                                 return false;
1071
1072                         VariableDefinition varValue;
1073                         if (!context.Variables.TryGetValue(valueNode as IElementNode, out varValue))
1074                                 return false;
1075                         return varValue.VariableType.FullName == typeof(DynamicResource).FullName;
1076                 }
1077
1078                 static IEnumerable<Instruction> SetDynamicResource(VariableDefinition parent, FieldReference bpRef, IElementNode elementNode, IXmlLineInfo iXmlLineInfo, ILContext context)
1079                 {
1080                         var module = context.Body.Method.Module;
1081                         var dynamicResourceType = ("XSF", "Xamarin.Forms.Internals", "DynamicResource");
1082                         var dynamicResourceHandlerType = ("XSF", "Xamarin.Forms.Internals", "IDynamicResourceHandler");
1083
1084                         foreach (var instruction in parent.LoadAs(module.GetTypeDefinition(dynamicResourceHandlerType), module))
1085                                 yield return instruction;
1086                         yield return Create(Ldsfld, bpRef);
1087                         foreach (var instruction in context.Variables[elementNode].LoadAs(module.GetTypeDefinition(dynamicResourceType), module))
1088                                 yield return instruction;
1089                         yield return Create(Callvirt, module.ImportPropertyGetterReference(dynamicResourceType, propertyName: "Key"));
1090                         yield return Create(Callvirt, module.ImportMethodReference(dynamicResourceHandlerType,
1091                                                                                                                                            methodName: "SetDynamicResource",
1092                                                                                                                                            parameterTypes: new[] {
1093                                                                                                                                                    ("XSF", "Xamarin.Forms", "BindableProperty"),
1094                                                                                                                                                    ("mscorlib", "System", "String"),
1095                                                                                                                                            }));
1096                 }
1097
1098                 static bool CanSetBinding(FieldReference bpRef, INode valueNode, ILContext context)
1099                 {
1100                         var module = context.Body.Method.Module;
1101
1102                         if (bpRef == null)
1103                                 return false;
1104                         if (!(valueNode is IElementNode elementNode))
1105                                 return false;
1106
1107                         if (!context.Variables.TryGetValue(valueNode as IElementNode, out VariableDefinition varValue))
1108                                 return false;
1109                         var implicitOperator = varValue.VariableType.GetImplicitOperatorTo(module.ImportReference(("XSF","Xamarin.Forms","BindingBase")), module);
1110                         if (implicitOperator != null)
1111                                 return true;
1112
1113                         return varValue.VariableType.InheritsFromOrImplements(module.ImportReference(("XSF", "Xamarin.Forms", "BindingBase")));
1114                 }
1115
1116                 static IEnumerable<Instruction> SetBinding(VariableDefinition parent, FieldReference bpRef, IElementNode elementNode, IXmlLineInfo iXmlLineInfo, ILContext context)
1117                 {
1118                         var module = context.Body.Method.Module;
1119                         var bindableObjectType = ("XSF", "Xamarin.Forms", "BindableObject");
1120                         var parameterTypes = new[] {
1121                                 ("XSF", "Xamarin.Forms", "BindableProperty"),
1122                                 ("XSF", "Xamarin.Forms", "BindingBase"),
1123                         };
1124
1125                         //TODO: check if parent is a BP
1126                         foreach (var instruction in parent.LoadAs(module.GetTypeDefinition(bindableObjectType), module))
1127                                 yield return instruction;
1128                         yield return Create(Ldsfld, bpRef);
1129                         foreach (var instruction in context.Variables [elementNode].LoadAs(module.GetTypeDefinition(parameterTypes[1]), module))
1130                                 yield return instruction;
1131                         yield return Create(Callvirt, module.ImportMethodReference(("XSF", "Xamarin.Forms", "BindableObject"),
1132                                                                                                                                            methodName: "SetBinding",
1133                                                                                                                                            parameterTypes: parameterTypes));
1134                 }
1135
1136                 static bool CanSetValue(FieldReference bpRef, bool attached, INode node, IXmlLineInfo iXmlLineInfo, ILContext context)
1137                 {
1138                         var module = context.Body.Method.Module;
1139
1140                         if (bpRef == null)
1141                                 return false;
1142
1143                         if (node is ValueNode valueNode && valueNode.CanConvertValue(context, bpRef))
1144                                 return true;
1145
1146                         if (!(node is IElementNode elementNode))
1147                                 return false;
1148
1149                         if (!context.Variables.TryGetValue(elementNode, out VariableDefinition varValue))
1150                                 return false;
1151
1152                         var bpTypeRef = bpRef.GetBindablePropertyType(iXmlLineInfo, module);
1153                         // If it's an attached BP, there's no second chance to handle IMarkupExtensions, so we try here.
1154                         // Worst case scenario ? InvalidCastException at runtime
1155                         if (attached && varValue.VariableType.FullName == "System.Object")
1156                                 return true;
1157                         var implicitOperator = varValue.VariableType.GetImplicitOperatorTo(bpTypeRef, module);
1158                         if (implicitOperator != null)
1159                                 return true;
1160
1161                         //as we're in the SetValue Scenario, we can accept value types, they'll be boxed
1162                         if (varValue.VariableType.IsValueType && bpTypeRef.FullName == "System.Object")
1163                                 return true;
1164
1165                         return varValue.VariableType.InheritsFromOrImplements(bpTypeRef);
1166                 }
1167
1168                 static bool CanGetValue(VariableDefinition parent, FieldReference bpRef, bool attached, IXmlLineInfo iXmlLineInfo, ILContext context, out TypeReference propertyType)
1169                 {
1170                         var module = context.Body.Method.Module;
1171                         propertyType = null;
1172
1173                         if (bpRef == null)
1174                                 return false;
1175
1176                         if (!parent.VariableType.InheritsFromOrImplements(module.ImportReference(("XSF", "Xamarin.Forms", "BindableObject"))))
1177                                 return false;
1178
1179                         propertyType = bpRef.GetBindablePropertyType(iXmlLineInfo, module);
1180                         return true;
1181                 }
1182
1183                 static IEnumerable<Instruction> SetValue(VariableDefinition parent, FieldReference bpRef, INode node, IXmlLineInfo iXmlLineInfo, ILContext context)
1184                 {
1185                         var valueNode = node as ValueNode;
1186                         var elementNode = node as IElementNode;
1187                         var module = context.Body.Method.Module;
1188                         var bindableObjectType = ("XSF", "Xamarin.Forms", "BindableObject");
1189
1190 //                      IL_0007:  ldloc.0
1191 //                      IL_0008:  ldsfld class [Xamarin.Forms.Core]Xamarin.Forms.BindableProperty [Xamarin.Forms.Core]Xamarin.Forms.Label::TextProperty
1192 //                      IL_000d:  ldstr "foo"
1193 //                      IL_0012:  callvirt instance void class [Xamarin.Forms.Core]Xamarin.Forms.BindableObject::SetValue(class [Xamarin.Forms.Core]Xamarin.Forms.BindableProperty, object)
1194
1195                         foreach (var instruction in parent.LoadAs(module.GetTypeDefinition(bindableObjectType), module))
1196                                 yield return instruction;
1197
1198                         yield return Create(Ldsfld, bpRef);
1199
1200                         if (valueNode != null) {
1201                                 foreach (var instruction in valueNode.PushConvertedValue(context, bpRef, valueNode.PushServiceProvider(context, bpRef:bpRef), true, false))
1202                                         yield return instruction;
1203                         } else if (elementNode != null) {
1204                                 var bpTypeRef = bpRef.GetBindablePropertyType(iXmlLineInfo, module);
1205                                 foreach (var instruction in context.Variables[elementNode].LoadAs(bpTypeRef, module))
1206                                         yield return instruction;
1207                                 if (bpTypeRef.IsValueType)
1208                                         yield return Create(Box, module.ImportReference(bpTypeRef));
1209                         }
1210                         yield return Create(Callvirt, module.ImportMethodReference(bindableObjectType,
1211                                                                                                                                            methodName: "SetValue",
1212                                                                                                                                            parameterTypes: new[] {
1213                                                                                                                                                    ("XSF", "Xamarin.Forms", "BindableProperty"),
1214                                                                                                                                                    ("mscorlib", "System", "Object"),
1215                                                                                                                                            }));
1216                 }
1217
1218                 static IEnumerable<Instruction> GetValue(VariableDefinition parent, FieldReference bpRef, IXmlLineInfo iXmlLineInfo, ILContext context, out TypeReference propertyType)
1219                 {
1220                         propertyType = bpRef.GetBindablePropertyType(iXmlLineInfo, context.Body.Method.Module);
1221                         return GetValue(parent, bpRef, iXmlLineInfo, context);
1222                 }
1223
1224                 static IEnumerable<Instruction> GetValue(VariableDefinition parent, FieldReference bpRef, IXmlLineInfo iXmlLineInfo, ILContext context)
1225                 {
1226                         var module = context.Body.Method.Module;
1227                         var bindableObjectType = ("XSF", "Xamarin.Forms", "BindableObject");
1228
1229                         foreach (var instruction in parent.LoadAs(module.GetTypeDefinition(bindableObjectType), module))
1230                                 yield return instruction;
1231
1232                         yield return Create(Ldsfld, bpRef);
1233                         yield return Create(Callvirt,  module.ImportMethodReference(bindableObjectType,
1234                                                                                                                                                 methodName: "GetValue",
1235                                                                                                                                                 parameterTypes: new[] { ("XSF", "Xamarin.Forms", "BindableProperty")}));
1236                 }
1237
1238                 static bool CanSet(VariableDefinition parent, string localName, INode node, ILContext context)
1239                 {
1240                         var module = context.Body.Method.Module;
1241                         TypeReference declaringTypeReference;
1242                         var property = parent.VariableType.GetProperty(pd => pd.Name == localName, out declaringTypeReference);
1243                         if (property == null)
1244                                 return false;
1245                         var propertyType = property.PropertyType.ResolveGenericParameters(declaringTypeReference);
1246                         var propertySetter = property.SetMethod;
1247                         if (propertySetter == null || !propertySetter.IsPublic || propertySetter.IsStatic)
1248                                 return false;
1249
1250                         var valueNode = node as ValueNode;
1251                         if (valueNode != null && valueNode.CanConvertValue(context, propertyType, new ICustomAttributeProvider[] { property, propertyType.ResolveCached()}))
1252                                 return true;
1253
1254                         var elementNode = node as IElementNode;
1255                         if (elementNode == null)
1256                                 return false;
1257
1258                         var vardef = context.Variables [elementNode];
1259                         var implicitOperator = vardef.VariableType.GetImplicitOperatorTo(propertyType, module);
1260
1261                         if (vardef.VariableType.InheritsFromOrImplements(propertyType))
1262                                 return true;
1263                         if (implicitOperator != null)
1264                                 return true;
1265                         if (propertyType.FullName == "System.Object")
1266                                 return true;
1267
1268                         //I'd like to get rid of this condition. This comment used to be //TODO replace latest check by a runtime type check
1269                         if (vardef.VariableType.FullName == "System.Object")
1270                                 return true;
1271
1272                         return false;
1273                 }
1274
1275                 static bool CanGet(VariableDefinition parent, string localName, ILContext context, out TypeReference propertyType)
1276                 {
1277                         var module = context.Body.Method.Module;
1278                         propertyType = null;
1279                         TypeReference declaringTypeReference;
1280                         var property = parent.VariableType.GetProperty(pd => pd.Name == localName, out declaringTypeReference);
1281                         if (property == null)
1282                                 return false;
1283                         var propertyGetter = property.GetMethod;
1284                         if (propertyGetter == null || !propertyGetter.IsPublic || propertyGetter.IsStatic)
1285                                 return false;
1286
1287                         module.ImportReference(parent.VariableType.ResolveCached());
1288                         var propertyGetterRef = module.ImportReference(module.ImportReference(propertyGetter).ResolveGenericParameters(declaringTypeReference, module));
1289                         propertyGetterRef.ImportTypes(module);
1290                         propertyType = propertyGetterRef.ReturnType.ResolveGenericParameters(declaringTypeReference);
1291
1292                         return true;
1293                 }
1294
1295                 static IEnumerable<Instruction> Set(VariableDefinition parent, string localName, INode node, IXmlLineInfo iXmlLineInfo, ILContext context)
1296                 {
1297                         var module = context.Body.Method.Module;
1298                         TypeReference declaringTypeReference;
1299                         var property = parent.VariableType.GetProperty(pd => pd.Name == localName, out declaringTypeReference);
1300                         var propertySetter = property.SetMethod;
1301
1302 //                      IL_0007:  ldloc.0
1303 //                      IL_0008:  ldstr "foo"
1304 //                      IL_000d:  callvirt instance void class [Xamarin.Forms.Core]Xamarin.Forms.Label::set_Text(string)
1305
1306                         module.ImportReference(parent.VariableType.ResolveCached());
1307                         var propertySetterRef = module.ImportReference(module.ImportReference(propertySetter).ResolveGenericParameters(declaringTypeReference, module));
1308                         propertySetterRef.ImportTypes(module);
1309                         var propertyType = property.PropertyType.ResolveGenericParameters(declaringTypeReference);
1310                         var valueNode = node as ValueNode;
1311                         var elementNode = node as IElementNode;
1312
1313                         //if it's a value type, load the address so we can invoke methods on it
1314                         if (parent.VariableType.IsValueType)
1315                                 yield return Instruction.Create(OpCodes.Ldloca, parent);
1316                         else
1317                                 yield return Instruction.Create(OpCodes.Ldloc, parent);
1318
1319                         if (valueNode != null) {
1320                                 foreach (var instruction in valueNode.PushConvertedValue(context, propertyType, new ICustomAttributeProvider [] { property, propertyType.ResolveCached() }, valueNode.PushServiceProvider(context, propertyRef:property), false, true))
1321                                         yield return instruction;
1322                                 if (parent.VariableType.IsValueType)
1323                                         yield return Instruction.Create(OpCodes.Call, propertySetterRef);
1324                                 else
1325                                         yield return Instruction.Create(OpCodes.Callvirt, propertySetterRef);
1326                         } else if (elementNode != null) {
1327                                 foreach (var instruction in context.Variables [elementNode].LoadAs(propertyType, module))
1328                                         yield return instruction;
1329                                 if (parent.VariableType.IsValueType)
1330                                         yield return Instruction.Create(OpCodes.Call, propertySetterRef);
1331                                 else
1332                                         yield return Instruction.Create(OpCodes.Callvirt, propertySetterRef);
1333                         }
1334                 }
1335
1336                 static IEnumerable<Instruction> Get(VariableDefinition parent, string localName, IXmlLineInfo iXmlLineInfo, ILContext context, out TypeReference propertyType)
1337                 {
1338                         var module = context.Body.Method.Module;
1339                         var property = parent.VariableType.GetProperty(pd => pd.Name == localName, out var declaringTypeReference);
1340                         var propertyGetter = property.GetMethod;
1341
1342                         module.ImportReference(parent.VariableType.ResolveCached());
1343                         var propertyGetterRef = module.ImportReference(module.ImportReference(propertyGetter).ResolveGenericParameters(declaringTypeReference, module));
1344                         propertyGetterRef.ImportTypes(module);
1345                         propertyType = propertyGetterRef.ReturnType.ResolveGenericParameters(declaringTypeReference);
1346
1347                         if (parent.VariableType.IsValueType)
1348                                 return new[] {
1349                                         Instruction.Create(OpCodes.Ldloca, parent),
1350                                         Instruction.Create(OpCodes.Call, propertyGetterRef),
1351                                 };
1352                         else
1353                                 return new[] {
1354                                         Instruction.Create(OpCodes.Ldloc, parent),
1355                                         Instruction.Create(OpCodes.Callvirt, propertyGetterRef),
1356                                 };
1357                 }
1358
1359                 static bool CanAdd(VariableDefinition parent, XmlName propertyName, INode node, IXmlLineInfo lineInfo, ILContext context)
1360                 {
1361                         var module = context.Body.Method.Module;
1362                         var localName = propertyName.LocalName;
1363                         bool attached;
1364                         var bpRef = GetBindablePropertyReference(parent, propertyName.NamespaceURI, ref localName, out attached, context, lineInfo);
1365                         TypeReference propertyType;
1366
1367                         if (   !CanGetValue(parent, bpRef, attached, null, context, out propertyType)
1368                                 && !CanGet(parent, localName, context, out propertyType))
1369                                 return false;
1370
1371                         //TODO check md.Parameters[0] type
1372                         var adderTuple = propertyType.GetMethods(md => md.Name == "Add" && md.Parameters.Count == 1, module).FirstOrDefault();
1373                         if (adderTuple == null)
1374                                 return false;
1375
1376                         return true;
1377                 }
1378
1379                 static Dictionary<VariableDefinition, IList<string>> resourceNamesInUse = new Dictionary<VariableDefinition, IList<string>>();
1380                 static bool CanAddToResourceDictionary(VariableDefinition parent, TypeReference collectionType, IElementNode node, IXmlLineInfo lineInfo, ILContext context)
1381                 {
1382                         if (   collectionType.FullName != "Xamarin.Forms.ResourceDictionary"
1383                                 && collectionType.ResolveCached().BaseType?.FullName != "Xamarin.Forms.ResourceDictionary")
1384                                 return false;
1385
1386
1387                         if (node.Properties.ContainsKey(XmlName.xKey)) {
1388                                 var key = (node.Properties[XmlName.xKey] as ValueNode).Value as string;
1389                                 if (!resourceNamesInUse.TryGetValue(parent, out var names))
1390                                         resourceNamesInUse[parent] = (names = new List<string>());
1391                                 if (names.Contains(key))
1392                                         throw new XamlParseException($"A resource with the key '{key}' is already present in the ResourceDictionary.", lineInfo);
1393                                 names.Add(key);
1394                                 return true;
1395                         }
1396
1397                         //is there a RD.Add() overrides that accepts this ?
1398                         var nodeTypeRef = context.Variables[node].VariableType;
1399                         var module = context.Body.Method.Module;
1400                         if (module.ImportMethodReference(module.GetTypeDefinition(("XSF", "Xamarin.Forms", "ResourceDictionary")),
1401                                                                                          methodName: "Add",
1402                                                                                          parameterTypes: new[] { (nodeTypeRef) }) != null)
1403                                 return true;
1404
1405                         throw new XamlParseException("resources in ResourceDictionary require a x:Key attribute", lineInfo);
1406                 }
1407
1408                 static IEnumerable<Instruction> Add(VariableDefinition parent, XmlName propertyName, INode node, IXmlLineInfo iXmlLineInfo, ILContext context)
1409                 {
1410                         var module = context.Body.Method.Module;
1411                         var elementNode = node as IElementNode;
1412                         var vardef = context.Variables [elementNode];
1413
1414                         TypeReference propertyType;
1415                         foreach (var instruction in GetPropertyValue(parent, propertyName, context, iXmlLineInfo, out propertyType))
1416                                 yield return instruction;
1417
1418                         if (CanAddToResourceDictionary(parent, propertyType, elementNode, iXmlLineInfo, context)) {
1419                                 foreach (var instruction in AddToResourceDictionary(elementNode, iXmlLineInfo, context))
1420                                         yield return instruction;
1421                                 yield break;
1422                         }
1423
1424                         var adderTuple = propertyType.GetMethods(md => md.Name == "Add" && md.Parameters.Count == 1, module).FirstOrDefault();
1425                         var adderRef = module.ImportReference(adderTuple.Item1);
1426                         adderRef = module.ImportReference(adderRef.ResolveGenericParameters(adderTuple.Item2, module));
1427
1428                         foreach (var instruction in vardef.LoadAs(adderRef.Parameters[0].ParameterType.ResolveGenericParameters(adderRef), module))
1429                                 yield return instruction;
1430                         yield return Instruction.Create(OpCodes.Callvirt, adderRef);
1431                         if (adderRef.ReturnType.FullName != "System.Void")
1432                                 yield return Instruction.Create(OpCodes.Pop);
1433                 }
1434
1435                 static IEnumerable<Instruction> AddToResourceDictionary(IElementNode node, IXmlLineInfo lineInfo, ILContext context)
1436                 {
1437                         var module = context.Body.Method.Module;
1438
1439                         if (node.Properties.ContainsKey(XmlName.xKey)) {
1440 //                              IL_0014:  ldstr "key"
1441 //                              IL_0019:  ldstr "foo"
1442 //                              IL_001e:  callvirt instance void class [Xamarin.Forms.Core]Xamarin.Forms.ResourceDictionary::Add(string, object)
1443                                 yield return Create(Ldstr, (node.Properties[XmlName.xKey] as ValueNode).Value as string);
1444                                 foreach (var instruction in context.Variables[node].LoadAs(module.TypeSystem.Object, module))
1445                                         yield return instruction;
1446                                 yield return Create(Callvirt, module.ImportMethodReference(("XSF", "Xamarin.Forms", "ResourceDictionary"),
1447                                                                                                                                                    methodName: "Add",
1448                                                                                                                                                    parameterTypes: new[] {
1449                                                                                                                                                            ("mscorlib", "System", "String"),
1450                                                                                                                                                            ("mscorlib", "System", "Object"),
1451                                                                                                                                                    }));
1452                                 yield break;
1453                         }
1454
1455                         var nodeTypeRef = context.Variables[node].VariableType;
1456                         yield return Create(Ldloc, context.Variables[node]);
1457                         yield return Create(Callvirt, module.ImportMethodReference(("XSF", "Xamarin.Forms", "ResourceDictionary"),
1458                                                                                                                                            methodName: "Add",
1459                                                                                                                                            parameterTypes: new[] { (nodeTypeRef.Scope.Name, nodeTypeRef.Namespace, nodeTypeRef.Name) }));
1460                         yield break;
1461                 }
1462
1463                 static bool GetNameAndTypeRef(ref TypeReference elementType, string namespaceURI, ref string localname,
1464                         ILContext context, IXmlLineInfo lineInfo)
1465                 {
1466                         var dotIdx = localname.IndexOf('.');
1467                         if (dotIdx > 0)
1468                         {
1469                                 var typename = localname.Substring(0, dotIdx);
1470                                 localname = localname.Substring(dotIdx + 1);
1471                                 elementType = new XmlType(namespaceURI, typename, null).GetTypeReference(context.Body.Method.Module, lineInfo);
1472                                 return true;
1473                         }
1474                         return false;
1475                 }
1476
1477                 static void SetDataTemplate(IElementNode parentNode, ElementNode node, ILContext parentContext,
1478                         IXmlLineInfo xmlLineInfo)
1479                 {
1480                         var module = parentContext.Module;
1481                         var dataTemplateType = ("XSF", "Xamarin.Forms.Internals", "IDataTemplate");
1482                         var parentVar = parentContext.Variables[parentNode];
1483                         //Push the DataTemplate to the stack, for setting the template
1484                         parentContext.IL.Append(parentVar.LoadAs(module.GetTypeDefinition(dataTemplateType), module));
1485
1486                         //Create nested class
1487                         //                      .class nested private auto ansi sealed beforefieldinit '<Main>c__AnonStorey0'
1488                         //                      extends [mscorlib]System.Object
1489
1490
1491                         var anonType = new TypeDefinition(
1492                                 null,
1493                                 "<" + parentContext.Body.Method.Name + ">_anonXamlCDataTemplate_" + dtcount++,
1494                                 TypeAttributes.BeforeFieldInit |
1495                                 TypeAttributes.Sealed |
1496                                 TypeAttributes.NestedPrivate) {
1497                                 BaseType = module.TypeSystem.Object,
1498                                 CustomAttributes = {
1499                                         new CustomAttribute (module.ImportCtorReference(("mscorlib", "System.Runtime.CompilerServices", "CompilerGeneratedAttribute"), parameterTypes: null)),
1500                                 }
1501                         };
1502
1503                         parentContext.Body.Method.DeclaringType.NestedTypes.Add(anonType);
1504                         var ctor = anonType.AddDefaultConstructor();
1505
1506                         var loadTemplate = new MethodDefinition("LoadDataTemplate",
1507                                 MethodAttributes.Assembly | MethodAttributes.HideBySig,
1508                                 module.TypeSystem.Object);
1509                         loadTemplate.Body.InitLocals = true;
1510                         anonType.Methods.Add(loadTemplate);
1511
1512                         var parentValues = new FieldDefinition("parentValues", FieldAttributes.Assembly, module.ImportArrayReference(("mscorlib", "System", "Object")));
1513                         anonType.Fields.Add(parentValues);
1514
1515                         TypeReference rootType = null;
1516                         var vdefRoot = parentContext.Root as VariableDefinition;
1517                         if (vdefRoot != null)
1518                                 rootType = vdefRoot.VariableType;
1519                         var fdefRoot = parentContext.Root as FieldDefinition;
1520                         if (fdefRoot != null)
1521                                 rootType = fdefRoot.FieldType;
1522
1523                         var root = new FieldDefinition("root", FieldAttributes.Assembly, rootType);
1524                         anonType.Fields.Add(root);
1525
1526                         //Fill the loadTemplate Body
1527                         var templateIl = loadTemplate.Body.GetILProcessor();
1528                         templateIl.Emit(OpCodes.Nop);
1529                         var templateContext = new ILContext(templateIl, loadTemplate.Body, module, parentValues)
1530                         {
1531                                 Root = root,
1532                                 DefineDebug = parentContext.DefineDebug,
1533                                 XamlFilePath = parentContext.XamlFilePath,
1534                         };
1535                         node.Accept(new CreateObjectVisitor(templateContext), null);
1536                         node.Accept(new SetNamescopesAndRegisterNamesVisitor(templateContext), null);
1537                         node.Accept(new SetFieldVisitor(templateContext), null);
1538                         node.Accept(new SetResourcesVisitor(templateContext), null);
1539                         node.Accept(new SetPropertiesVisitor(templateContext, stopOnResourceDictionary: true), null);
1540
1541                         templateIl.Append(templateContext.Variables[node].LoadAs(module.TypeSystem.Object, module));
1542                         templateIl.Emit(OpCodes.Ret);
1543
1544                         //Instanciate nested class
1545                         var parentIl = parentContext.IL;
1546                         parentIl.Emit(OpCodes.Newobj, ctor);
1547
1548                         //Copy required local vars
1549                         parentIl.Emit(OpCodes.Dup); //Duplicate the nestedclass instance
1550                         parentIl.Append(node.PushParentObjectsArray(parentContext));
1551                         parentIl.Emit(OpCodes.Stfld, parentValues);
1552                         parentIl.Emit(OpCodes.Dup); //Duplicate the nestedclass instance
1553                         if (parentContext.Root is VariableDefinition)
1554                                 parentIl.Append((parentContext.Root as VariableDefinition).LoadAs(module.TypeSystem.Object, module));
1555                         else if (parentContext.Root is FieldDefinition)
1556                         {
1557                                 parentIl.Emit(OpCodes.Ldarg_0);
1558                                 parentIl.Emit(OpCodes.Ldfld, parentContext.Root as FieldDefinition);
1559                         }
1560                         else
1561                                 throw new InvalidProgramException();
1562                         parentIl.Emit(OpCodes.Stfld, root);
1563
1564                         //SetDataTemplate
1565                         parentIl.Emit(Ldftn, loadTemplate);
1566                         parentIl.Emit(Newobj, module.ImportCtorReference(("mscorlib", "System", "Func`1"),
1567                                                                                                                          classArguments: new[] { ("mscorlib", "System", "Object") },
1568                                                                                                                          paramCount: 2));
1569
1570                         parentContext.IL.Emit(OpCodes.Callvirt, module.ImportPropertySetterReference(dataTemplateType, propertyName: "LoadTemplate"));
1571
1572                         loadTemplate.Body.Optimize();
1573                 }
1574
1575                 bool TrySetRuntimeName(XmlName propertyName, VariableDefinition variableDefinition, ValueNode node)
1576                 {
1577                         if (propertyName != XmlName.xName)
1578                                 return false;
1579
1580                         var attributes = variableDefinition.VariableType.ResolveCached()
1581                                 .CustomAttributes.Where(attribute => attribute.AttributeType.FullName == "Xamarin.Forms.Xaml.RuntimeNamePropertyAttribute").ToList();
1582
1583                         if (!attributes.Any())
1584                                 return false;
1585
1586                         var runTimeName = attributes[0].ConstructorArguments[0].Value as string;
1587
1588                         if (string.IsNullOrEmpty(runTimeName))
1589                                 return false;
1590
1591                         Context.IL.Append(SetPropertyValue(variableDefinition, new XmlName("", runTimeName), node, Context, node));
1592                         return true;
1593                 }
1594         }
1595
1596         class VariableDefinitionReference
1597         {
1598                 public VariableDefinitionReference(VariableDefinition vardef)
1599                 {
1600                         VariableDefinition = vardef;
1601                 }
1602
1603                 public VariableDefinition VariableDefinition { get; set; }
1604
1605                 public static implicit operator VariableDefinition(VariableDefinitionReference vardefref)
1606                 {
1607                         return vardefref.VariableDefinition;
1608                 }
1609         }
1610 }