File: SetPropertiesVisitor.cs
Web Access
Project: src\src\Controls\src\Build.Tasks\Controls.Build.Tasks.csproj (Microsoft.Maui.Controls.Build.Tasks)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using Microsoft.Maui.Controls.Internals;
using Microsoft.Maui.Controls.Xaml;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Cecil.Rocks;
using static Microsoft.Maui.Controls.Build.Tasks.BuildExceptionCode;
using static Mono.Cecil.Cil.Instruction;
using static Mono.Cecil.Cil.OpCodes;
 
namespace Microsoft.Maui.Controls.Build.Tasks
{
	class SetPropertiesVisitor : IXamlNodeVisitor
	{
		static readonly IList<XmlName> skips =
		[
			XmlName.xKey,
			XmlName.xTypeArguments,
			XmlName.xArguments,
			XmlName.xFactoryMethod,
			XmlName.xName,
			XmlName.xDataType
		];
 
		public SetPropertiesVisitor(ILContext context, bool stopOnResourceDictionary = false)
		{
			Context = context;
			Module = context.Body.Method.Module;
			StopOnResourceDictionary = stopOnResourceDictionary;
		}
 
		public ILContext Context { get; }
		public bool StopOnResourceDictionary { get; }
		public TreeVisitingMode VisitingMode => TreeVisitingMode.BottomUp;
		public bool StopOnDataTemplate => true;
		public bool VisitNodeOnDataTemplate => true;
		public bool SkipChildren(INode node, INode parentNode) => false;
 
		public bool IsResourceDictionary(ElementNode node)
		{
			var parentVar = Context.Variables[(IElementNode)node];
			return parentVar.VariableType.FullName == "Microsoft.Maui.Controls.ResourceDictionary"
				|| parentVar.VariableType.Resolve().BaseType?.FullName == "Microsoft.Maui.Controls.ResourceDictionary";
		}
 
		ModuleDefinition Module { get; }
 
		public void Visit(ValueNode node, INode parentNode)
		{
			//TODO support Label text as element
			if (!TryGetPropertyName(node, parentNode, out XmlName propertyName))
			{
				if (!IsCollectionItem(node, parentNode))
					return;
				string contentProperty;
				if (!Context.Variables.ContainsKey((IElementNode)parentNode))
					return;
				var parentVar = Context.Variables[(IElementNode)parentNode];
				if ((contentProperty = GetContentProperty(Context.Cache, parentVar.VariableType)) != null)
					propertyName = new XmlName(((IElementNode)parentNode).NamespaceURI, contentProperty);
				else
					return;
			}
 
			if (TrySetRuntimeName(propertyName, Context.Variables[(IElementNode)parentNode], node))
				return;
			if (skips.Contains(propertyName))
				return;
			if (parentNode is IElementNode && ((IElementNode)parentNode).SkipProperties.Contains(propertyName))
				return;
			if (propertyName.Equals(XamlParser.McUri, "Ignorable"))
				return;
			Context.IL.Append(SetPropertyValue(Context.Variables[(IElementNode)parentNode], propertyName, node, Context, node));
		}
 
		public void Visit(MarkupNode node, INode parentNode)
		{
		}
 
		public void Visit(ElementNode node, INode parentNode)
		{
			XmlName propertyName = XmlName.Empty;
 
			//Simplify ListNodes with single elements
			var pList = parentNode as ListNode;
			if (pList != null && pList.CollectionItems.Count == 1)
			{
				propertyName = pList.XmlName;
				parentNode = parentNode.Parent;
			}
 
			if ((propertyName != XmlName.Empty || TryGetPropertyName(node, parentNode, out propertyName)) && skips.Contains(propertyName))
				return;
 
			if (propertyName == XmlName._CreateContent)
			{
				SetDataTemplate((IElementNode)parentNode, node, Context, node);
				return;
			}
 
			//if this node is an IMarkupExtension, invoke ProvideValue() and replace the variable
			var vardef = Context.Variables[node];
			var vardefref = new VariableDefinitionReference(vardef);
			var localName = propertyName.LocalName;
			TypeReference declaringTypeReference = null;
			FieldReference bpRef = null;
			var _ = false;
			PropertyDefinition propertyRef = null;
			if (parentNode is IElementNode && propertyName != XmlName.Empty)
			{
				bpRef = GetBindablePropertyReference(Context.Variables[(IElementNode)parentNode], propertyName.NamespaceURI, ref localName, out _, Context, node);
				propertyRef = Context.Variables[(IElementNode)parentNode].VariableType.GetProperty(Context.Cache, pd => pd.Name == localName, out declaringTypeReference);
			}
			Context.IL.Append(ProvideValue(vardefref, Context, Module, node, bpRef: bpRef, propertyRef: propertyRef, propertyDeclaringTypeRef: declaringTypeReference));
			if (vardef != vardefref.VariableDefinition)
			{
				vardef = vardefref.VariableDefinition;
				Context.Body.Variables.Add(vardef);
				Context.Variables[node] = vardef;
			}
 
			if (propertyName != XmlName.Empty)
			{
				if (skips.Contains(propertyName))
					return;
				if (parentNode is IElementNode && ((IElementNode)parentNode).SkipProperties.Contains(propertyName))
					return;
 
				Context.IL.Append(SetPropertyValue(Context.Variables[(IElementNode)parentNode], propertyName, node, Context, node));
			}
			else if (IsCollectionItem(node, parentNode) && parentNode is IElementNode)
			{
				var parentVar = Context.Variables[(IElementNode)parentNode];
				string contentProperty;
 
				if (CanAddToResourceDictionary(parentVar, parentVar.VariableType, node, node, Context))
				{
					Context.IL.Append(parentVar.LoadAs(Context.Cache, Module.GetTypeDefinition(Context.Cache, ("Microsoft.Maui.Controls", "Microsoft.Maui.Controls", "ResourceDictionary")), Module));
					Context.IL.Append(AddToResourceDictionary(parentVar, node, node, Context));
				}
				else if ((contentProperty = GetContentProperty(Context.Cache, parentVar.VariableType)) != null)
				{
					var name = new XmlName(node.NamespaceURI, contentProperty);
					if (skips.Contains(name))
						return;
					if (parentNode is IElementNode && ((IElementNode)parentNode).SkipProperties.Contains(propertyName))
						return;
					Context.IL.Append(SetPropertyValue(Context.Variables[(IElementNode)parentNode], name, node, Context, node));
				}
				// Collection element, implicit content, or implicit collection element.
				else if (parentVar.VariableType.ImplementsInterface(Context.Cache, Module.ImportReference(Context.Cache, ("mscorlib", "System.Collections", "IEnumerable")))
						 && parentVar.VariableType.GetMethods(Context.Cache, md => md.Name == "Add" && md.Parameters.Count == 1, Module).Any())
				{
					var elementType = parentVar.VariableType;
					var adderTuple = elementType.GetMethods(Context.Cache, md => md.Name == "Add" && md.Parameters.Count == 1, Module).First();
					var adderRef = Module.ImportReference(adderTuple.Item1);
					adderRef = Module.ImportReference(adderRef.ResolveGenericParameters(adderTuple.Item2, Module));
 
					Context.IL.Emit(Ldloc, parentVar);
					Context.IL.Append(vardef.LoadAs(Context.Cache, adderRef.Parameters[0].ParameterType.ResolveGenericParameters(adderRef), Module));
					Context.IL.Emit(Callvirt, adderRef);
					if (adderRef.ReturnType.FullName != "System.Void")
						Context.IL.Emit(Pop);
				}
 
				else
					throw new BuildException(BuildExceptionCode.ContentPropertyAttributeMissing, node, null, ((IElementNode)parentNode).XmlType.Name);
			}
			else if (IsCollectionItem(node, parentNode) && parentNode is ListNode)
			{
				//				IL_000d:  ldloc.2 
				//				IL_000e:  callvirt instance class [mscorlib]System.Collections.Generic.IList`1<!0> class [Microsoft.Maui.Controls]Microsoft.Maui.Controls.Layout`1<class [Microsoft.Maui.Controls]Microsoft.Maui.Controls.View>::get_Children()
				//				IL_0013:  ldloc.0 
				//				IL_0014:  callvirt instance void class [mscorlib]System.Collections.Generic.ICollection`1<class [Microsoft.Maui.Controls]Microsoft.Maui.Controls.View>::Add(!0)
 
				var parentList = (ListNode)parentNode;
				var parent = Context.Variables[((IElementNode)parentNode.Parent)];
 
				if (skips.Contains(parentList.XmlName))
					return;
				if (parentNode is IElementNode && ((IElementNode)parentNode).SkipProperties.Contains(propertyName))
					return;
				var elementType = parent.VariableType;
				var localname = parentList.XmlName.LocalName;
 
				TypeReference propertyType;
				Context.IL.Append(GetPropertyValue(parent, parentList.XmlName, Context, node, out propertyType));
 
				if (CanAddToResourceDictionary(parent, propertyType, node, node, Context))
				{
					Context.IL.Append(AddToResourceDictionary(parent, node, node, Context));
					return;
				}
				var adderTuple = propertyType.GetMethods(Context.Cache, md => md.Name == "Add" && md.Parameters.Count == 1, Module).FirstOrDefault() ??
					throw new BuildException(BuildExceptionCode.AdderMissing, node, null, parent.VariableType, localname);
 
				var adderRef = Module.ImportReference(adderTuple.Item1);
				adderRef = Module.ImportReference(adderRef.ResolveGenericParameters(adderTuple.Item2, Module));
 
				Context.IL.Append(vardef.LoadAs(Context.Cache, adderRef.Parameters[0].ParameterType.ResolveGenericParameters(adderRef), Module));
				Context.IL.Emit(OpCodes.Callvirt, adderRef);
				if (adderRef.ReturnType.FullName != "System.Void")
					Context.IL.Emit(OpCodes.Pop);
			}
		}
 
		public void Visit(RootNode node, INode parentNode)
		{
		}
 
		public void Visit(ListNode node, INode parentNode)
		{
		}
 
		public static bool TryGetPropertyName(INode node, INode parentNode, out XmlName name)
		{
			name = default(XmlName);
			if (!(parentNode is IElementNode parentElement))
				return false;
			foreach (var kvp in parentElement.Properties)
			{
				if (kvp.Value != node)
					continue;
				name = kvp.Key;
				return true;
			}
			return false;
		}
 
		static bool IsCollectionItem(INode node, INode parentNode)
		{
			if (!(parentNode is IListNode parentList))
				return false;
			return parentList.CollectionItems.Contains(node);
		}
 
		internal static string GetContentProperty(XamlCache cache, TypeReference typeRef)
		{
			var typeDef = typeRef.ResolveCached(cache);
			var attributes = typeDef.CustomAttributes;
			var attr =
				attributes.FirstOrDefault(cad => ContentPropertyAttribute.ContentPropertyTypes.Contains(cad.AttributeType.FullName));
			if (attr != null)
				return attr.ConstructorArguments[0].Value as string;
			if (typeDef.BaseType == null)
				return null;
			return GetContentProperty(cache, typeDef.BaseType);
		}
 
		public static IEnumerable<Instruction> ProvideValue(VariableDefinitionReference vardefref, ILContext context,
															ModuleDefinition module, ElementNode node, FieldReference bpRef = null,
															PropertyReference propertyRef = null, TypeReference propertyDeclaringTypeRef = null)
		{
			GenericInstanceType markupExtension;
			IList<TypeReference> genericArguments;
			if (vardefref.VariableDefinition.VariableType.FullName == "Microsoft.Maui.Controls.Xaml.ArrayExtension" &&
				vardefref.VariableDefinition.VariableType.ImplementsGenericInterface(context.Cache, "Microsoft.Maui.Controls.Xaml.IMarkupExtension`1",
					out markupExtension, out genericArguments))
			{
				var markExt = markupExtension.ResolveCached(context.Cache);
				var provideValueInfo = markExt.Methods.First(md => md.Name == "ProvideValue");
				var provideValue = module.ImportReference(provideValueInfo);
				provideValue =
					module.ImportReference(provideValue.ResolveGenericParameters(markupExtension, module));
 
				var typeNode = node.Properties[new XmlName("", "Type")];
				TypeReference arrayTypeRef;
				if (context.TypeExtensions.TryGetValue(typeNode, out arrayTypeRef))
					vardefref.VariableDefinition = new VariableDefinition(module.ImportReference(arrayTypeRef.MakeArrayType()));
				else
					vardefref.VariableDefinition = new VariableDefinition(module.ImportReference(genericArguments.First()));
				foreach (var instruction in context.Variables[node].LoadAs(context.Cache, markupExtension, module))
					yield return instruction;
				yield return Instruction.Create(OpCodes.Ldnull); //ArrayExtension does not require ServiceProvider
				yield return Instruction.Create(OpCodes.Callvirt, provideValue);
 
				if (arrayTypeRef != null)
					yield return Instruction.Create(OpCodes.Castclass, module.ImportReference(arrayTypeRef.MakeArrayType()));
				yield return Instruction.Create(OpCodes.Stloc, vardefref.VariableDefinition);
			}
			else if (vardefref.VariableDefinition.VariableType.ImplementsGenericInterface(context.Cache, "Microsoft.Maui.Controls.Xaml.IMarkupExtension`1",
				out markupExtension, out genericArguments))
			{
				var acceptEmptyServiceProvider = vardefref.VariableDefinition.VariableType.GetCustomAttribute(context.Cache, module, ("Microsoft.Maui.Controls", "Microsoft.Maui.Controls.Xaml", "AcceptEmptyServiceProviderAttribute")) != null;
				var requiredServiceType = vardefref.VariableDefinition.VariableType.GetRequiredServices(context.Cache, module);
 
				if (!acceptEmptyServiceProvider && requiredServiceType is null)
					context.LoggingHelper.LogWarningOrError(BuildExceptionCode.UnattributedMarkupType, context.XamlFilePath, node.LineNumber, node.LinePosition, 0, 0, vardefref.VariableDefinition.VariableType);
 
				(string, string, string)? bindingExtensionType = vardefref.VariableDefinition.VariableType.FullName switch
				{
					"Microsoft.Maui.Controls.Xaml.BindingExtension" => ("Microsoft.Maui.Controls.Xaml", "Microsoft.Maui.Controls.Xaml", "BindingExtension"),
					"Microsoft.Maui.Controls.Xaml.TemplateBindingExtension" => ("Microsoft.Maui.Controls.Xaml", "Microsoft.Maui.Controls.Xaml", "TemplateBindingExtension"),
					_ => null,
				};
 
				if (bindingExtensionType.HasValue)
				{
					// for backwards compatibility, it is possible to disable compilation of bindings with the `Source` property via a feature switch
					// this feature switch is enabled by default only for NativeAOT and full trimming mode
					bool hasSource = node.Properties.ContainsKey(new XmlName("", "Source"));
					bool skipBindingCompilation = hasSource && !context.CompileBindingsWithSource;
					if (!skipBindingCompilation)
					{
						if (TryCompileBindingPath(node, context, vardefref.VariableDefinition, bindingExtensionType.Value, out var instructions))
						{
							// if the binding is compiled, there's no need to pass the ServiceProvider to the extension
							acceptEmptyServiceProvider = true;
 
							foreach (var instruction in instructions)
								yield return instruction;
						}
					}
					else
					{
						context.LoggingHelper.LogWarningOrError(BuildExceptionCode.BindingWithSourceCompilationSkipped, context.XamlFilePath, node.LineNumber, node.LinePosition, 0, 0, null);
					}
				}
 
				var markExt = markupExtension.ResolveCached(context.Cache);
				var provideValueInfo = markExt.Methods.First(md => md.Name == "ProvideValue");
				var provideValue = module.ImportReference(provideValueInfo);
				provideValue =
					module.ImportReference(provideValue.ResolveGenericParameters(markupExtension, module));
 
				vardefref.VariableDefinition = new VariableDefinition(module.ImportReference(genericArguments.First()));
				foreach (var instruction in context.Variables[node].LoadAs(context.Cache, markupExtension, module))
					yield return instruction;
				if (acceptEmptyServiceProvider)
					yield return Create(Ldnull);
				else
					foreach (var instruction in node.PushServiceProvider(context, requiredServiceType, bpRef, propertyRef, propertyDeclaringTypeRef))
						yield return instruction;
				yield return Instruction.Create(OpCodes.Callvirt, provideValue);
				yield return Instruction.Create(OpCodes.Stloc, vardefref.VariableDefinition);
			}
			else if (context.Variables[node].VariableType.ImplementsInterface(context.Cache, module.ImportReference(context.Cache, ("Microsoft.Maui.Controls", "Microsoft.Maui.Controls.Xaml", "IMarkupExtension"))))
			{
				var acceptEmptyServiceProvider = context.Variables[node].VariableType.GetCustomAttribute(context.Cache, module, ("Microsoft.Maui.Controls", "Microsoft.Maui.Controls.Xaml", "AcceptEmptyServiceProviderAttribute")) != null;
				var requiredServiceType = vardefref.VariableDefinition.VariableType.GetRequiredServices(context.Cache, module);
 
				if (!acceptEmptyServiceProvider && requiredServiceType is null)
					context.LoggingHelper.LogWarningOrError(BuildExceptionCode.UnattributedMarkupType, context.XamlFilePath, node.LineNumber, node.LinePosition, 0, 0, vardefref.VariableDefinition.VariableType);
 
				var markupExtensionType = ("Microsoft.Maui.Controls", "Microsoft.Maui.Controls.Xaml", "IMarkupExtension");
				//some markup extensions weren't compiled earlier (on purpose), so we need to compile them now
				var compiledValueProviderName = context.Variables[node].VariableType.GetCustomAttribute(context.Cache, module, ("Microsoft.Maui.Controls", "Microsoft.Maui.Controls.Xaml", "ProvideCompiledAttribute"))?.ConstructorArguments?[0].Value as string;
				Type compiledValueProviderType;
				ICompiledValueProvider valueProvider;
 
				if (compiledValueProviderName != null
					&& (compiledValueProviderType = Type.GetType(compiledValueProviderName)) != null
					&& (valueProvider = Activator.CreateInstance(compiledValueProviderType) as ICompiledValueProvider) != null)
				{
					var cProvideValue = typeof(ICompiledValueProvider).GetMethods().FirstOrDefault(md => md.Name == "ProvideValue");
					var instructions = (IEnumerable<Instruction>)cProvideValue.Invoke(valueProvider, [
						vardefref,
						context.Body.Method.Module,
						node as BaseNode,
						context]);
					foreach (var i in instructions)
						yield return i;
					yield break;
				}
 
				vardefref.VariableDefinition = new VariableDefinition(module.TypeSystem.Object);
				foreach (var instruction in context.Variables[node].LoadAs(context.Cache, module.GetTypeDefinition(context.Cache, markupExtensionType), module))
					yield return instruction;
				if (acceptEmptyServiceProvider)
					yield return Create(Ldnull);
				else
					foreach (var instruction in node.PushServiceProvider(context, requiredServiceType, bpRef, propertyRef, propertyDeclaringTypeRef))
						yield return instruction;
				yield return Create(Callvirt, module.ImportMethodReference(context.Cache,
																		   markupExtensionType,
																		   methodName: "ProvideValue",
																		   parameterTypes: new[] { ("System.ComponentModel", "System", "IServiceProvider") }));
				yield return Create(Stloc, vardefref.VariableDefinition);
			}
			else if (context.Variables[node].VariableType.ImplementsInterface(context.Cache, module.ImportReference(context.Cache, ("Microsoft.Maui.Controls", "Microsoft.Maui.Controls.Xaml", "IValueProvider"))))
			{
				var acceptEmptyServiceProvider = context.Variables[node].VariableType.GetCustomAttribute(context.Cache, module, ("Microsoft.Maui.Controls", "Microsoft.Maui.Controls.Xaml", "AcceptEmptyServiceProviderAttribute")) != null;
				var requiredServiceType = vardefref.VariableDefinition.VariableType.GetRequiredServices(context.Cache, module);
 
				if (!acceptEmptyServiceProvider && requiredServiceType is null)
					context.LoggingHelper.LogWarningOrError(BuildExceptionCode.UnattributedMarkupType, context.XamlFilePath, node.LineNumber, node.LinePosition, 0, 0, vardefref.VariableDefinition.VariableType);
 
				var valueProviderType = context.Variables[node].VariableType;
				//If the IValueProvider has a ProvideCompiledAttribute that can be resolved, shortcut this
				var compiledValueProviderName = valueProviderType?.GetCustomAttribute(context.Cache, module, ("Microsoft.Maui.Controls", "Microsoft.Maui.Controls.Xaml", "ProvideCompiledAttribute"))?.ConstructorArguments?[0].Value as string;
				Type compiledValueProviderType;
				if (compiledValueProviderName != null && (compiledValueProviderType = Type.GetType(compiledValueProviderName)) != null)
				{
					var compiledValueProvider = Activator.CreateInstance(compiledValueProviderType);
					var cProvideValue = typeof(ICompiledValueProvider).GetMethods().FirstOrDefault(md => md.Name == "ProvideValue");
					var instructions = (IEnumerable<Instruction>)cProvideValue.Invoke(compiledValueProvider, new object[] {
						vardefref,
						context.Body.Method.Module,
						node as BaseNode,
						context});
					foreach (var i in instructions)
						yield return i;
					yield break;
				}
 
				var valueProviderInterface = ("Microsoft.Maui.Controls", "Microsoft.Maui.Controls.Xaml", "IValueProvider");
				vardefref.VariableDefinition = new VariableDefinition(module.TypeSystem.Object);
				foreach (var instruction in context.Variables[node].LoadAs(context.Cache, module.GetTypeDefinition(context.Cache, valueProviderInterface), module))
					yield return instruction;
				if (acceptEmptyServiceProvider)
					yield return Create(Ldnull);
				else
					foreach (var instruction in node.PushServiceProvider(context, requiredServiceType, bpRef, propertyRef, propertyDeclaringTypeRef))
						yield return instruction;
				yield return Create(Callvirt, module.ImportMethodReference(context.Cache,
																		   valueProviderInterface,
																		   methodName: "ProvideValue",
																		   parameterTypes: new[] { ("System.ComponentModel", "System", "IServiceProvider") }));
				yield return Create(Stloc, vardefref.VariableDefinition);
			}
		}
 
