diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguage.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguage.cs index 22ac094114..71cc9d81b2 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguage.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguage.cs @@ -110,8 +110,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions definition.TypeBuilder.AddInterfaceImplementation(interfaceType); - // IReadOnlyList DirectParents => (IReadOnlyList)ParentsStack; - var directParentsGetter = ImplementInterfacePropertyGetter("DirectParents"); + // IReadOnlyList DirectParentsStack => (IReadOnlyList)ParentsStack; + var directParentsGetter = ImplementInterfacePropertyGetter("DirectParentsStack"); directParentsGetter.Generator .LdThisFld(definition.ParentListField) .Castclass(directParentsGetter.ReturnType) diff --git a/src/Markup/Avalonia.Markup.Xaml/EagerParentStackEnumerator.cs b/src/Markup/Avalonia.Markup.Xaml/EagerParentStackEnumerator.cs index 359cb901ed..fa1c1992e6 100644 --- a/src/Markup/Avalonia.Markup.Xaml/EagerParentStackEnumerator.cs +++ b/src/Markup/Avalonia.Markup.Xaml/EagerParentStackEnumerator.cs @@ -6,7 +6,7 @@ namespace Avalonia.Markup.Xaml; internal struct EagerParentStackEnumerator { private IAvaloniaXamlIlEagerParentStackProvider? _provider; - private IReadOnlyList? _currentParents; + private IReadOnlyList? _currentParentsStack; private int _currentIndex; // only valid when _currentParents isn't null public EagerParentStackEnumerator(IAvaloniaXamlIlEagerParentStackProvider? provider) @@ -16,18 +16,18 @@ internal struct EagerParentStackEnumerator { while (_provider is not null) { - if (_currentParents is null) + if (_currentParentsStack is null) { - _currentParents = _provider.DirectParents; - _currentIndex = _currentParents.Count; + _currentParentsStack = _provider.DirectParentsStack; + _currentIndex = _currentParentsStack.Count; } --_currentIndex; if (_currentIndex >= 0) - return _currentParents[_currentIndex]; + return _currentParentsStack[_currentIndex]; - _currentParents = null; + _currentParentsStack = null; _provider = _provider.ParentProvider; } diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/IAvaloniaXamlIlParentStackProvider.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/IAvaloniaXamlIlParentStackProvider.cs index eef51a7f50..5657cd75b2 100644 --- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/IAvaloniaXamlIlParentStackProvider.cs +++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/IAvaloniaXamlIlParentStackProvider.cs @@ -2,15 +2,36 @@ using System.Collections.Generic; namespace Avalonia.Markup.Xaml.XamlIl.Runtime { + /// + /// Provides the parents for the current XAML node in a lazy way. + /// + /// This interface is used by the XAML compiler and shouldn't be implemented in your code. public interface IAvaloniaXamlIlParentStackProvider { + /// + /// Gets an enumerator iterating over the available parents in the whole hierarchy. + /// The parents are returned in normal order: + /// the first element is the most direct parent while the last element is the most distant ancestor. + /// IEnumerable Parents { get; } } + /// + /// Provides the parents for the current XAML node in an eager way, avoiding allocations when possible. + /// + /// This interface is used by the XAML compiler and shouldn't be implemented in your code. public interface IAvaloniaXamlIlEagerParentStackProvider : IAvaloniaXamlIlParentStackProvider { - IReadOnlyList DirectParents { get; } + /// + /// Gets the directly available parents (which don't include ones returned by parent providers). + /// The parents are returned in reverse order: + /// the last element is the most direct parent while the first element is the most distant ancestor. + /// + IReadOnlyList DirectParentsStack { get; } + /// + /// Gets the parent , if available. + /// IAvaloniaXamlIlEagerParentStackProvider? ParentProvider { get; } } } diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs index 6e48ef2508..79c56cb351 100644 --- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs +++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs @@ -30,7 +30,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.Runtime public static Func DeferredTransformationFactoryV2(Func builder, IServiceProvider provider) { - var resourceNodes = AsResourceNodes(provider.GetRequiredService()); + var resourceNodes = AsResourceNodesStack(provider.GetRequiredService()); var rootObject = provider.GetRequiredService().RootObject; var parentScope = provider.GetService(); @@ -43,7 +43,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.Runtime /*delegate**/ IntPtr builder, IServiceProvider provider) { - var resourceNodes = AsResourceNodes(provider.GetRequiredService()); + var resourceNodes = AsResourceNodesStack(provider.GetRequiredService()); var rootObject = provider.GetRequiredService().RootObject; var parentScope = provider.GetService(); var typedBuilder = (delegate*)builder; @@ -51,7 +51,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.Runtime return new PointerDeferredContent(resourceNodes, rootObject, parentScope, typedBuilder); } - private static IResourceNode[] AsResourceNodes(IAvaloniaXamlIlParentStackProvider provider) + private static IResourceNode[] AsResourceNodesStack(IAvaloniaXamlIlParentStackProvider provider) { var buffer = s_resourceNodeBuffer ??= new List(8); buffer.Clear(); @@ -72,6 +72,9 @@ namespace Avalonia.Markup.Xaml.XamlIl.Runtime } } + // The immediate parent should be last in the stack. + buffer.Reverse(); + var lastParentStack = s_lastParentStack; if (lastParentStack is null @@ -230,9 +233,9 @@ namespace Avalonia.Markup.Xaml.XamlIl.Runtime => RootObject; public IEnumerable Parents - => _parentResourceNodes; + => _parentResourceNodes.Reverse(); - public IReadOnlyList DirectParents + public IReadOnlyList DirectParentsStack => _parentResourceNodes; public IAvaloniaXamlIlEagerParentStackProvider? ParentProvider @@ -418,7 +421,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.Runtime public IEnumerable Parents => _parents ??= new object[] { _application }; - public IReadOnlyList DirectParents + public IReadOnlyList DirectParentsStack => this; public int Count @@ -462,7 +465,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.Runtime public IEnumerable Parents => Array.Empty(); - public IReadOnlyList DirectParents + public IReadOnlyList DirectParentsStack => Array.Empty(); public IAvaloniaXamlIlEagerParentStackProvider? ParentProvider diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/StaticResourceExtensionTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/StaticResourceExtensionTests.cs index 88d281c3b3..c48978a516 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/StaticResourceExtensionTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/StaticResourceExtensionTests.cs @@ -340,7 +340,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions