private static XamlIlBindingPathNode TransformBindingPath(AstTransformationContext context, IXamlLineInfo lineInfo, Func <IXamlType> startTypeResolver, IXamlType selfType, IEnumerable <BindingExpressionGrammar.INode> bindingExpression) { List <IXamlIlBindingPathElementNode> transformNodes = new List <IXamlIlBindingPathElementNode>(); List <IXamlIlBindingPathElementNode> nodes = new List <IXamlIlBindingPathElementNode>(); foreach (var astNode in bindingExpression) { var targetTypeResolver = nodes.Count == 0 ? startTypeResolver : () => nodes[nodes.Count - 1].Type; switch (astNode) { case BindingExpressionGrammar.EmptyExpressionNode _: break; case BindingExpressionGrammar.NotNode _: transformNodes.Add(new XamlIlNotPathElementNode(context.Configuration.WellKnownTypes.Boolean)); break; case BindingExpressionGrammar.StreamNode _: { IXamlType targetType = targetTypeResolver(); IXamlType observableType; if (targetType.GenericTypeDefinition?.Equals(context.Configuration.TypeSystem.FindType("System.IObservable`1")) == true) { observableType = targetType; } else { observableType = targetType.GetAllInterfaces().FirstOrDefault(i => i.GenericTypeDefinition?.Equals(context.Configuration.TypeSystem.FindType("System.IObservable`1")) ?? false); } if (observableType != null) { nodes.Add(new XamlIlStreamObservablePathElementNode(observableType.GenericArguments[0])); break; } bool foundTask = false; for (var currentType = targetType; currentType != null; currentType = currentType.BaseType) { if (currentType.GenericTypeDefinition.Equals(context.Configuration.TypeSystem.GetType("System.Threading.Tasks.Task`1"))) { foundTask = true; nodes.Add(new XamlIlStreamTaskPathElementNode(currentType.GenericArguments[0])); break; } } if (foundTask) { break; } throw new XamlX.XamlParseException($"Compiled bindings do not support stream bindings for objects of type {targetType.FullName}.", lineInfo); } case BindingExpressionGrammar.PropertyNameNode propName: { IXamlType targetType = targetTypeResolver(); var avaloniaPropertyFieldNameMaybe = propName.PropertyName + "Property"; var avaloniaPropertyFieldMaybe = targetType.GetAllFields().FirstOrDefault(f => f.IsStatic && f.IsPublic && f.Name == avaloniaPropertyFieldNameMaybe); if (avaloniaPropertyFieldMaybe != null) { nodes.Add(new XamlIlAvaloniaPropertyPropertyPathElementNode(avaloniaPropertyFieldMaybe, XamlIlAvaloniaPropertyHelper.GetAvaloniaPropertyType(avaloniaPropertyFieldMaybe, context.GetAvaloniaTypes(), lineInfo))); } else if (GetAllDefinedProperties(targetType).FirstOrDefault(p => p.Name == propName.PropertyName) is IXamlProperty clrProperty) { nodes.Add(new XamlIlClrPropertyPathElementNode(clrProperty)); } else if (GetAllDefinedMethods(targetType).FirstOrDefault(m => m.Name == propName.PropertyName) is IXamlMethod method) { nodes.Add(new XamlIlClrMethodPathElementNode(method, context.Configuration.WellKnownTypes.Delegate)); } else { throw new XamlX.XamlParseException($"Unable to resolve property or method of name '{propName.PropertyName}' on type '{targetType}'.", lineInfo); } break; } case BindingExpressionGrammar.IndexerNode indexer: { IXamlType targetType = targetTypeResolver(); if (targetType.IsArray) { nodes.Add(new XamlIlArrayIndexerPathElementNode(targetType, indexer.Arguments, lineInfo)); break; } IXamlProperty property = null; foreach (var currentType in TraverseTypeHierarchy(targetType)) { var defaultMemberAttribute = currentType.CustomAttributes.FirstOrDefault(x => x.Type.Namespace == "System.Reflection" && x.Type.Name == "DefaultMemberAttribute"); if (defaultMemberAttribute != null) { property = currentType.GetAllProperties().FirstOrDefault(x => x.Name == (string)defaultMemberAttribute.Parameters[0]); break; } } if (property is null) { throw new XamlX.XamlParseException($"The type '${targetType}' does not have an indexer.", lineInfo); } IEnumerable <IXamlType> parameters = property.IndexerParameters; List <IXamlAstValueNode> values = new List <IXamlAstValueNode>(); int currentParamIndex = 0; foreach (var param in parameters) { var textNode = new XamlAstTextNode(lineInfo, indexer.Arguments[currentParamIndex], type: context.Configuration.WellKnownTypes.String); if (!XamlTransformHelpers.TryGetCorrectlyTypedValue(context, textNode, param, out var converted)) { throw new XamlX.XamlParseException( $"Unable to convert indexer parameter value of '{indexer.Arguments[currentParamIndex]}' to {param.GetFqn()}", textNode); } values.Add(converted); currentParamIndex++; } bool isNotifyingCollection = targetType.GetAllInterfaces().Any(i => i.FullName == "System.Collections.Specialized.INotifyCollectionChanged"); nodes.Add(new XamlIlClrIndexerPathElementNode(property, values, string.Join(",", indexer.Arguments), isNotifyingCollection)); break; } case BindingExpressionGrammar.AttachedPropertyNameNode attachedProp: var avaloniaPropertyFieldName = attachedProp.PropertyName + "Property"; var avaloniaPropertyField = GetType(attachedProp.Namespace, attachedProp.TypeName).GetAllFields().FirstOrDefault(f => f.IsStatic && f.IsPublic && f.Name == avaloniaPropertyFieldName); nodes.Add(new XamlIlAvaloniaPropertyPropertyPathElementNode(avaloniaPropertyField, XamlIlAvaloniaPropertyHelper.GetAvaloniaPropertyType(avaloniaPropertyField, context.GetAvaloniaTypes(), lineInfo))); break; case BindingExpressionGrammar.SelfNode _: nodes.Add(new SelfPathElementNode(selfType)); break; case VisualAncestorBindingExpressionNode visualAncestor: nodes.Add(new FindVisualAncestorPathElementNode(visualAncestor.Type, visualAncestor.Level)); break; case TemplatedParentBindingExpressionNode templatedParent: var templatedParentField = context.GetAvaloniaTypes().StyledElement.GetAllFields() .FirstOrDefault(f => f.IsStatic && f.IsPublic && f.Name == "TemplatedParentProperty"); nodes.Add(new XamlIlAvaloniaPropertyPropertyPathElementNode( templatedParentField, templatedParent.Type)); break; case BindingExpressionGrammar.AncestorNode ancestor: if (ancestor.Namespace is null && ancestor.TypeName is null) { var styledElementType = context.GetAvaloniaTypes().StyledElement; var ancestorType = context .ParentNodes() .OfType <XamlAstConstructableObjectNode>() .Where(x => styledElementType.IsAssignableFrom(x.Type.GetClrType())) .Skip(1) .ElementAtOrDefault(ancestor.Level) ?.Type.GetClrType(); if (ancestorType is null) { throw new XamlX.XamlParseException("Unable to resolve implicit ancestor type based on XAML tree.", lineInfo); } nodes.Add(new FindAncestorPathElementNode(ancestorType, ancestor.Level)); } else { nodes.Add(new FindAncestorPathElementNode(GetType(ancestor.Namespace, ancestor.TypeName), ancestor.Level)); } break;