		//Once we get compiled IValueProvider, this will move to the BindingExpression
		static bool TryCompileBindingPath(ElementNode node, ILContext context, VariableDefinition bindingExt, (string, string, string) bindingExtensionType, out IEnumerable<Instruction> instructions)
		{
			instructions = null;
 
			//TODO support casting operators
			var module = context.Module;
 
			if (!node.Properties.TryGetValue(new XmlName("", "Path"), out INode pathNode) && node.CollectionItems.Any())
				pathNode = node.CollectionItems[0];
			var path = (pathNode as ValueNode)?.Value as string;
			if (!node.Properties.TryGetValue(new XmlName("", "Mode"), out INode modeNode)
				|| !Enum.TryParse((modeNode as ValueNode)?.Value as string, true, out BindingMode declaredmode))
				declaredmode = BindingMode.TwoWay;  //meaning the mode isn't specified in the Binding extension. generate getters, setters, handlers
 
			INode dataTypeNode = null;
			IElementNode n = node;
 
			// Special handling for BindingContext={Binding ...}
			// The order of checks is:
			// - x:DataType on the binding itself
			// - SKIP looking for x:DataType on the parent
			// - continue looking for x:DataType on the parent's parent...
			IElementNode skipNode = null;
			if (IsBindingContextBinding(node))
			{
				skipNode = GetParent(node);
			}
 
			bool xDataTypeIsInOuterScope = false;
			while (n != null)
			{
				if (n != skipNode && n.Properties.TryGetValue(XmlName.xDataType, out dataTypeNode))
				{
					break;
				}
				if (DoesNotInheritDataType(n))
				{
					break;
				}
 
				if (n.XmlType.Name == nameof(Microsoft.Maui.Controls.DataTemplate)
					&& n.XmlType.NamespaceUri == XamlParser.MauiUri)
				{
					xDataTypeIsInOuterScope = true;
				}
 
				n = GetParent(n);
			}
 
			if (dataTypeNode is null)
			{
				context.LoggingHelper.LogWarningOrError(BuildExceptionCode.BindingWithoutDataType, context.XamlFilePath, node.LineNumber, node.LinePosition, 0, 0, null);
 
				return false;
			}
 
			if (xDataTypeIsInOuterScope)
			{
				context.LoggingHelper.LogWarningOrError(BuildExceptionCode.BindingWithXDataTypeFromOuterScope, context.XamlFilePath, node.LineNumber, node.LinePosition, 0, 0, null);
				// continue compilation
			}
 
			if (dataTypeNode is ElementNode enode
				&& enode.XmlType.NamespaceUri == XamlParser.X2009Uri
				&& enode.XmlType.Name == nameof(Microsoft.Maui.Controls.Xaml.NullExtension))
			{
				context.LoggingHelper.LogWarningOrError(BuildExceptionCode.BindingWithNullDataType, context.XamlFilePath, node.LineNumber, node.LinePosition, 0, 0, null);
				return false;
			}
 
			string dataType = (dataTypeNode as ValueNode)?.Value as string;
			if (dataType is null)
				throw new BuildException(XDataTypeSyntax, dataTypeNode as IXmlLineInfo, null);
 
			XmlType dtXType = null;
			try
			{
				dtXType = TypeArgumentsParser.ParseSingle(dataType, node.NamespaceResolver, dataTypeNode as IXmlLineInfo)
					?? throw new BuildException(XDataTypeSyntax, dataTypeNode as IXmlLineInfo, null);
			}
			catch (XamlParseException)
			{
				var prefix = dataType.Contains(":") ? dataType.Substring(0, dataType.IndexOf(":", StringComparison.Ordinal)) : "";
				throw new BuildException(XmlnsUndeclared, dataTypeNode as IXmlLineInfo, null, prefix);
			}
 
			var tSourceRef = dtXType.GetTypeReference(context.Cache, module, (IXmlLineInfo)node);
			if (tSourceRef == null)
				return false; //throw
 
			if (!TryParsePath(context, path, tSourceRef, node as IXmlLineInfo, module, out var properties))
			{
				return false;
			}
 
			instructions = GenerateInstructions();
			return true;
 
			IEnumerable<Instruction> GenerateInstructions()
			{
				TypeReference tPropertyRef = tSourceRef;
				if (properties != null && properties.Count > 0)
				{
					var lastProp = properties[properties.Count - 1];
					if (lastProp.property != null)
						tPropertyRef = lastProp.property.PropertyType.ResolveGenericParameters(lastProp.propDeclTypeRef);
					else //array type
						tPropertyRef = lastProp.propDeclTypeRef.ResolveCached(context.Cache);
				}
				tPropertyRef = module.ImportReference(tPropertyRef);
				var valuetupleRef = context.Module.ImportReference(module.ImportReference(context.Cache, ("mscorlib", "System", "ValueTuple`2")).MakeGenericInstanceType(new[] { tPropertyRef, module.TypeSystem.Boolean }));
				var funcRef = module.ImportReference(module.ImportReference(context.Cache, ("mscorlib", "System", "Func`2")).MakeGenericInstanceType(new[] { tSourceRef, valuetupleRef }));
				var actionRef = module.ImportReference(module.ImportReference(context.Cache, ("mscorlib", "System", "Action`2")).MakeGenericInstanceType(new[] { tSourceRef, tPropertyRef }));
				var funcObjRef = module.ImportReference(module.ImportReference(context.Cache, ("mscorlib", "System", "Func`2")).MakeGenericInstanceType(new[] { tSourceRef, module.TypeSystem.Object }));
				var tupleRef = module.ImportReference(module.ImportReference(context.Cache, ("mscorlib", "System", "Tuple`2")).MakeGenericInstanceType(new[] { funcObjRef, module.TypeSystem.String }));
				var typedBindingRef = module.ImportReference(module.ImportReference(context.Cache, ("Microsoft.Maui.Controls", "Microsoft.Maui.Controls.Internals", "TypedBinding`2")).MakeGenericInstanceType(new[] { tSourceRef, tPropertyRef }));
 
				//FIXME: make sure the non-deprecated one is used
				var ctorInfo = module.ImportReference(typedBindingRef.ResolveCached(context.Cache).Methods.FirstOrDefault(md =>
						md.IsConstructor
					&& !md.IsStatic
					&& md.Parameters.Count == 3
					&& !md.HasCustomAttributes(module.ImportReference(context.Cache, ("mscorlib", "System", "ObsoleteAttribute")))));
				var ctorinforef = ctorInfo.MakeGeneric(typedBindingRef, funcRef, actionRef, tupleRef);
 
				foreach (var instruction in bindingExt.LoadAs(context.Cache, module.GetTypeDefinition(context.Cache, bindingExtensionType), module))
					yield return instruction;
				foreach (var instruction in CompiledBindingGetGetter(tSourceRef, tPropertyRef, properties, node, context))
					yield return instruction;
				if (declaredmode != BindingMode.OneTime && declaredmode != BindingMode.OneWay)
				{ //if the mode is explicitly 1w, or 1t, no need for setters
					foreach (var instruction in CompiledBindingGetSetter(tSourceRef, tPropertyRef, properties, node, context))
						yield return instruction;
				}
				else
					yield return Create(Ldnull);
				if (declaredmode != BindingMode.OneTime)
				{ //if the mode is explicitly 1t, no need for handlers
					foreach (var instruction in CompiledBindingGetHandlers(tSourceRef, tPropertyRef, properties, node, context))
						yield return instruction;
				}
				else
					yield return Create(Ldnull);
				yield return Create(Newobj, module.ImportReference(ctorinforef));
				yield return Create(Callvirt, module.ImportPropertySetterReference(context.Cache, bindingExtensionType, propertyName: "TypedBinding"));
			}
 
			static IElementNode GetParent(IElementNode node)
			{
				return node switch
				{
					{ Parent: ListNode { Parent: IElementNode parentNode } } => parentNode,
					{ Parent: IElementNode parentNode } => parentNode,
					_ => null,
				};
			}
 
			static bool IsBindingContextBinding(ElementNode node)
			{
				// looking for BindingContext="{Binding ...}"
				return GetParent(node) is IElementNode parentNode
					&& node.TryGetPropertyName(parentNode, out var propertyName)
					&& propertyName.NamespaceURI == ""
					&& propertyName.LocalName == nameof(BindableObject.BindingContext);
			}
 
			bool DoesNotInheritDataType(IElementNode node)
			{
				return GetParent(node) is IElementNode parentNode
					&& node.TryGetPropertyName(parentNode, out XmlName propertyName)
					&& parentNode.XmlType.TryGetTypeReference(context.Cache, module, (IXmlLineInfo)node, out TypeReference parentTypeRef)
					&& parentTypeRef.ResolveCached(context.Cache) is TypeDefinition parentType
					&& parentType.GetProperty(context.Cache, pd => pd.Name == propertyName.LocalName, out var propertyDeclaringTypeRef) is PropertyDefinition propertyDef
					&& propertyDef.CustomAttributes.Any(ca => ca.AttributeType.FullName == "Microsoft.Maui.Controls.Xaml.DoesNotInheritDataTypeAttribute");
			}
		}
 
		static bool TryParsePath(ILContext context, string path, TypeReference tSourceRef, IXmlLineInfo lineInfo, ModuleDefinition module, out IList<(PropertyDefinition property, TypeReference propDeclTypeRef, string indexArg)> pathProperties)
		{
			pathProperties = null;
 
			if (string.IsNullOrWhiteSpace(path))
				return true;
 
			path = path.Trim(' ', '.'); //trim leading or trailing dots
			var parts = path.Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries);
			var properties = new List<(PropertyDefinition property, TypeReference propDeclTypeRef, string indexArg)>();
 
			var previousPartTypeRef = tSourceRef;
			foreach (var part in parts)
			{
				var p = part;
				string indexArg = null;
				var lbIndex = p.IndexOf('[');
				if (lbIndex != -1)
				{
					var rbIndex = p.LastIndexOf(']');
					if (rbIndex == -1)
						throw new BuildException(BuildExceptionCode.BindingIndexerNotClosed, lineInfo, null);
 
					var argLength = rbIndex - lbIndex - 1;
					if (argLength == 0)
						throw new BuildException(BuildExceptionCode.BindingIndexerEmpty, lineInfo, null);
 
					indexArg = p.Substring(lbIndex + 1, argLength).Trim();
					if (indexArg.Length == 0)
						throw new BuildException(BuildExceptionCode.BindingIndexerEmpty, lineInfo, null);
 
					p = p.Substring(0, lbIndex);
					p = p.Trim();
				}
 
				if (p.Length > 0)
				{
					var property = previousPartTypeRef.GetProperty(context.Cache, pd => pd.Name == p && pd.GetMethod != null && pd.GetMethod.IsPublic && !pd.GetMethod.IsStatic, out var propDeclTypeRef);
					if (property is null)
					{
						context.LoggingHelper.LogWarningOrError(BuildExceptionCode.BindingPropertyNotFound, context.XamlFilePath, lineInfo.LineNumber, lineInfo.LinePosition, 0, 0, p, previousPartTypeRef);
						return false;
					}
 
					properties.Add((property, propDeclTypeRef, null));
					previousPartTypeRef = property.PropertyType.ResolveGenericParameters(propDeclTypeRef);
				}
				if (indexArg != null)
				{
					var defaultMemberAttribute = previousPartTypeRef.GetCustomAttribute(context.Cache, module, ("mscorlib", "System.Reflection", "DefaultMemberAttribute"));
					var indexerName = defaultMemberAttribute?.ConstructorArguments?.FirstOrDefault().Value as string ?? "Item";
					PropertyDefinition indexer = null;
					TypeReference indexerDeclTypeRef = null;
					if (int.TryParse(indexArg, out _))
						indexer = previousPartTypeRef.GetProperty(context.Cache,
																	 pd => pd.Name == indexerName
																	 && pd.GetMethod != null
																	 && TypeRefComparer.Default.Equals(pd.GetMethod.Parameters[0].ParameterType.ResolveGenericParameters(previousPartTypeRef), module.ImportReference(context.Cache, ("mscorlib", "System", "Int32")))
																	 && pd.GetMethod.IsPublic, out indexerDeclTypeRef);
					indexer = indexer ?? previousPartTypeRef.GetProperty(context.Cache,
																			pd => pd.Name == indexerName
																			&& pd.GetMethod != null
																			&& TypeRefComparer.Default.Equals(pd.GetMethod.Parameters[0].ParameterType.ResolveGenericParameters(previousPartTypeRef), module.ImportReference(context.Cache, ("mscorlib", "System", "String")))
																			&& pd.GetMethod.IsPublic, out indexerDeclTypeRef);
					indexer = indexer ?? previousPartTypeRef.GetProperty(context.Cache,
																			pd => pd.Name == indexerName
																			&& pd.GetMethod != null
																			&& TypeRefComparer.Default.Equals(pd.GetMethod.Parameters[0].ParameterType.ResolveGenericParameters(previousPartTypeRef), module.ImportReference(context.Cache, ("mscorlib", "System", "Object")))
																			&& pd.GetMethod.IsPublic, out indexerDeclTypeRef);
 
					properties.Add((indexer, indexerDeclTypeRef, indexArg));
					if (indexer != null) //the case when we index on an array, not a list
					{
						var indexType = indexer.GetMethod.Parameters[0].ParameterType.ResolveGenericParameters(indexerDeclTypeRef);
						if (!TypeRefComparer.Default.Equals(indexType, module.TypeSystem.String) && !TypeRefComparer.Default.Equals(indexType, module.TypeSystem.Int32))
							throw new BuildException(BuildExceptionCode.BindingIndexerTypeUnsupported, lineInfo, null, indexType.FullName);
						previousPartTypeRef = indexer.PropertyType.ResolveGenericParameters(indexerDeclTypeRef);
					}
					else
					{
						if (previousPartTypeRef.IsArray)
							previousPartTypeRef = previousPartTypeRef.GetElementType();
						
						previousPartTypeRef.ResolveCached(context.Cache);
					}
 
				}
			}
			pathProperties = properties;
			return true;
		}
 
		static IEnumerable<Instruction> DigProperties(IEnumerable<(PropertyDefinition property, TypeReference propDeclTypeRef, string indexArg)> properties, Dictionary<TypeReference, VariableDefinition> locs, Func<Instruction> fallback, IXmlLineInfo lineInfo, ModuleDefinition module)
		{
			var first = true;
 
			foreach (var (property, propDeclTypeRef, indexArg) in properties)
			{
				if (!first && propDeclTypeRef.IsValueType)
				{
					var importedPropDeclTypeRef = module.ImportReference(propDeclTypeRef);
 
					if (!locs.TryGetValue(importedPropDeclTypeRef, out var loc))
					{
						loc = new VariableDefinition(importedPropDeclTypeRef);
						locs[importedPropDeclTypeRef] = loc;
					}
 
					yield return Create(Stloc, loc);
					yield return Create(Ldloca, loc);
				}
 
				if (fallback != null && !propDeclTypeRef.IsValueType)
				{
					yield return Create(Dup);
					yield return Create(Brfalse, fallback());
				}
 
				if (indexArg != null)
				{
					if (property == null && int.TryParse(indexArg, out int index)) //array
						yield return Create(Ldc_I4, index);
					else
					{
						var indexType = property.GetMethod.Parameters[0].ParameterType.ResolveGenericParameters(propDeclTypeRef);
						if (TypeRefComparer.Default.Equals(indexType, module.TypeSystem.String))
							yield return Create(Ldstr, indexArg);
						else if (TypeRefComparer.Default.Equals(indexType, module.TypeSystem.Int32) && int.TryParse(indexArg, out index))
							yield return Create(Ldc_I4, index);
						else
							throw new BuildException(BindingIndexerParse, lineInfo, null, indexArg, property.Name);
					}
				}
 
				if (indexArg != null && property == null)
					yield return Create(Ldelem_Ref);
				else
				{
					var getMethod = module.ImportReference((module.ImportReference(property.GetMethod)).ResolveGenericParameters(propDeclTypeRef, module));
 
					if (property.GetMethod.IsVirtual)
						yield return Create(Callvirt, getMethod);
					else
						yield return Create(Call, getMethod);
				}
				first = false;
			}
		}
 
		static IEnumerable<Instruction> CompiledBindingGetGetter(TypeReference tSourceRef, TypeReference tPropertyRef, IList<(PropertyDefinition property, TypeReference propDeclTypeRef, string indexArg)> properties, ElementNode node, ILContext context)
		{
			//				.method private static hidebysig default valuetype[mscorlib] System.ValueTuple`2<string, bool> '<Main>m__0' (class ViewModel A_0)  cil managed
			//				{
			//					.custom instance void class [mscorlib] System.Runtime.CompilerServices.CompilerGeneratedAttribute::'.ctor'() =  (01 00 00 00 ) // ....
			//					IL_0000:  ldarg.0 
			//					IL_0001:  dup
			//					IL_0002:  ldnull
			//					IL_0003:  ceq
			//					IL_0005:  brfalse IL_0013
			//					IL_000a:  pop
			//					IL_000b:  ldnull
			//					IL_000c:  ldc.i4.0 
			//					IL_000d:  newobj instance void valuetype[mscorlib]System.ValueTuple`2<string, bool>::'.ctor'(!0, !1)
			//					IL_0012:  ret
			//					IL_0013:  nop
			//					IL_0014:  call instance string class ViewModel::get_Text()
			//					IL_0019:  ldc.i4.1 
			//					IL_001a:  newobj instance void valuetype[mscorlib]System.ValueTuple`2<string, bool>::'.ctor'(!0, !1)
			//					IL_001f:  ret
			//				}
 
			var module = context.Module;
			var tupleRef = module.ImportReference(module.ImportReference(context.Cache, ("mscorlib", "System", "ValueTuple`2")).MakeGenericInstanceType(new[] { tPropertyRef, module.TypeSystem.Boolean }));
			var tupleCtorRef = module.ImportCtorReference(context.Cache, tupleRef, 2);
			tupleCtorRef = module.ImportReference(tupleCtorRef.ResolveGenericParameters(tupleRef, module));
			var getter = new MethodDefinition($"<{context.Body.Method.Name}>typedBindingsM__{context.Cache.TypedBindingCount++}",
											  MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Static,
											  tupleRef)
			{
				Parameters = { new ParameterDefinition(tSourceRef) },
				CustomAttributes = { new CustomAttribute(module.ImportCtorReference(context.Cache, ("mscorlib", "System.Runtime.CompilerServices", "CompilerGeneratedAttribute"), parameterTypes: null)) }
			};
 
			getter.Body.InitLocals = true;
			var il = getter.Body.GetILProcessor();
 
			if (properties == null || properties.Count == 0)
			{ //return self
				il.Emit(Ldarg_0);
				il.Emit(Ldc_I4_1); //true
				il.Emit(Newobj, tupleCtorRef);
				il.Emit(Ret);
			}
			else
			{
				var locs = new Dictionary<TypeReference, VariableDefinition>();
 
				if (tSourceRef.IsValueType)
					il.Emit(Ldarga_S, (byte)0);
				else
					il.Emit(Ldarg_0);
 
				Instruction pop = null;
				il.Append(DigProperties(properties, locs, () =>
				{
					if (pop == null)
						pop = Create(Pop);
 
					return pop;
				}, node as IXmlLineInfo, module));
 
				foreach (var loc in locs.Values)
					getter.Body.Variables.Add(loc);
 
				il.Emit(Ldc_I4_1); //true
				il.Emit(Newobj, tupleCtorRef);
				il.Emit(Ret);
 
				if (pop != null)
				{
					if (!locs.TryGetValue(tupleRef, out var defaultValueVarDef))
					{
						defaultValueVarDef = new VariableDefinition(tupleRef);
						getter.Body.Variables.Add(defaultValueVarDef);
					}
 
					il.Append(pop);
					il.Emit(Ldloca_S, defaultValueVarDef);
					il.Emit(Initobj, tupleRef);
					il.Emit(Ldloc, defaultValueVarDef);
					il.Emit(Ret);
				}
			}
			context.Body.Method.DeclaringType.Methods.Add(getter);
 
			//			IL_02fa:  ldnull
			//			IL_02fb:  ldftn valuetype[mscorlib]System.ValueTuple`2 <string,bool> class Test::'<Main>m__0'(class ViewModel)
			//			IL_0301:  newobj instance void class [mscorlib] System.Func`2<class ViewModel, valuetype[mscorlib] System.ValueTuple`2<string, bool>>::'.ctor'(object, native int)
			yield return Create(Ldnull);
			yield return Create(Ldftn, getter);
			yield return Create(Newobj, module.ImportCtorReference(context.Cache, ("mscorlib", "System", "Func`2"), paramCount: 2, classArguments: new[] { tSourceRef, tupleRef }));
		}
 
		static IEnumerable<Instruction> CompiledBindingGetSetter(TypeReference tSourceRef, TypeReference tPropertyRef, IList<(PropertyDefinition property, TypeReference propDeclTypeRef, string indexArg)> properties, ElementNode node, ILContext context)
		{
			if (properties == null || properties.Count == 0)
			{
				yield return Create(Ldnull);
				yield break;
			}
 
			//			.method private static hidebysig default void '<Main>m__1' (class ViewModel vm, string s)  cil managed
			//			{
			//				.custom instance void class [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::'.ctor'() =  (01 00 00 00 ) // ....
			//
			//				IL_0000:  ldarg.0 
			//				IL_0001:  callvirt instance class ViewModel class ViewModel::get_Model()
			//				IL_0006:  ldarg.1 
			//				IL_0007:  callvirt instance void class ViewModel::set_Text(string)
			//				IL_000c:  ret
			//			}
 
			var module = context.Module;
			var setter = new MethodDefinition($"<{context.Body.Method.Name}>typedBindingsM__{context.Cache.TypedBindingCount++}",
											  MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Static,
											  module.TypeSystem.Void)
			{
				Parameters = {
					new ParameterDefinition(tSourceRef),
					new ParameterDefinition(tPropertyRef)
				},
				CustomAttributes = {
					new CustomAttribute (module.ImportCtorReference(context.Cache, ("mscorlib", "System.Runtime.CompilerServices", "CompilerGeneratedAttribute"), parameterTypes: null))
				}
			};
			setter.Body.InitLocals = true;
 
			var il = setter.Body.GetILProcessor();
			if (!properties.Any() || properties.Last().property == null || properties.Last().property.SetMethod == null)
			{
				yield return Create(Ldnull); //throw or not ?
				yield break;
			}
 
			var setterRef = module.ImportReference(properties.Last().property.SetMethod);
			setterRef = module.ImportReference(setterRef.ResolveGenericParameters(properties.Last().propDeclTypeRef, module));
 
			if (tSourceRef.IsValueType)
				il.Emit(Ldarga_S, (byte)0);
			else
				il.Emit(Ldarg_0);
			var locs = new Dictionary<TypeReference, VariableDefinition>();
			Instruction pop = null;
			il.Append(DigProperties(properties.Take(properties.Count - 1), locs, () =>
			{
				if (pop == null)
					pop = Instruction.Create(Pop);
 
				return pop;
			}, node as IXmlLineInfo, module));
 
			foreach (var loc in locs.Values)
				setter.Body.Variables.Add(loc);
 
			(PropertyDefinition lastProperty, TypeReference lastPropDeclTypeRef, string lastIndexArg) = properties.Last();
			if (lastPropDeclTypeRef.IsValueType)
			{
				var importedPropDeclTypeRef = module.ImportReference(lastPropDeclTypeRef);
 
				if (!locs.TryGetValue(importedPropDeclTypeRef, out var loc))
				{
					loc = new VariableDefinition(importedPropDeclTypeRef);
					setter.Body.Variables.Add(loc);
				}
 
				il.Emit(Stloc, loc);
				il.Emit(Ldloca, loc);
			}
			else
			{
				if (pop == null)
					pop = Instruction.Create(Pop);
 
				il.Emit(Dup);
				il.Emit(Brfalse, pop);
			}
 
			if (lastIndexArg != null)
			{
				var indexType = lastProperty.GetMethod.Parameters[0].ParameterType.ResolveGenericParameters(lastPropDeclTypeRef);
				if (TypeRefComparer.Default.Equals(indexType, module.TypeSystem.String))
					il.Emit(Ldstr, lastIndexArg);
				else if (TypeRefComparer.Default.Equals(indexType, module.TypeSystem.Int32))
				{
					if (!int.TryParse(lastIndexArg, out int index))
						throw new BuildException(BindingIndexerParse, node as IXmlLineInfo, null, lastIndexArg, lastProperty.Name);
					il.Emit(Ldc_I4, index);
				}
			}
 
			il.Emit(Ldarg_1);
 
			if (properties.Last().property.SetMethod.IsVirtual)
				il.Emit(Callvirt, setterRef);
			else
				il.Emit(Call, setterRef);
 
			il.Emit(Ret);
 
			if (pop != null)
			{
				il.Append(pop);
				il.Emit(Ret);
			}
 
			context.Body.Method.DeclaringType.Methods.Add(setter);
 
			//			IL_0024: ldnull
			//			IL_0025: ldftn void class Test::'<Main>m__1'(class ViewModel, string)
			//			IL_002b: newobj instance void class [mscorlib]System.Action`2<class ViewModel, string>::'.ctor'(object, native int)
			yield return Create(Ldnull);
			yield return Create(Ldftn, setter);
			yield return Create(Newobj, module.ImportCtorReference(context.Cache, ("mscorlib", "System", "Action`2"),
																   paramCount: 2,
																   classArguments:
																   new[] { tSourceRef, tPropertyRef }));
		}
 
		static IEnumerable<Instruction> CompiledBindingGetHandlers(TypeReference tSourceRef, TypeReference tPropertyRef, IList<(PropertyDefinition property, TypeReference propDeclTypeRef, string indexArg)> properties, ElementNode node, ILContext context)
		{
			//			.method private static hidebysig default object '<Main>m__2'(class ViewModel vm)  cil managed {
			//				.custom instance void class [mscorlib] System.Runtime.CompilerServices.CompilerGeneratedAttribute::'.ctor'() =  (01 00 00 00 ) // ....
			//				IL_0000:  ldarg.0 
			//				IL_0001:  ret
			//			} // end of method Test::<Main>m__2
 
			//			.method private static hidebysig default object '<Main>m__3' (class ViewModel vm)  cil managed {
			//				.custom instance void class [mscorlib] System.Runtime.CompilerServices.CompilerGeneratedAttribute::'.ctor'() =  (01 00 00 00 ) // ....
			//				IL_0000:  ldarg.0 
			//				IL_0001:  callvirt instance class ViewModel class ViewModel::get_Model()
			//				IL_0006:  ret
			//			}
 
			var module = context.Module;
 
			var partGetters = new List<MethodDefinition>();
			if (properties == null || properties.Count == 0)
			{
				yield return Create(Ldnull);
				yield break;
			}
 
			for (int i = 0; i < properties.Count; i++)
			{
				var tuple = properties[i];
				var partGetter = new MethodDefinition($"<{context.Body.Method.Name}>typedBindingsM__{context.Cache.TypedBindingCount++}", MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Static, module.TypeSystem.Object)
				{
					Parameters = {
						new ParameterDefinition(tSourceRef)
					},
					CustomAttributes = {
						new CustomAttribute (module.ImportCtorReference(context.Cache, ("mscorlib", "System.Runtime.CompilerServices", "CompilerGeneratedAttribute"), parameterTypes: null))
					}
				};
				partGetter.Body.InitLocals = true;
				var il = partGetter.Body.GetILProcessor();
 
				if (i == 0)
				{ //return self
					il.Emit(Ldarg_0);
					if (tSourceRef.IsValueType)
						il.Emit(Box, module.ImportReference(tSourceRef));
 
					il.Emit(Ret);
					context.Body.Method.DeclaringType.Methods.Add(partGetter);
					partGetters.Add(partGetter);
					continue;
				}
 
				if (tSourceRef.IsValueType)
					il.Emit(Ldarga_S, (byte)0);
				else
					il.Emit(Ldarg_0);
				var lastGetterTypeRef = properties[i - 1].property?.PropertyType;
				var locs = new Dictionary<TypeReference, VariableDefinition>();
				il.Append(DigProperties(properties.Take(i), locs, null, node as IXmlLineInfo, module));
				foreach (var loc in locs.Values)
					partGetter.Body.Variables.Add(loc);
				if (lastGetterTypeRef != null && lastGetterTypeRef.IsValueType)
					il.Emit(Box, module.ImportReference(lastGetterTypeRef));
 
				il.Emit(Ret);
				context.Body.Method.DeclaringType.Methods.Add(partGetter);
				partGetters.Add(partGetter);
			}
 
			var funcObjRef = context.Module.ImportReference(module.ImportReference(context.Cache, ("mscorlib", "System", "Func`2")).MakeGenericInstanceType(new[] { tSourceRef, module.TypeSystem.Object }));
			var tupleRef = context.Module.ImportReference(module.ImportReference(context.Cache, ("mscorlib", "System", "Tuple`2")).MakeGenericInstanceType(new[] { funcObjRef, module.TypeSystem.String }));
			var funcCtor = module.ImportReference(funcObjRef.ResolveCached(context.Cache).GetConstructors().First());
			funcCtor = funcCtor.MakeGeneric(funcObjRef, new[] { tSourceRef, module.TypeSystem.Object });
			var tupleCtor = module.ImportReference(tupleRef.ResolveCached(context.Cache).GetConstructors().First());
			tupleCtor = tupleCtor.MakeGeneric(tupleRef, new[] { funcObjRef, module.TypeSystem.String });
 
			//			IL_003a:  ldc.i4.2 
			//			IL_003b:  newarr class [mscorlib] System.Tuple`2<class [mscorlib]System.Func`2<class ViewModel,object>,string>
 
			//			IL_0040:  dup
			//			IL_0041:  ldc.i4.0 
			//			IL_0049:  ldnull
			//			IL_004a:  ldftn object class Test::'<Main>m__2'(class ViewModel)
			//			IL_0050:  newobj instance void class [mscorlib]System.Func`2<class ViewModel, object>::'.ctor'(object, native int)
			//			IL_005f:  ldstr "Model"
			//			IL_0064:  newobj instance void class [mscorlib]System.Tuple`2<class [mscorlib]System.Func`2<class ViewModel, object>, string>::'.ctor'(!0, !1)
			//			IL_0069:  stelem.ref 
 
			//			IL_006a:  dup
			//			IL_006b:  ldc.i4.1 
			//			IL_0073:  ldnull
			//			IL_0074:  ldftn object class Test::'<Main>m__3'(class ViewModel)
			//			IL_007a:  newobj instance void class [mscorlib]System.Func`2<class ViewModel, object>::'.ctor'(object, native int)
			//			IL_0089:  ldstr "Text"
			//			IL_008e:  newobj instance void class [mscorlib]System.Tuple`2<class [mscorlib]System.Func`2<class ViewModel, object>, string>::'.ctor'(!0, !1)
			//			IL_0093:  stelem.ref 
 
			var handlers = new List<(MethodDefinition PartGetter, string PropertyName)>();
			for (int i = 0; i < properties.Count; i++)
			{
				if (properties[i].property == null)
					continue;
 
				var partGetter = partGetters[i];
				var propertyName = properties[i].Item1.Name;
 
				handlers.Add((partGetter, propertyName));
 
				// for indexers add also a handler for the specific index
				if (properties[i].Item3 is not null)
				{
					handlers.Add((partGetter, $"{propertyName}[{properties[i].Item3}]"));
				}
			}
 
			yield return Create(Ldc_I4, handlers.Count);
			yield return Create(Newarr, tupleRef);
 
			for (var i = 0; i < handlers.Count; i++)
			{
				yield return Create(Dup);
				yield return Create(Ldc_I4, i);
				yield return Create(Ldnull);
				yield return Create(Ldftn, handlers[i].PartGetter);
				yield return Create(Newobj, module.ImportReference(funcCtor));
				yield return Create(Ldstr, handlers[i].PropertyName);
				yield return Create(Newobj, module.ImportReference(tupleCtor));
				yield return Create(Stelem_Ref);
			}
		}
 
		public static IEnumerable<Instruction> SetPropertyValue(VariableDefinition parent, XmlName propertyName, INode valueNode, ILContext context, IXmlLineInfo iXmlLineInfo)
		{
			var localName = propertyName.LocalName;
			var bpRef = GetBindablePropertyReference(parent, propertyName.NamespaceURI, ref localName, out System.Boolean attached, context, iXmlLineInfo);
 
			//If the target is an event, connect
			if (CanConnectEvent(parent, localName, valueNode, attached, context))
				return ConnectEvent(parent, localName, valueNode, iXmlLineInfo, context);
 
			//If Value is DynamicResource, SetDynamicResource
			if (CanSetDynamicResource(bpRef, valueNode, context))
				return SetDynamicResource(parent, bpRef, valueNode as IElementNode, iXmlLineInfo, context);
 
			//If Value is a BindingBase and target is a BP, SetBinding
			if (CanSetBinding(bpRef, valueNode, context))
				return SetBinding(parent, bpRef, valueNode as IElementNode, iXmlLineInfo, context);
 
			//If it's a BP, SetValue ()
			if (CanSetValue(bpRef, attached, valueNode, iXmlLineInfo, context))
				return SetValue(parent, bpRef, valueNode, iXmlLineInfo, context);
 
			//If it's a property, set it
			if (CanSet(parent, localName, valueNode, context))
				return Set(parent, localName, valueNode, iXmlLineInfo, context);
 
			//If it's an already initialized property, add to it
			if (CanAdd(parent, propertyName, valueNode, iXmlLineInfo, context))
				return Add(parent, propertyName, valueNode, iXmlLineInfo, context);
 
			throw new BuildException(MemberResolution, iXmlLineInfo, null, localName);
		}
 
		public static IEnumerable<Instruction> GetPropertyValue(VariableDefinition parent, XmlName propertyName, ILContext context, IXmlLineInfo lineInfo, out TypeReference propertyType)
		{
			var module = context.Body.Method.Module;
			var localName = propertyName.LocalName;
			bool attached;
			var bpRef = GetBindablePropertyReference(parent, propertyName.NamespaceURI, ref localName, out attached, context, lineInfo);
 
			//If it's a BP, GetValue ()
			if (CanGetValue(parent, bpRef, attached, lineInfo, context, out _))
				return GetValue(parent, bpRef, lineInfo, context, out propertyType);
 
			//If it's a property, get it
			if (CanGet(parent, localName, context, out _))
				return Get(parent, localName, lineInfo, context, out propertyType);
 
			throw new BuildException(PropertyResolution, lineInfo, null, localName, parent.VariableType.FullName);
		}
 
		static FieldReference GetBindablePropertyReference(VariableDefinition parent, string namespaceURI, ref string localName, out bool attached, ILContext context, IXmlLineInfo iXmlLineInfo)
			=> GetBindablePropertyReference(parent.VariableType, namespaceURI, ref localName, out attached, context, iXmlLineInfo);
 
		public static FieldReference GetBindablePropertyReference(TypeReference bpOwnerType, string namespaceURI, ref string localName, out bool attached, ILContext context, IXmlLineInfo iXmlLineInfo)
		{
			var module = context.Body.Method.Module;
			TypeReference declaringTypeReference;
 
			//If it's an attached BP, update elementType and propertyName
			attached = GetNameAndTypeRef(ref bpOwnerType, namespaceURI, ref localName, context, iXmlLineInfo);
			var name = $"{localName}Property";
			FieldDefinition bpDef = bpOwnerType.GetField(context.Cache,
														fd => fd.Name == name &&
														fd.IsStatic &&
														(fd.IsPublic || fd.IsAssembly), out declaringTypeReference);
			if (bpDef == null)
				return null;
			var bpRef = module.ImportReference(bpDef.ResolveGenericParameters(declaringTypeReference));
			bpRef.FieldType = module.ImportReference(bpRef.FieldType);
 
			var isObsolete = bpDef.CustomAttributes.Any(ca => ca.AttributeType.FullName == "System.ObsoleteAttribute");
			if (isObsolete)
				context.LoggingHelper.LogWarningOrError(BuildExceptionCode.ObsoleteProperty, context.XamlFilePath, iXmlLineInfo.LineNumber, iXmlLineInfo.LinePosition, 0, 0, localName);
 
			return bpRef;
		}
 
		static bool CanConnectEvent(VariableDefinition parent, string localName, INode valueNode, bool attached, ILContext context)
		{
			return !attached && valueNode is ValueNode && parent.VariableType.GetEvent(context.Cache, ed => ed.Name == localName, out _) != null;
		}
 
		static IEnumerable<Instruction> ConnectEvent(VariableDefinition parent, string localName, INode valueNode, IXmlLineInfo iXmlLineInfo, ILContext context)
		{
			var elementType = parent.VariableType;
			var module = context.Body.Method.Module;
			TypeReference eventDeclaringTypeRef;
			var eventinfo = elementType.GetEvent(context.Cache, ed => ed.Name == localName, out eventDeclaringTypeRef);
			var adder = module.ImportReference(eventinfo.AddMethod);
			adder = adder.ResolveGenericParameters(eventDeclaringTypeRef, module);
 
			//			IL_0007:  ldloc.0 
			//			IL_0008:  ldarg.0 
			//
			//			IL_0009:  ldftn instance void class Microsoft.Maui.Controls.Xaml.XamlcTests.MyPage::OnButtonClicked(object, class [mscorlib]System.EventArgs)
			//OR, if the handler is virtual
			//			IL_000x:  ldarg.0 
			//			IL_0009:  ldvirtftn instance void class Microsoft.Maui.Controls.Xaml.XamlcTests.MyPage::OnButtonClicked(object, class [mscorlib]System.EventArgs)
			//
			//			IL_000f:  newobj instance void class [mscorlib]System.EventHandler::'.ctor'(object, native int)
			//			IL_0014:  callvirt instance void class [Microsoft.Maui.Controls]Microsoft.Maui.Controls.Button::add_Clicked(class [mscorlib]System.EventHandler)
 
			var value = ((ValueNode)valueNode).Value;
 
			yield return Create(Ldloc, parent);
			var declaringType = context.Body.Method.DeclaringType;
			while (declaringType.IsNested)
				declaringType = declaringType.DeclaringType;
			var handler = declaringType.AllMethods(context.Cache).FirstOrDefault(md =>
			{
				if (md.methodDef.Name != value as string)
					return false;
 
				//check if the handler signature matches the Invoke signature;
				var invoke = module.ImportReference(eventinfo.EventType.ResolveCached(context.Cache).GetMethods().First(eventmd => eventmd.Name == "Invoke"));
				invoke = invoke.ResolveGenericParameters(eventinfo.EventType, module);
				if (!md.methodDef.ReturnType.InheritsFromOrImplements(context.Cache, invoke.ReturnType) || invoke.Parameters.Count != md.methodDef.Parameters.Count)
					return false;
 
				if (!invoke.ContainsGenericParameter)
					for (var i = 0; i < invoke.Parameters.Count; i++)
						if (!invoke.Parameters[i].ParameterType.InheritsFromOrImplements(context.Cache, md.methodDef.Parameters[i].ParameterType))
							return false;
				//TODO check generic parameters if any
 
				return true;
			});
			MethodReference handlerRef = null;
			if (handler.methodDef != null)
				handlerRef = handler.methodDef.ResolveGenericParameters(handler.declTypeRef, module);
			if (handler.methodDef == null)
				throw new BuildException(MissingEventHandler, iXmlLineInfo, null, value, declaringType);
 
			//FIXME: eventually get the right ctor instead fo the First() one, just in case another one could exists (not even sure it's possible).
			var ctor = module.ImportReference(eventinfo.EventType.ResolveCached(context.Cache).GetConstructors().First());
			ctor = ctor.ResolveGenericParameters(eventinfo.EventType, module);
 
			if (handler.methodDef.IsStatic)
			{
				yield return Create(Ldnull);
			}
			else
			{
				if (context.Root is VariableDefinition)
					foreach (var instruction in (context.Root as VariableDefinition).LoadAs(context.Cache, ctor.Parameters[0].ParameterType.ResolveGenericParameters(ctor), module))
						yield return instruction;
				else if (context.Root is FieldDefinition)
				{
					yield return Create(Ldarg_0);
					yield return Create(Ldfld, context.Root as FieldDefinition);
				}
				else
					throw new InvalidProgramException();
			}
 
			if (handler.methodDef.IsVirtual)
			{
				yield return Create(Ldarg_0);
				yield return Create(Ldvirtftn, handlerRef);
			}
			else
				yield return Create(Ldftn, handlerRef);
 
			yield return Create(Newobj, module.ImportReference(ctor));
			//Check if the handler has the same signature as the ctor (it should)
			yield return Create(Callvirt, module.ImportReference(adder));
		}
 
		static bool CanSetDynamicResource(FieldReference bpRef, INode valueNode, ILContext context)
		{
			if (bpRef == null)
				return false;
			var elementNode = valueNode as IElementNode;
			if (elementNode == null)
				return false;
 
			VariableDefinition varValue;
			if (!context.Variables.TryGetValue(valueNode as IElementNode, out varValue))
				return false;
			return varValue.VariableType.FullName == typeof(DynamicResource).FullName;
		}
 
		static IEnumerable<Instruction> SetDynamicResource(VariableDefinition parent, FieldReference bpRef, IElementNode elementNode, IXmlLineInfo iXmlLineInfo, ILContext context)
		{
			var module = context.Body.Method.Module;
			var dynamicResourceType = ("Microsoft.Maui.Controls", "Microsoft.Maui.Controls.Internals", "DynamicResource");
			var dynamicResourceHandlerType = ("Microsoft.Maui.Controls", "Microsoft.Maui.Controls.Internals", "IDynamicResourceHandler");
 
			foreach (var instruction in parent.LoadAs(context.Cache, module.GetTypeDefinition(context.Cache, dynamicResourceHandlerType), module))
				yield return instruction;
			yield return Create(Ldsfld, bpRef);
			foreach (var instruction in context.Variables[elementNode].LoadAs(context.Cache, module.GetTypeDefinition(context.Cache, dynamicResourceType), module))
				yield return instruction;
			yield return Create(Callvirt, module.ImportPropertyGetterReference(context.Cache, dynamicResourceType, propertyName: "Key"));
			yield return Create(Callvirt, module.ImportMethodReference(context.Cache,
																	   dynamicResourceHandlerType,
																	   methodName: "SetDynamicResource",
																	   parameterTypes: new[] {
																		   ("Microsoft.Maui.Controls", "Microsoft.Maui.Controls", "BindableProperty"),
																		   ("mscorlib", "System", "String"),
																	   }));
		}
 
		static bool CanSetBinding(FieldReference bpRef, INode valueNode, ILContext context)
		{
			var module = context.Body.Method.Module;
 
			if (bpRef == null)
				return false;
			if (!(valueNode is IElementNode elementNode))
				return false;
 
			if (!context.Variables.TryGetValue(valueNode as IElementNode, out VariableDefinition varValue))
				return false;
			var implicitOperator = varValue.VariableType.GetImplicitOperatorTo(context.Cache, module.ImportReference(context.Cache, ("Microsoft.Maui.Controls", "Microsoft.Maui.Controls", "BindingBase")), module);
			if (implicitOperator != null)
				return true;
 
			return varValue.VariableType.InheritsFromOrImplements(context.Cache, module.ImportReference(context.Cache, ("Microsoft.Maui.Controls", "Microsoft.Maui.Controls", "BindingBase")));
		}
 
		static IEnumerable<Instruction> SetBinding(VariableDefinition parent, FieldReference bpRef, IElementNode elementNode, IXmlLineInfo iXmlLineInfo, ILContext context)
		{
			var module = context.Body.Method.Module;
			var bindableObjectType = ("Microsoft.Maui.Controls", "Microsoft.Maui.Controls", "BindableObject");
			var parameterTypes = new[] {
				("Microsoft.Maui.Controls", "Microsoft.Maui.Controls", "BindableProperty"),
				("Microsoft.Maui.Controls", "Microsoft.Maui.Controls", "BindingBase"),
			};
 
			//TODO: check if parent is a BP
			foreach (var instruction in parent.LoadAs(context.Cache, module.GetTypeDefinition(context.Cache, bindableObjectType), module))
				yield return instruction;
			yield return Create(Ldsfld, bpRef);
			foreach (var instruction in context.Variables[elementNode].LoadAs(context.Cache, module.GetTypeDefinition(context.Cache, parameterTypes[1]), module))
				yield return instruction;
			yield return Create(Callvirt, module.ImportMethodReference(context.Cache, ("Microsoft.Maui.Controls", "Microsoft.Maui.Controls", "BindableObject"),
																	   methodName: "SetBinding",
																	   parameterTypes: parameterTypes));
		}
 
		static bool CanSetValue(FieldReference bpRef, bool attached, INode node, IXmlLineInfo iXmlLineInfo, ILContext context)
		{
			var module = context.Body.Method.Module;
 
			if (bpRef == null)
				return false;
 
			if (node is ValueNode valueNode && valueNode.CanConvertValue(context, bpRef))
				return true;
 
			if (!(node is IElementNode elementNode))
				return false;
 
			if (!context.Variables.TryGetValue(elementNode, out VariableDefinition varValue))
				return false;
 
			var bpTypeRef = bpRef.GetBindablePropertyType(context.Cache, iXmlLineInfo, module);
			// If it's an attached BP, there's no second chance to handle IMarkupExtensions, so we try here.
			// Worst case scenario ? InvalidCastException at runtime
			if (varValue.VariableType.FullName == "System.Object")
				return true;
			var implicitOperator = varValue.VariableType.GetImplicitOperatorTo(context.Cache, bpTypeRef, module);
			if (implicitOperator != null)
				return true;
 
			//as we're in the SetValue Scenario, we can accept value types, they'll be boxed
			if (varValue.VariableType.IsValueType && bpTypeRef.FullName == "System.Object")
				return true;
 
			return varValue.VariableType.InheritsFromOrImplements(context.Cache, bpTypeRef) || varValue.VariableType.FullName == "System.Object";
		}
 
		static bool CanGetValue(VariableDefinition parent, FieldReference bpRef, bool attached, IXmlLineInfo iXmlLineInfo, ILContext context, out TypeReference propertyType)
		{
			var module = context.Body.Method.Module;
			propertyType = null;
 
			if (bpRef == null)
				return false;
 
			if (!parent.VariableType.InheritsFromOrImplements(context.Cache, module.ImportReference(context.Cache, ("Microsoft.Maui.Controls", "Microsoft.Maui.Controls", "BindableObject"))))
				return false;
 
			propertyType = bpRef.GetBindablePropertyType(context.Cache, iXmlLineInfo, module);
			return true;
		}
 
		static IEnumerable<Instruction> SetValue(VariableDefinition parent, FieldReference bpRef, INode node, IXmlLineInfo iXmlLineInfo, ILContext context)
		{
			var valueNode = node as ValueNode;
			var elementNode = node as IElementNode;
			var module = context.Body.Method.Module;
			var bindableObjectType = ("Microsoft.Maui.Controls", "Microsoft.Maui.Controls", "BindableObject");
 
			//			IL_0007:  ldloc.0 
			//			IL_0008:  ldsfld class [Microsoft.Maui.Controls]Microsoft.Maui.Controls.BindableProperty [Microsoft.Maui.Controls]Microsoft.Maui.Controls.Label::TextProperty
			//			IL_000d:  ldstr "foo"
			//			IL_0012:  callvirt instance void class [Microsoft.Maui.Controls]Microsoft.Maui.Controls.BindableObject::SetValue(class [Microsoft.Maui.Controls]Microsoft.Maui.Controls.BindableProperty, object)
 
			foreach (var instruction in parent.LoadAs(context.Cache, module.GetTypeDefinition(context.Cache, bindableObjectType), module))
				yield return instruction;
 
			yield return Create(Ldsfld, bpRef);
 
			if (valueNode != null)
			{
				//FIXME which services are required here ?
				foreach (var instruction in valueNode.PushConvertedValue(context, bpRef, (requiredServices) => valueNode.PushServiceProvider(context, requiredServices, bpRef: bpRef), true, false))
					yield return instruction;
			}
			else if (elementNode != null)
			{
				var @else = Create(OpCodes.Nop);
				var endif = Create(OpCodes.Nop);
 
				if (context.Variables[elementNode].VariableType.FullName == "System.Object")
				{
					//if(value != null && value.GetType().IsAssignableFrom(typeof(BindingBase)))
					yield return Create(Ldloc, context.Variables[elementNode]);
					yield return Create(Brfalse, @else);
 
					yield return Create(Ldtoken, module.ImportReference(context.Cache, ("Microsoft.Maui.Controls", "Microsoft.Maui.Controls", "BindingBase")));
					yield return Create(Call, module.ImportMethodReference(context.Cache, ("mscorlib", "System", "Type"), methodName: "GetTypeFromHandle", parameterTypes: new[] { ("mscorlib", "System", "RuntimeTypeHandle") }, isStatic: true));
					yield return Create(Ldloc, context.Variables[elementNode]);
					yield return Create(Callvirt, module.ImportMethodReference(context.Cache, ("mscorlib", "System", "Object"), methodName: "GetType", paramCount: 0));
					yield return Create(Callvirt, module.ImportMethodReference(context.Cache, ("mscorlib", "System", "Type"), methodName: "IsAssignableFrom", parameterTypes: new[] { ("mscorlib", "System", "Type") }));
					yield return Create(Brfalse, @else);
					//then
					yield return Create(Ldloc, context.Variables[elementNode]);
					yield return Create(Br, endif);
					//else
					yield return @else;
				}
				var bpTypeRef = bpRef.GetBindablePropertyType(context.Cache, iXmlLineInfo, module);
				foreach (var instruction in context.Variables[elementNode].LoadAs(context.Cache, bpTypeRef, module))
					yield return instruction;
				if (bpTypeRef.IsValueType)
					yield return Create(Box, module.ImportReference(bpTypeRef));
 
				//endif
				if (context.Variables[elementNode].VariableType.FullName == "System.Object")
					yield return endif;
 
			}
			yield return Create(Callvirt, module.ImportMethodReference(context.Cache,
																	   bindableObjectType,
																	   methodName: "SetValue",
																	   parameterTypes: new[] {
																		   ("Microsoft.Maui.Controls", "Microsoft.Maui.Controls", "BindableProperty"),
																		   ("mscorlib", "System", "Object"),
																	   }));
		}
 
		static IEnumerable<Instruction> GetValue(VariableDefinition parent, FieldReference bpRef, IXmlLineInfo iXmlLineInfo, ILContext context, out TypeReference propertyType)
		{
			propertyType = bpRef.GetBindablePropertyType(context.Cache, iXmlLineInfo, context.Body.Method.Module);
			return GetValue(parent, bpRef, iXmlLineInfo, context);
		}
 
		static IEnumerable<Instruction> GetValue(VariableDefinition parent, FieldReference bpRef, IXmlLineInfo iXmlLineInfo, ILContext context)
		{
			var module = context.Body.Method.Module;
			var bindableObjectType = ("Microsoft.Maui.Controls", "Microsoft.Maui.Controls", "BindableObject");
 
			foreach (var instruction in parent.LoadAs(context.Cache, module.GetTypeDefinition(context.Cache, bindableObjectType), module))
				yield return instruction;
 
			yield return Create(Ldsfld, bpRef);
			yield return Create(Callvirt, module.ImportMethodReference(context.Cache,
																		bindableObjectType,
																		methodName: "GetValue",
																		parameterTypes: new[] { ("Microsoft.Maui.Controls", "Microsoft.Maui.Controls", "BindableProperty") }));
		}
 
		static bool CanSet(VariableDefinition parent, string localName, INode node, ILContext context)
		{
			var module = context.Body.Method.Module;
			TypeReference declaringTypeReference;
			var property = parent.VariableType.GetProperty(context.Cache, pd => pd.Name == localName, out declaringTypeReference);
			if (property == null)
				return false;
			var propertyType = property.PropertyType.ResolveGenericParameters(declaringTypeReference);
			var propertySetter = property.SetMethod;
			if (propertySetter == null || !propertySetter.IsPublic || propertySetter.IsStatic)
				return false;
 
			var valueNode = node as ValueNode;
			if (valueNode != null && valueNode.CanConvertValue(context, propertyType, new ICustomAttributeProvider[] { property, propertyType.ResolveCached(context.Cache) }))
				return true;
 
			var elementNode = node as IElementNode;
			if (elementNode == null)
				return false;
 
			var vardef = context.Variables[elementNode];
			var implicitOperator = vardef.VariableType.GetImplicitOperatorTo(context.Cache, propertyType, module);
 
			if (vardef.VariableType.InheritsFromOrImplements(context.Cache, propertyType))
				return true;
			if (implicitOperator != null)
				return true;
			if (propertyType.FullName == "System.Object")
				return true;
 
			//I'd like to get rid of this condition. This comment used to be //TODO replace latest check by a runtime type check
			if (vardef.VariableType.FullName == "System.Object")
				return true;
 
			return false;
		}
 
		static bool CanGet(VariableDefinition parent, string localName, ILContext context, out TypeReference propertyType)
		{
			var module = context.Body.Method.Module;
			propertyType = null;
			TypeReference declaringTypeReference;
			var property = parent.VariableType.GetProperty(context.Cache, pd => pd.Name == localName, out declaringTypeReference);
			if (property == null)
				return false;
			var propertyGetter = property.GetMethod;
			if (propertyGetter == null || !propertyGetter.IsPublic || propertyGetter.IsStatic)
				return false;
 
			module.ImportReference(parent.VariableType.ResolveCached(context.Cache));
			var propertyGetterRef = module.ImportReference(module.ImportReference(propertyGetter).ResolveGenericParameters(declaringTypeReference, module));
			propertyGetterRef.ImportTypes(module);
			propertyType = propertyGetterRef.ReturnType.ResolveGenericParameters(declaringTypeReference);
 
			return true;
		}
 
		static IEnumerable<Instruction> Set(VariableDefinition parent, string localName, INode node, IXmlLineInfo iXmlLineInfo, ILContext context)
		{
			var module = context.Body.Method.Module;
			TypeReference declaringTypeReference;
			var property = parent.VariableType.GetProperty(context.Cache, pd => pd.Name == localName, out declaringTypeReference);
			var propertyIsObsolete = property.CustomAttributes.Any(ca => ca.AttributeType.FullName == "System.ObsoleteAttribute");
			if (propertyIsObsolete)
				context.LoggingHelper.LogWarningOrError(BuildExceptionCode.ObsoleteProperty, context.XamlFilePath, iXmlLineInfo.LineNumber, iXmlLineInfo.LinePosition, 0, 0, localName);
 
			var propertySetter = property.SetMethod;
			var propertySetterIsObsolete = propertySetter.CustomAttributes.Any(ca => ca.AttributeType.FullName == "System.ObsoleteAttribute");
			if (propertySetterIsObsolete)
				context.LoggingHelper.LogWarningOrError(BuildExceptionCode.ObsoleteProperty, context.XamlFilePath, iXmlLineInfo.LineNumber, iXmlLineInfo.LinePosition, 0, 0, localName);
 
			//			IL_0007:  ldloc.0
			//			IL_0008:  ldstr "foo"
			//			IL_000d:  callvirt instance void class [Microsoft.Maui.Controls]Microsoft.Maui.Controls.Label::set_Text(string)
 
			module.ImportReference(parent.VariableType.ResolveCached(context.Cache));
			var propertySetterRef = module.ImportReference(module.ImportReference(propertySetter).ResolveGenericParameters(declaringTypeReference, module));
			propertySetterRef.ImportTypes(module);
			var propertyType = module.ImportReference(property.PropertyType.ResolveGenericParameters(declaringTypeReference));
			var valueNode = node as ValueNode;
			var elementNode = node as IElementNode;
 
			//if it's a value type, load the address so we can invoke methods on it
			if (parent.VariableType.IsValueType)
				yield return Instruction.Create(OpCodes.Ldloca, parent);
			else
				yield return Instruction.Create(OpCodes.Ldloc, parent);
 
			if (valueNode != null)
			{
				//FIXME which services are required here ?
				foreach (var instruction in valueNode.PushConvertedValue(context, propertyType, new ICustomAttributeProvider[] { property, propertyType.ResolveCached(context.Cache) }, (requiredServices) => valueNode.PushServiceProvider(context, requiredServices, propertyRef: property), false, true))
					yield return instruction;
				if (parent.VariableType.IsValueType)
					yield return Instruction.Create(OpCodes.Call, propertySetterRef);
				else
					yield return Instruction.Create(OpCodes.Callvirt, propertySetterRef);
			}
			else if (elementNode != null)
			{
				foreach (var instruction in context.Variables[elementNode].LoadAs(context.Cache, propertyType, module))
					yield return instruction;
				if (parent.VariableType.IsValueType)
					yield return Instruction.Create(OpCodes.Call, propertySetterRef);
				else
					yield return Instruction.Create(OpCodes.Callvirt, propertySetterRef);
			}
		}
 
		static IEnumerable<Instruction> Get(VariableDefinition parent, string localName, IXmlLineInfo iXmlLineInfo, ILContext context, out TypeReference propertyType)
		{
			var module = context.Body.Method.Module;
			var property = parent.VariableType.GetProperty(context.Cache, pd => pd.Name == localName, out var declaringTypeReference);
			var propertyGetter = property.GetMethod;
 
			module.ImportReference(parent.VariableType.ResolveCached(context.Cache));
			var propertyGetterRef = module.ImportReference(module.ImportReference(propertyGetter).ResolveGenericParameters(declaringTypeReference, module));
			propertyGetterRef.ImportTypes(module);
			propertyType = propertyGetterRef.ReturnType.ResolveGenericParameters(declaringTypeReference);
 
			if (parent.VariableType.IsValueType)
				return new[] {
					Create(Ldloca, parent),
					Create(Call, propertyGetterRef),
				};
			else
				return new[] {
					Create(Ldloc, parent),
					Create(Callvirt, propertyGetterRef),
				};
		}
 
		static bool CanAdd(VariableDefinition parent, XmlName propertyName, INode valueNode, IXmlLineInfo lineInfo, ILContext context)
		{
			var module = context.Body.Method.Module;
			var localName = propertyName.LocalName;
			var bpRef = GetBindablePropertyReference(parent, propertyName.NamespaceURI, ref localName, out var attached, context, lineInfo);
 
			if (!(valueNode is IElementNode elementNode))
				return false;
 
			if (!CanGetValue(parent, bpRef, attached, null, context, out TypeReference propertyType)
				&& !CanGet(parent, localName, context, out propertyType))
				return false;
 
			if (!context.Variables.TryGetValue(elementNode, out VariableDefinition varValue))
				return false;
 
			var adderTuple = propertyType.GetMethods(context.Cache, md => md.Name == "Add"
														&& md.Parameters.Count == 1, module).FirstOrDefault();
			if (adderTuple == null)
				return false;
 
			var adderRef = module.ImportReference(adderTuple.Item1);
			adderRef = module.ImportReference(adderRef.ResolveGenericParameters(adderTuple.Item2, module));
			var paramType = adderRef.Parameters[0].ParameterType.ResolveGenericParameters(adderRef);
			if (varValue.VariableType.InheritsFromOrImplements(context.Cache, paramType))
				return true;
 
			if (varValue.VariableType.GetImplicitOperatorTo(context.Cache, paramType, module) != null)
				return true;
 
			if (paramType.FullName == "System.Object" && varValue.VariableType.IsValueType)
				return true;
 
			return CanAddToResourceDictionary(parent, propertyType, elementNode, lineInfo, context);
		}
 
		static bool CanAddToResourceDictionary(VariableDefinition parent, TypeReference collectionType, IElementNode node, IXmlLineInfo lineInfo, ILContext context)
		{
			if (collectionType.FullName != "Microsoft.Maui.Controls.ResourceDictionary"
				&& !collectionType.InheritsFromOrImplements(context.Cache, context.Module.ImportReference(context.Cache, ("Microsoft.Maui.Controls", "Microsoft.Maui.Controls", "ResourceDictionary"))))
				return false;
 
			if (node.Properties.ContainsKey(XmlName.xKey))
			{
				var valueNode = node.Properties[XmlName.xKey] as ValueNode ?? throw new BuildException(XKeyNotLiteral, lineInfo, null);
				var key = (valueNode).Value as string;
				var names = context.Cache.GetResourceNamesInUse(parent);
				if (names.Contains(key))
					throw new BuildException(ResourceDictDuplicateKey, lineInfo, null, key);
				return true;
			}
 
			//is there a RD.Add() overrides that accepts this ?
			var nodeTypeRef = context.Variables[node].VariableType;
			var module = context.Body.Method.Module;
			if (module.ImportMethodReference(context.Cache,
											 module.GetTypeDefinition(context.Cache, ("Microsoft.Maui.Controls", "Microsoft.Maui.Controls", "ResourceDictionary")),
											 methodName: "Add",
											 parameterTypes: new[] { (nodeTypeRef) }) != null)
				return true;
			throw new BuildException(ResourceDictMissingKey, lineInfo, null);
		}
 
		static IEnumerable<Instruction> Add(VariableDefinition parent, XmlName propertyName, INode node, IXmlLineInfo iXmlLineInfo, ILContext context)
		{
			var module = context.Body.Method.Module;
			var elementNode = node as IElementNode;
			var vardef = context.Variables[elementNode];
 
			TypeReference propertyType;
			foreach (var instruction in GetPropertyValue(parent, propertyName, context, iXmlLineInfo, out propertyType))
				yield return instruction;
 
			if (CanAddToResourceDictionary(parent, propertyType, elementNode, iXmlLineInfo, context))
			{
				foreach (var instruction in AddToResourceDictionary(parent, elementNode, iXmlLineInfo, context))
					yield return instruction;
				yield break;
			}
 
			var adderTuple = propertyType.GetMethods(context.Cache, md => md.Name == "Add" && md.Parameters.Count == 1, module).FirstOrDefault();
			var adderRef = module.ImportReference(adderTuple.Item1);
			adderRef = module.ImportReference(adderRef.ResolveGenericParameters(adderTuple.Item2, module));
 
			foreach (var instruction in vardef.LoadAs(context.Cache, adderRef.Parameters[0].ParameterType.ResolveGenericParameters(adderRef), module))
				yield return instruction;
			yield return Instruction.Create(OpCodes.Callvirt, adderRef);
			if (adderRef.ReturnType.FullName != "System.Void")
				yield return Instruction.Create(OpCodes.Pop);
		}
 
		static IEnumerable<Instruction> AddToResourceDictionary(VariableDefinition parent, IElementNode node, IXmlLineInfo lineInfo, ILContext context)
		{
			var module = context.Body.Method.Module;
 
			if (node.Properties.ContainsKey(XmlName.xKey))
			{
				var names = context.Cache.GetResourceNamesInUse(parent);
				var valueNode = node.Properties[XmlName.xKey] as ValueNode ?? throw new BuildException(XKeyNotLiteral, lineInfo, null);
				var key = (valueNode).Value as string;
				names.Add(key);
 
				//				IL_0014:  ldstr "key"
				//				IL_0019:  ldstr "foo"
				//				IL_001e:  callvirt instance void class [Microsoft.Maui.Controls]Microsoft.Maui.Controls.ResourceDictionary::Add(string, object)
				yield return Create(Ldstr, (key));
				foreach (var instruction in context.Variables[node].LoadAs(context.Cache, module.TypeSystem.Object, module))
					yield return instruction;
				yield return Create(Callvirt, module.ImportMethodReference(context.Cache, ("Microsoft.Maui.Controls", "Microsoft.Maui.Controls", "ResourceDictionary"),
																		   methodName: "Add",
																		   parameterTypes: new[] {
																			   ("mscorlib", "System", "String"),
																			   ("mscorlib", "System", "Object"),
																		   }));
				yield break;
			}
 
			var nodeTypeRef = context.Variables[node].VariableType;
			yield return Create(Ldloc, context.Variables[node]);
			yield return Create(Callvirt, module.ImportMethodReference(context.Cache, ("Microsoft.Maui.Controls", "Microsoft.Maui.Controls", "ResourceDictionary"),
																	   methodName: "Add",
																	   parameterTypes: new[] { (nodeTypeRef.Scope.Name, nodeTypeRef.Namespace, nodeTypeRef.Name) }));
			yield break;
		}
 
		static bool GetNameAndTypeRef(ref TypeReference elementType, string namespaceURI, ref string localname,
			ILContext context, IXmlLineInfo lineInfo)
		{
			var dotIdx = localname.IndexOf('.');
			if (dotIdx > 0)
			{
				var typename = localname.Substring(0, dotIdx);
				localname = localname.Substring(dotIdx + 1);
				elementType = new XmlType(namespaceURI, typename, null).GetTypeReference(context.Cache, context.Body.Method.Module, lineInfo);
				return true;
			}
			return false;
		}
 
		static void SetDataTemplate(IElementNode parentNode, ElementNode node, ILContext parentContext,
			IXmlLineInfo xmlLineInfo)
		{
			var module = parentContext.Module;
			var dataTemplateType = ("Microsoft.Maui.Controls", "Microsoft.Maui.Controls", "ElementTemplate");
			var parentVar = parentContext.Variables[parentNode];
			//Push the DataTemplate to the stack, for setting the template
			parentContext.IL.Append(parentVar.LoadAs(parentContext.Cache, module.GetTypeDefinition(parentContext.Cache, dataTemplateType), module));
 
			//Create nested class
			//			.class nested private auto ansi sealed beforefieldinit '<Main>c__AnonStorey0'
			//			extends [mscorlib]System.Object
 
 
			var anonType = new TypeDefinition(
				null,
				"<" + parentContext.Body.Method.Name + ">_anonXamlCDataTemplate_" + parentContext.Cache.DataTemplateCount++,
				TypeAttributes.BeforeFieldInit |
				TypeAttributes.Sealed |
				TypeAttributes.NestedPrivate)
			{
				BaseType = module.TypeSystem.Object,
				CustomAttributes = {
					new CustomAttribute (module.ImportCtorReference(parentContext.Cache, ("mscorlib", "System.Runtime.CompilerServices", "CompilerGeneratedAttribute"), parameterTypes: null)),
				}
			};
 
			parentContext.Body.Method.DeclaringType.NestedTypes.Add(anonType);
			var ctor = anonType.AddDefaultConstructor(parentContext.Cache);
 
			var loadTemplate = new MethodDefinition("LoadDataTemplate",
				MethodAttributes.Assembly | MethodAttributes.HideBySig,
				module.TypeSystem.Object);
			loadTemplate.Body.InitLocals = true;
			anonType.Methods.Add(loadTemplate);
 
			var parentValues = new FieldDefinition("parentValues", FieldAttributes.Assembly, module.ImportArrayReference(parentContext.Cache, ("mscorlib", "System", "Object")));
			anonType.Fields.Add(parentValues);
 
			TypeReference rootType = null;
			var vdefRoot = parentContext.Root as VariableDefinition;
			if (vdefRoot != null)
				rootType = vdefRoot.VariableType;
			var fdefRoot = parentContext.Root as FieldDefinition;
			if (fdefRoot != null)
				rootType = fdefRoot.FieldType;
 
			var root = new FieldDefinition("root", FieldAttributes.Assembly, rootType);
			anonType.Fields.Add(root);
 
			//Fill the loadTemplate Body
			var templateIl = loadTemplate.Body.GetILProcessor();
			templateIl.Emit(OpCodes.Nop);
			var templateContext = new ILContext(templateIl, loadTemplate.Body, module, parentContext.Cache, parentValues)
			{
				Root = root,
				XamlFilePath = parentContext.XamlFilePath,
				LoggingHelper = parentContext.LoggingHelper,
				ValidateOnly = parentContext.ValidateOnly,
				CompileBindingsWithSource = parentContext.CompileBindingsWithSource,
			};
 
			//Instanciate nested class
			var parentIl = parentContext.IL;
			parentIl.Emit(OpCodes.Newobj, ctor);
 
			//Copy the scopes over for x:Reference to work
			//the scopes will be copied to fields of the anon type, the templateIL will copy them as VariableDef and populate context.Scopes
			var i = 0;
			foreach (var kvp in parentContext.Scopes)
			{
				//On the parentIL, copy the scope to a field
				parentIl.Emit(OpCodes.Dup); //Duplicate the nestedclass instance
				parentIl.Append(kvp.Value.Item1.LoadAs(parentContext.Cache, module.ImportReference(parentContext.Cache, ("Microsoft.Maui.Controls", "Microsoft.Maui.Controls.Internals", "NameScope")), module));
				var fieldDefScope = new FieldDefinition($"_scope{i++}", FieldAttributes.Assembly, module.ImportReference(parentContext.Cache, ("Microsoft.Maui.Controls", "Microsoft.Maui.Controls.Internals", "NameScope")));
				anonType.Fields.Add(fieldDefScope);
				parentIl.Emit(OpCodes.Stfld, fieldDefScope);
 
				//On the templateIL, copy the field to a var, and populate the Scopes
				templateIl.Emit(OpCodes.Ldarg_0);
				var varDefScope = new VariableDefinition(module.ImportReference(parentContext.Cache, ("Microsoft.Maui.Controls", "Microsoft.Maui.Controls.Internals", "NameScope")));
				loadTemplate.Body.Variables.Add(varDefScope);
				templateIl.Emit(OpCodes.Ldfld, fieldDefScope);
				templateIl.Emit(OpCodes.Stloc, varDefScope);
				templateContext.Scopes[kvp.Key] = new Tuple<VariableDefinition, IList<string>>(varDefScope, kvp.Value.Item2);
			}
 
			//inflate the template
			node.Accept(new CreateObjectVisitor(templateContext), null);
			node.Accept(new SetNamescopesAndRegisterNamesVisitor(templateContext), null);
			node.Accept(new SetFieldVisitor(templateContext), null);
			node.Accept(new SetResourcesVisitor(templateContext), null);
			node.Accept(new SetPropertiesVisitor(templateContext, stopOnResourceDictionary: true), null);
 
			templateIl.Append(templateContext.Variables[node].LoadAs(parentContext.Cache, module.TypeSystem.Object, module));
			templateIl.Emit(OpCodes.Ret);
 
 
			//Copy required local vars
			parentIl.Emit(OpCodes.Dup); //Duplicate the nestedclass instance
			parentIl.Append(node.PushParentObjectsArray(parentContext));
			parentIl.Emit(OpCodes.Stfld, parentValues);
			parentIl.Emit(OpCodes.Dup); //Duplicate the nestedclass instance
			if (parentContext.Root is VariableDefinition)
				parentIl.Append((parentContext.Root as VariableDefinition).LoadAs(parentContext.Cache, module.TypeSystem.Object, module));
			else if (parentContext.Root is FieldDefinition)
			{
				parentIl.Emit(OpCodes.Ldarg_0);
				parentIl.Emit(OpCodes.Ldfld, parentContext.Root as FieldDefinition);
			}
			else
				throw new InvalidProgramException();
			parentIl.Emit(OpCodes.Stfld, root);
 
			//SetDataTemplate
			parentIl.Emit(Ldftn, loadTemplate);
			parentIl.Emit(Newobj, module.ImportCtorReference(parentContext.Cache, ("mscorlib", "System", "Func`1"),
															 classArguments: new[] { ("mscorlib", "System", "Object") },
															 paramCount: 2));
 
			parentContext.IL.Emit(OpCodes.Callvirt, module.ImportPropertySetterReference(parentContext.Cache, dataTemplateType, propertyName: "LoadTemplate"));
 
			loadTemplate.Body.Optimize();
		}
 
		bool TrySetRuntimeName(XmlName propertyName, VariableDefinition variableDefinition, ValueNode node)
		{
			if (propertyName != XmlName.xName)
				return false;
 
			var attributes = variableDefinition.VariableType.ResolveCached(Context.Cache)
				.CustomAttributes.Where(attribute => attribute.AttributeType.FullName == "Microsoft.Maui.Controls.Xaml.RuntimeNamePropertyAttribute").ToList();
 
			if (!attributes.Any())
				return false;
 
			var runTimeName = attributes[0].ConstructorArguments[0].Value as string;
 
			if (string.IsNullOrEmpty(runTimeName))
				return false;
 
			Context.IL.Append(SetPropertyValue(variableDefinition, new XmlName("", runTimeName), node, Context, node));
			return true;
		}
	}
 
	class VariableDefinitionReference
	{
		public VariableDefinitionReference(VariableDefinition vardef)
		{
			VariableDefinition = vardef;
		}
 
		public VariableDefinition VariableDefinition { get; set; }
 
		public static implicit operator VariableDefinition(VariableDefinitionReference vardefref)
		{
			return vardefref.VariableDefinition;
		}
	}
}