From f14aac365bf6bc12b5d7133c22ce0ddd839fc0f2 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Fri, 15 Feb 2019 14:48:10 +0300 Subject: [PATCH 01/73] WIP --- .gitmodules | 3 + .../Pages/ButtonSpinnerPage.xaml.cs | 2 +- .../UsableDuringInitializationAttribute.cs | 10 ++ .../Controls/NameScopeExtensions.cs | 3 +- src/Avalonia.Visuals/Visual.cs | 2 + .../Avalonia.Markup.Xaml.csproj | 18 ++ .../AvaloniaTypeConverters.cs | 1 + .../AvaloniaXamlLoader.cs | 15 +- .../AvaloniaPropertyTypeConverter.cs | 4 +- .../Converters/BitmapTypeConverter.cs | 4 +- .../Converters/FontFamilyTypeConverter.cs | 2 +- .../Converters/IconTypeConverter.cs | 2 +- src/Markup/Avalonia.Markup.Xaml/Extensions.cs | 47 +++++ .../MarkupExtensions/BindingExtension.cs | 11 +- .../DynamicResourceExtension.cs | 16 +- .../MarkupExtensions/ResourceInclude.cs | 2 +- .../MarkupExtensions/StyleIncludeExtension.cs | 5 +- .../PortableXaml/TypeDescriptorExtensions.cs | 4 +- .../XamlIl/AvaloniaXamlIlRuntimeCompiler.cs | 160 ++++++++++++++++++ .../AvaloniaPropertyDescriptorEmitter.cs | 21 +++ .../AvaloniaXamlIlCompiler.cs | 61 +++++++ .../AvaloniaXamlIlLanguage.cs | 153 +++++++++++++++++ .../XamlIl/CompilerExtensions/README.md | 4 + .../Transformers/AddNameScopeRegistration.cs | 79 +++++++++ .../IgnoredDirectivesTransformer.cs | 27 +++ .../KnownPseudoMarkupExtensionsTransformer.cs | 26 +++ .../Transformers/XNameTransformer.cs | 33 ++++ .../IAvaloniaXamlIlParentStackProvider.cs | 9 + ...valoniaXamlIlXmlNamespaceInfoProviderV1.cs | 15 ++ .../XamlIl/Runtime/XamlIlRuntimeHelpers.cs | 121 +++++++++++++ .../Avalonia.Markup.Xaml/XamlIl/xamlil.github | 1 + 31 files changed, 827 insertions(+), 34 deletions(-) create mode 100644 src/Avalonia.Base/Metadata/UsableDuringInitializationAttribute.cs create mode 100644 src/Markup/Avalonia.Markup.Xaml/Extensions.cs create mode 100644 src/Markup/Avalonia.Markup.Xaml/XamlIl/AvaloniaXamlIlRuntimeCompiler.cs create mode 100644 src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaPropertyDescriptorEmitter.cs create mode 100644 src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlCompiler.cs create mode 100644 src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlLanguage.cs create mode 100644 src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/README.md create mode 100644 src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AddNameScopeRegistration.cs create mode 100644 src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/IgnoredDirectivesTransformer.cs create mode 100644 src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/KnownPseudoMarkupExtensionsTransformer.cs create mode 100644 src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/XNameTransformer.cs create mode 100644 src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/IAvaloniaXamlIlParentStackProvider.cs create mode 100644 src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/IAvaloniaXamlIlXmlNamespaceInfoProviderV1.cs create mode 100644 src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs create mode 160000 src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github diff --git a/.gitmodules b/.gitmodules index 22c56307b0..22a241f120 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "nukebuild/Numerge"] path = nukebuild/Numerge url = https://github.com/kekekeks/Numerge.git +[submodule "src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github"] + path = src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github + url = https://github.com/kekekeks/XamlIl.git diff --git a/samples/ControlCatalog/Pages/ButtonSpinnerPage.xaml.cs b/samples/ControlCatalog/Pages/ButtonSpinnerPage.xaml.cs index 1f753ab3ea..c5042baccb 100644 --- a/samples/ControlCatalog/Pages/ButtonSpinnerPage.xaml.cs +++ b/samples/ControlCatalog/Pages/ButtonSpinnerPage.xaml.cs @@ -18,7 +18,7 @@ namespace ControlCatalog.Pages AvaloniaXamlLoader.Load(this); } - private void OnSpin(object sender, SpinEventArgs e) + public void OnSpin(object sender, SpinEventArgs e) { var spinner = (ButtonSpinner)sender; var txtBox = (TextBlock)spinner.Content; diff --git a/src/Avalonia.Base/Metadata/UsableDuringInitializationAttribute.cs b/src/Avalonia.Base/Metadata/UsableDuringInitializationAttribute.cs new file mode 100644 index 0000000000..753a96b9ce --- /dev/null +++ b/src/Avalonia.Base/Metadata/UsableDuringInitializationAttribute.cs @@ -0,0 +1,10 @@ +using System; + +namespace Avalonia.Metadata +{ + [AttributeUsage(AttributeTargets.Class)] + public class UsableDuringInitializationAttribute : Attribute + { + + } +} diff --git a/src/Avalonia.Styling/Controls/NameScopeExtensions.cs b/src/Avalonia.Styling/Controls/NameScopeExtensions.cs index 491e4d71a7..991a97a614 100644 --- a/src/Avalonia.Styling/Controls/NameScopeExtensions.cs +++ b/src/Avalonia.Styling/Controls/NameScopeExtensions.cs @@ -71,10 +71,11 @@ namespace Avalonia.Controls { Contract.Requires(control != null); - return control.GetSelfAndLogicalAncestors() + var scope = control.GetSelfAndLogicalAncestors() .OfType() .Select(x => (x as INameScope) ?? NameScope.GetNameScope(x)) .FirstOrDefault(x => x != null); + return scope; } } } diff --git a/src/Avalonia.Visuals/Visual.cs b/src/Avalonia.Visuals/Visual.cs index f26c21d1b6..3360ba84c1 100644 --- a/src/Avalonia.Visuals/Visual.cs +++ b/src/Avalonia.Visuals/Visual.cs @@ -9,6 +9,7 @@ using Avalonia.Collections; using Avalonia.Data; using Avalonia.Logging; using Avalonia.Media; +using Avalonia.Metadata; using Avalonia.Rendering; using Avalonia.VisualTree; @@ -23,6 +24,7 @@ namespace Avalonia /// to render the control. To traverse the visual tree, use the /// extension methods defined in . /// + [UsableDuringInitialization] public class Visual : StyledElement, IVisual { /// diff --git a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj index 38d207a31d..4603d31fae 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj +++ b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj @@ -15,6 +15,7 @@ + @@ -50,8 +51,21 @@ + + + + + + + + + + + + + @@ -63,6 +77,10 @@ + + + + diff --git a/src/Markup/Avalonia.Markup.Xaml/AvaloniaTypeConverters.cs b/src/Markup/Avalonia.Markup.Xaml/AvaloniaTypeConverters.cs index c30822aacb..2b6572e1a2 100644 --- a/src/Markup/Avalonia.Markup.Xaml/AvaloniaTypeConverters.cs +++ b/src/Markup/Avalonia.Markup.Xaml/AvaloniaTypeConverters.cs @@ -29,6 +29,7 @@ namespace Avalonia.Markup.Xaml /// public static class AvaloniaTypeConverters { + // When adding item to that list make sure to modify AvaloniaXamlIlLanguage private static Dictionary _converters = new Dictionary() { { typeof(AvaloniaList<>), typeof(AvaloniaListConverter<>) }, diff --git a/src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs b/src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs index a825deeae3..dd413ee10f 100644 --- a/src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs +++ b/src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs @@ -13,6 +13,7 @@ using System.Reflection; using System.Runtime.Serialization; using System.Runtime.Serialization.Json; using System.Text; +using Avalonia.Markup.Xaml.XamlIl; namespace Avalonia.Markup.Xaml { @@ -23,6 +24,8 @@ namespace Avalonia.Markup.Xaml { private readonly AvaloniaXamlSchemaContext _context = GetContext(); + public bool EnforceCompilerForRuntimeXaml { get; set; } = true; + public bool IsDesignMode { get => _context.IsDesignMode; @@ -137,13 +140,13 @@ namespace Avalonia.Markup.Xaml using (var stream = asset.stream) { var absoluteUri = uri.IsAbsoluteUri ? uri : new Uri(baseUri, uri); - try + //try { return Load(stream, asset.assembly, rootInstance, absoluteUri); } - catch (Exception e) + //catch (Exception e) { - throw new XamlLoadException("Error loading xaml at " + absoluteUri + ": " + e.Message, e); + //throw new XamlLoadException("Error loading xaml at " + absoluteUri + ": " + e.Message, e); } } } @@ -179,6 +182,12 @@ namespace Avalonia.Markup.Xaml /// The loaded object. public object Load(Stream stream, Assembly localAssembly, object rootInstance = null, Uri uri = null) { + if (EnforceCompilerForRuntimeXaml) + { + return AvaloniaXamlIlRuntimeCompiler.Load(stream, localAssembly, rootInstance, uri); + } + + var readerSettings = new XamlXmlReaderSettings() { BaseUri = uri, diff --git a/src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaPropertyTypeConverter.cs b/src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaPropertyTypeConverter.cs index 8a0ba64582..18a7fe9ab6 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaPropertyTypeConverter.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaPropertyTypeConverter.cs @@ -28,8 +28,8 @@ namespace Avalonia.Markup.Xaml.Converters var parser = new PropertyParser(); var (ns, owner, propertyName) = parser.Parse(new CharacterReader(((string)value).AsSpan())); var ownerType = TryResolveOwnerByName(context, ns, owner); - var targetType = context.GetFirstAmbientValue()?.TargetType ?? - context.GetFirstAmbientValue"; + var xaml = ""; var loader = new AvaloniaXamlLoader(); var style = (Style)loader.Load(xaml); var setter = (Setter)(style.Setters.First()); diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs index d2247e3d02..359d2521e0 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs @@ -162,6 +162,10 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml [Fact] public void Non_Attached_Property_With_Attached_Property_Syntax_Throws() { + // 1) It has been allowed in AvaloniaObject.SetValue for ages + // 2) There is no way to know if AddOwner was called in compile-time + if (!AvaloniaXamlLoader.UseLegacyXamlLoader) + return; var xaml = @""; From c021bf717dc4d3d055857a3bf0f96d5f3895e1f5 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Mon, 22 Apr 2019 17:21:25 +0300 Subject: [PATCH 40/73] Disable Xamarin projects on CI --- dirs.proj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dirs.proj b/dirs.proj index 03fa51b3c4..b78b38d51d 100644 --- a/dirs.proj +++ b/dirs.proj @@ -8,6 +8,7 @@ + From 223e929ca37f64ffc6afee0f7ca0ba6a5e6b54b8 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Mon, 22 Apr 2019 18:04:02 +0300 Subject: [PATCH 41/73] Update dirs.proj --- dirs.proj | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/dirs.proj b/dirs.proj index b78b38d51d..4939a158bb 100644 --- a/dirs.proj +++ b/dirs.proj @@ -8,16 +8,13 @@ - + - - ---> From 8a877edce01b9cf9d3e5b9419f2fce8023e3a066 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Mon, 22 Apr 2019 21:37:33 +0300 Subject: [PATCH 42/73] Why was it here in the first place? --- src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj index 610d489d34..35d34d83a8 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj +++ b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj @@ -92,8 +92,5 @@ - - - From 63b7815fc06b2d87e1951ab898cd6c224c9ca211 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Tue, 23 Apr 2019 08:16:30 +0300 Subject: [PATCH 43/73] Don't overwrite dll with pdb --- src/Avalonia.Build.Tasks/CompileAvaloniaXamlTask.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Build.Tasks/CompileAvaloniaXamlTask.cs b/src/Avalonia.Build.Tasks/CompileAvaloniaXamlTask.cs index 97f7af86b9..61303dbbfe 100644 --- a/src/Avalonia.Build.Tasks/CompileAvaloniaXamlTask.cs +++ b/src/Avalonia.Build.Tasks/CompileAvaloniaXamlTask.cs @@ -41,7 +41,7 @@ namespace Avalonia.Build.Tasks { File.Copy(input, OutputPath, true); if(File.Exists(inputPdb)) - File.Copy(inputPdb, OutputPath, true); + File.Copy(inputPdb, outputPdb, true); } return true; } From d52afe7a96e732a0f12b22e64d1acf3ac2cd0eba Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Tue, 23 Apr 2019 09:48:51 +0300 Subject: [PATCH 44/73] Fixed Avalonia.Diagnostics --- build/EmbedXaml.props | 6 +++--- samples/ControlCatalog/App.xaml | 2 +- .../Avalonia.Controls.DataGrid.csproj | 2 ++ src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj | 1 + src/Avalonia.Diagnostics/DevTools.xaml | 4 +++- src/Avalonia.Diagnostics/DevTools.xaml.cs | 6 ++++++ src/Avalonia.Diagnostics/Views/EventsView.xaml | 3 ++- src/Avalonia.Diagnostics/Views/TreePageView.xaml | 4 +++- src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj | 5 +++-- 9 files changed, 24 insertions(+), 9 deletions(-) diff --git a/build/EmbedXaml.props b/build/EmbedXaml.props index 219ffb2e42..dba14521ab 100644 --- a/build/EmbedXaml.props +++ b/build/EmbedXaml.props @@ -4,8 +4,8 @@ %(Filename) - + Designer - + - \ No newline at end of file + diff --git a/samples/ControlCatalog/App.xaml b/samples/ControlCatalog/App.xaml index d20e0100a0..2f6d25c089 100644 --- a/samples/ControlCatalog/App.xaml +++ b/samples/ControlCatalog/App.xaml @@ -4,7 +4,7 @@ - + \ No newline at end of file + diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/Style2.xaml b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/Style2.xaml index fd93c4a468..04db8fd2dd 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/Style2.xaml +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/Style2.xaml @@ -1,8 +1,9 @@ \ No newline at end of file + diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlClassWithPrecompiledXaml.xaml b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlClassWithPrecompiledXaml.xaml new file mode 100644 index 0000000000..ac2f75b893 --- /dev/null +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlClassWithPrecompiledXaml.xaml @@ -0,0 +1,6 @@ + + + diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/XamlIlBugTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs similarity index 66% rename from tests/Avalonia.Markup.Xaml.UnitTests/XamlIlBugTests.cs rename to tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs index 839cd07755..f33054e5ce 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/XamlIlBugTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs @@ -7,7 +7,7 @@ using Xunit; namespace Avalonia.Markup.Xaml.UnitTests { - public class XamlIlBugTests + public class XamlIlTests { [Fact] public void Binding_Button_IsPressed_ShouldWork() @@ -34,7 +34,24 @@ namespace Avalonia.Markup.Xaml.UnitTests Assert.Equal(1, parsed.Transitions.Count); Assert.Equal(Visual.OpacityProperty, parsed.Transitions[0].Property); } - + + [Fact] + public void Parser_Should_Override_Precompiled_Xaml() + { + var precompiled = new XamlIlClassWithPrecompiledXaml(); + Assert.Equal(Brushes.Red, precompiled.Background); + Assert.Equal(1, precompiled.Opacity); + var loaded = (XamlIlClassWithPrecompiledXaml)AvaloniaXamlLoader.Parse(@" + + +"); + Assert.Equal(loaded.Opacity, 0); + Assert.Null(loaded.Background); + + } } @@ -50,4 +67,8 @@ namespace Avalonia.Markup.Xaml.UnitTests } } + public class XamlIlClassWithPrecompiledXaml : UserControl + { + } + } From cfeeb5ed3fb4023b42b7dcc76e908b19f6e8e0d8 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Sun, 5 May 2019 21:48:46 +0300 Subject: [PATCH 59/73] Updated XamlIl. Now even more static typing --- .../Avalonia.Markup.Xaml.csproj | 1 - .../MarkupExtensions/BindingExtension.cs | 2 +- .../AvaloniaXamlIlCompiler.cs | 3 +- .../AvaloniaXamlIlLanguage.cs | 16 --- .../Transformers/AddNameScopeRegistration.cs | 66 ++++++---- .../AvaloniaXamlIlAvaloniaPropertyResolver.cs | 8 +- ...IlBindingPropertyAssignmentsTransformer.cs | 114 ------------------ .../AvaloniaXamlIlSelectorTransformer.cs | 2 +- .../AvaloniaXamlIlSetterTransformer.cs | 51 ++++++-- ...mlIlTransformInstanceAttachedProperties.cs | 88 ++++++-------- ...amlIlTransitionsTypeMetadataTransformer.cs | 2 +- .../AvaloniaXamlIlWellKnownTypes.cs | 7 ++ .../XamlIlAvaloniaPropertyHelper.cs | 113 ++++++++++++++--- .../Avalonia.Markup.Xaml/XamlIl/xamlil.github | 2 +- 14 files changed, 236 insertions(+), 239 deletions(-) delete mode 100644 src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPropertyAssignmentsTransformer.cs diff --git a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj index 4efe500bb7..fb51942120 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj +++ b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj @@ -63,7 +63,6 @@ - diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/BindingExtension.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/BindingExtension.cs index e0adbcdbbc..223716ae3b 100644 --- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/BindingExtension.cs +++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/BindingExtension.cs @@ -33,7 +33,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions return ProvideTypedValue(serviceProvider); } - public IBinding ProvideTypedValue(IServiceProvider serviceProvider) + public Binding ProvideTypedValue(IServiceProvider serviceProvider) { var descriptorContext = (ITypeDescriptorContext)serviceProvider; diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlCompiler.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlCompiler.cs index 1d7d93596e..facac748c3 100644 --- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlCompiler.cs +++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlCompiler.cs @@ -39,10 +39,9 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions InsertBefore(new AvaloniaXamlIlTransformInstanceAttachedProperties()); InsertAfter(new AvaloniaXamlIlAvaloniaPropertyResolver()); - InsertBefore(new AvaloniaXamlIlBindingPropertyAssignmentsTransformer()); - InsertBefore( + InsertBefore( new AvaloniaXamlIlSelectorTransformer(), new AvaloniaXamlIlSetterTransformer(), new AvaloniaXamlIlControlTemplateTargetTypeMetadataTransformer(), diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlLanguage.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlLanguage.cs index 0de9e7942b..581dbcdac7 100644 --- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlLanguage.cs +++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlLanguage.cs @@ -50,24 +50,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions typeSystem.GetType("Portable.Xaml.Markup.UsableDuringInitializationAttribute"), typeSystem.GetType("Avalonia.Metadata.UsableDuringInitializationAttribute"), }, - MarkupExtensionCustomResultTypes = - { - bindingType, - typeSystem.GetType("Avalonia.UnsetValueType") - }, - MarkupExtensionCustomResultHandler = - runtimeHelpers.FindMethod(m => m.Name == "ApplyNonMatchingMarkupExtensionV1"), InnerServiceProviderFactoryMethod = runtimeHelpers.FindMethod(m => m.Name == "CreateInnerServiceProviderV1"), - ShouldIgnoreMarkupExtensionCustomResultForProperty = (prop, customType) => - { - if (prop.Name == "Value" && prop.Setter?.DeclaringType.FullName == "Avalonia.Styling.Setter") - return true; - if (customType.Equals(bindingType) && - prop.CustomAttributes.Any(a => a.Type.Equals(assignBindingAttribute))) - return true; - return false; - }, ProvideValueTargetPropertyEmitter = XamlIlAvaloniaPropertyHelper.Emit, }; rv.CustomAttributeResolver = new AttributeResolver(typeSystem, rv); diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AddNameScopeRegistration.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AddNameScopeRegistration.cs index c400995fd1..f9bc440b6b 100644 --- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AddNameScopeRegistration.cs +++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AddNameScopeRegistration.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using XamlIl.Ast; using XamlIl.Transform; using XamlIl.TypeSystem; @@ -11,21 +12,50 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers { if (node is XamlIlPropertyAssignmentNode pa && pa.Property.Name == "Name" - && pa.Property.Setter.DeclaringType.FullName == "Avalonia.StyledElement") - return new ScopeRegistrationNode(pa); + && pa.Property.DeclaringType.FullName == "Avalonia.StyledElement") + { + if (context.ParentNodes().FirstOrDefault() is XamlIlManipulationGroupNode mg + && mg.Children.OfType().Any()) + return node; + + IXamlIlAstValueNode value = null; + for (var c = 0; c < pa.Values.Count; c++) + if (pa.Values[c].Type.GetClrType().Equals(context.Configuration.WellKnownTypes.String)) + { + value = pa.Values[c]; + if (!(value is XamlIlAstTextNode)) + { + var local = new XamlIlAstCompilerLocalNode(value); + // Wrap original in local initialization + pa.Values[c] = new XamlIlAstLocalInitializationNodeEmitter(value, value, local); + // Use local + value = local; + } + + break; + } + + if (value != null) + return new XamlIlManipulationGroupNode(pa) + { + Children = + { + pa, + new ScopeRegistrationNode(value) + } + }; + } return node; } class ScopeRegistrationNode : XamlIlAstNode, IXamlIlAstManipulationNode, IXamlIlAstEmitableNode { + private readonly IXamlIlType _targetType; public IXamlIlAstValueNode Value { get; set; } - private IXamlIlProperty _property; - - public ScopeRegistrationNode(XamlIlPropertyAssignmentNode pa) : base(pa) + public ScopeRegistrationNode(IXamlIlAstValueNode value) : base(value) { - _property = pa.Property; - Value = pa.Value; + Value = value; } public override void VisitChildren(IXamlIlAstVisitor visitor) @@ -36,24 +66,13 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers var exts = context.Configuration.TypeSystem.GetType("Avalonia.Controls.NameScopeExtensions"); var findNameScope = exts.FindMethod(m => m.Name == "FindNameScope"); var registerMethod = findNameScope.ReturnType.FindMethod(m => m.Name == "Register"); - using (var nameLoc = context.GetLocal(context.Configuration.WellKnownTypes.String)) - using (var targetLoc = context.GetLocal(_property.Setter.DeclaringType)) + using (var targetLoc = context.GetLocal(context.Configuration.WellKnownTypes.Object)) using (var nameScopeLoc = context.GetLocal(findNameScope.ReturnType)) { var exit = codeGen.DefineLabel(); - - // var target = {target} codeGen - .Castclass(_property.Setter.DeclaringType) - .Stloc(targetLoc.Local); - - // var name = {EmitName()} - context.Emit(Value, codeGen, _property.PropertyType); - codeGen.Stloc(nameLoc.Local) - // target.Name = name - .Ldloc(targetLoc.Local) - .Ldloc(nameLoc.Local) - .EmitCall(_property.Setter) + // var target = {pop} + .Stloc(targetLoc.Local) // var scope = target.FindNameScope() .Ldloc(targetLoc.Local) .Castclass(findNameScope.Parameters[0]) @@ -62,8 +81,9 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers // if({scope} != null) goto call; .Ldloc(nameScopeLoc.Local) .Brfalse(exit) - .Ldloc(nameScopeLoc.Local) - .Ldloc(nameLoc.Local) + .Ldloc(nameScopeLoc.Local); + context.Emit(Value, codeGen, Value.Type.GetClrType()); + codeGen .Ldloc(targetLoc.Local) .EmitCall(registerMethod) .MarkLabel(exit); diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlAvaloniaPropertyResolver.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlAvaloniaPropertyResolver.cs index ed17e59e14..f95ad132ad 100644 --- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlAvaloniaPropertyResolver.cs +++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlAvaloniaPropertyResolver.cs @@ -8,14 +8,14 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers { public IXamlIlAstNode Transform(XamlIlAstTransformationContext context, IXamlIlAstNode node) { - if (node is XamlIlAstClrPropertyReference prop) + if (node is XamlIlAstClrProperty prop) { - var n = prop.Property.Name + "Property"; + var n = prop.Name + "Property"; var field = - (prop.Property.Getter ?? prop.Property.Setter).DeclaringType.Fields + prop.DeclaringType.Fields .FirstOrDefault(f => f.Name == n); if (field != null) - prop.Property = new XamlIlAvaloniaProperty(prop.Property, field); + return new XamlIlAvaloniaProperty(prop, field, context.GetAvaloniaTypes()); } return node; diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPropertyAssignmentsTransformer.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPropertyAssignmentsTransformer.cs deleted file mode 100644 index 17b057a745..0000000000 --- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPropertyAssignmentsTransformer.cs +++ /dev/null @@ -1,114 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using XamlIl.Ast; -using XamlIl.Transform; -using XamlIl.TypeSystem; - -namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers -{ - public class AvaloniaXamlIlBindingPropertyAssignmentsTransformer : IXamlIlAstTransformer - { - public IXamlIlAstNode Transform(XamlIlAstTransformationContext context, IXamlIlAstNode node) - { - if (node is XamlIlAstXamlPropertyValueNode pv - && pv.Property.GetClrProperty() is IXamlIlAvaloniaProperty ap - && pv.Values.Count == 1) - { - var types = context.GetAvaloniaTypes(); - - var vn = pv.Values[0]; - if (vn.Type.GetClrType().Equals(XamlIlPseudoType.Null)) - return node; - - if (pv.Property.GetClrProperty().CustomAttributes - .Any(a => a.Type.Equals(types.AssignBindingAttribute))) - return node; - - // Special handling for markup extensions - if (vn.Type.IsMarkupExtension) - { - if (XamlIlTransformHelpers - .GetMarkupExtensionProvideValueAlternatives(context, vn.Type.GetClrType()) - .Any(x => types.IBinding.IsAssignableFrom(x.ReturnType))) - { - if (XamlIlTransformHelpers.TryConvertMarkupExtension(context, pv.Values[0], - new AssignBindingProperty(types, ap), out var ext)) - return ext; - } - } - else if (types.IBinding.IsAssignableFrom(vn.Type.GetClrType())) - { - pv.Property = new XamlIlAstClrPropertyReference(pv.Property, new AssignBindingProperty(types, ap)); - } - } - - return node; - } - - class AssignBindingProperty : IXamlIlAvaloniaProperty - { - public AssignBindingProperty( - AvaloniaXamlIlWellKnownTypes types, - IXamlIlAvaloniaProperty property - ) - { - PropertyType = types.IBinding; - AvaloniaProperty = property.AvaloniaProperty; - CustomAttributes = property.CustomAttributes; - Name = property.Name; - Setter = new SetterMethod(types, (property.Setter ?? property.Getter).DeclaringType, - AvaloniaProperty); - } - - public bool Equals(IXamlIlProperty other) => - other is AssignBindingProperty abp && abp.AvaloniaProperty.Equals(AvaloniaProperty); - - public string Name { get; } - public IXamlIlType PropertyType { get; } - public IXamlIlMethod Setter { get; } - public IXamlIlMethod Getter { get; } - public IReadOnlyList CustomAttributes { get; } - public IXamlIlField AvaloniaProperty { get; } - - class SetterMethod : IXamlIlCustomEmitMethod - { - private readonly AvaloniaXamlIlWellKnownTypes _types; - private readonly IXamlIlField _avaloniaProperty; - - public SetterMethod(AvaloniaXamlIlWellKnownTypes types, - IXamlIlType declaringType, - IXamlIlField avaloniaProperty) - { - _types = types; - _avaloniaProperty = avaloniaProperty; - Parameters = new[] {types.AvaloniaObject, types.IBinding}; - ReturnType = types.XamlIlTypes.Void; - DeclaringType = declaringType; - Name = "Bind_" + avaloniaProperty.Name; - } - - public bool Equals(IXamlIlMethod other) => - other is SetterMethod sm && sm._avaloniaProperty.Equals(_avaloniaProperty); - - public string Name { get; } - public bool IsPublic => true; - public bool IsStatic => true; - public IXamlIlType ReturnType { get; } - public IReadOnlyList Parameters { get; } - public IXamlIlType DeclaringType { get; } - public void EmitCall(IXamlIlEmitter emitter) - { - using (var bloc = emitter.LocalsPool.GetLocal(_types.IBinding)) - emitter - .Stloc(bloc.Local) - .Ldsfld(_avaloniaProperty) - .Ldloc(bloc.Local) - // TODO: provide anchor? - .Ldnull(); - emitter.EmitCall(_types.AvaloniaObjectBindMethod, true); - } - } - } - } -} diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlSelectorTransformer.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlSelectorTransformer.cs index 11df8bfa82..a7f7603012 100644 --- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlSelectorTransformer.cs +++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlSelectorTransformer.cs @@ -34,7 +34,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers if (!(pn.Values[0] is XamlIlAstTextNode tn)) throw new XamlIlParseException("Selector property should be a text node", node); - var selectorType = pn.Property.GetClrProperty().PropertyType; + var selectorType = pn.Property.GetClrProperty().Getter.ReturnType; var initialNode = new XamlIlSelectorInitialNode(node, selectorType); XamlIlSelectorNode Create(IEnumerable syntax, Func typeResolver) diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTransformer.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTransformer.cs index 28a8675903..0e7ea34bb7 100644 --- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTransformer.cs +++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTransformer.cs @@ -53,21 +53,56 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers .OfType().FirstOrDefault(p => p.Property.GetClrProperty().Name == "Value"); if (valueProperty?.Values?.Count == 1 && valueProperty.Values[0] is XamlIlAstTextNode) { + var propType = avaloniaPropertyNode.Property.Getter?.ReturnType + ?? avaloniaPropertyNode.Property.Setters.First().Parameters[0]; if (!XamlIlTransformHelpers.TryGetCorrectlyTypedValue(context, valueProperty.Values[0], - avaloniaPropertyNode.Property.PropertyType, out var converted)) + propType, out var converted)) throw new XamlIlParseException( - $"Unable to convert property value to {avaloniaPropertyNode.Property.PropertyType.GetFqn()}", + $"Unable to convert property value to {propType.GetFqn()}", valueProperty.Values[0]); - valueProperty.Values = new List - { - new XamlIlAstRuntimeCastNode(converted, converted, - new XamlIlAstClrTypeReference(converted, context.Configuration.WellKnownTypes.Object, false)) - }; + valueProperty.Property = new SetterValueProperty(valueProperty.Property, + on.Type.GetClrType(), propType, context.GetAvaloniaTypes()); } return node; } - + + class SetterValueProperty : XamlIlAstClrProperty + { + public SetterValueProperty(IXamlIlLineInfo line, IXamlIlType setterType, IXamlIlType targetType, + AvaloniaXamlIlWellKnownTypes types) + : base(line, "Value", setterType, null) + { + Getter = setterType.Methods.First(m => m.Name == "get_Value"); + var method = setterType.Methods.First(m => m.Name == "set_Value"); + Setters.Add(new XamlIlDirectCallPropertySetter(method, types.IBinding)); + Setters.Add(new XamlIlDirectCallPropertySetter(method, types.UnsetValueType)); + Setters.Add(new XamlIlDirectCallPropertySetter(method, targetType)); + } + + class XamlIlDirectCallPropertySetter : IXamlIlPropertySetter + { + private readonly IXamlIlMethod _method; + private readonly IXamlIlType _type; + public IXamlIlType TargetType { get; } + public PropertySetterBinderParameters BinderParameters { get; } = new PropertySetterBinderParameters(); + public IReadOnlyList Parameters { get; } + public void Emit(IXamlIlEmitter codegen) + { + if (_type.IsValueType) + codegen.Box(_type); + codegen.EmitCall(_method, true); + } + + public XamlIlDirectCallPropertySetter(IXamlIlMethod method, IXamlIlType type) + { + _method = method; + _type = type; + Parameters = new[] {type}; + TargetType = method.ThisOrFirstParameter(); + } + } + } } } diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlTransformInstanceAttachedProperties.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlTransformInstanceAttachedProperties.cs index 1b3b1d7047..bbacef43dd 100644 --- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlTransformInstanceAttachedProperties.cs +++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlTransformInstanceAttachedProperties.cs @@ -56,10 +56,9 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers avaloniaPropertyType.GenericArguments[0] : context.Configuration.WellKnownTypes.Object; - return new XamlIlAstClrPropertyReference(prop, - new AvaloniaAttachedInstanceProperty(prop.Name, context.Configuration, + return new AvaloniaAttachedInstanceProperty(prop, context.Configuration, declaringType, propertyType, avaloniaPropertyType, avaloniaObject, - avaloniaPropertyField)); + avaloniaPropertyField); } } @@ -71,7 +70,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers return node; } - class AvaloniaAttachedInstanceProperty : IXamlIlAvaloniaProperty + class AvaloniaAttachedInstanceProperty : XamlIlAstClrProperty, IXamlIlAvaloniaProperty { private readonly XamlIlTransformerConfiguration _config; private readonly IXamlIlType _declaringType; @@ -79,13 +78,16 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers private readonly IXamlIlType _avaloniaObject; private readonly IXamlIlField _field; - public AvaloniaAttachedInstanceProperty(string name, + public AvaloniaAttachedInstanceProperty(XamlIlAstNamePropertyReference prop, XamlIlTransformerConfiguration config, IXamlIlType declaringType, IXamlIlType type, IXamlIlType avaloniaPropertyType, IXamlIlType avaloniaObject, - IXamlIlField field) + IXamlIlField field) : base(prop, prop.Name, + declaringType, null) + + { _config = config; _declaringType = declaringType; @@ -97,72 +99,50 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers _avaloniaObject = avaloniaObject; _field = field; - Name = name; PropertyType = type; - Setter = new SetterMethod(this); + Setters.Add(new SetterMethod(this)); Getter = new GetterMethod(this); } - public IXamlIlField AvaloniaProperty => _field; - public bool Equals(IXamlIlProperty other) => - other is AvaloniaAttachedInstanceProperty ap && ap._field.Equals(_field); - - public string Name { get; } - public IXamlIlType PropertyType { get; } - public IXamlIlMethod Setter { get; } - public IXamlIlMethod Getter { get; } - public IReadOnlyList CustomAttributes { get; } = new List(); - - class Method - { - public AvaloniaAttachedInstanceProperty Parent { get; } - public bool IsPublic => true; - public bool IsStatic => true; - public string Name { get; protected set; } - public IXamlIlType DeclaringType { get; } - public Method(AvaloniaAttachedInstanceProperty parent) - { - Parent = parent; - DeclaringType = parent._declaringType; - } + public IXamlIlType PropertyType { get; } - public bool Equals(IXamlIlMethod other) => - other is Method m && m.Name == Name && m.DeclaringType.Equals(DeclaringType); - } + public IXamlIlField AvaloniaProperty => _field; - class SetterMethod : Method, IXamlIlCustomEmitMethod + class SetterMethod : IXamlIlPropertySetter { - public SetterMethod(AvaloniaAttachedInstanceProperty parent) : base(parent) + private readonly AvaloniaAttachedInstanceProperty _parent; + + public SetterMethod(AvaloniaAttachedInstanceProperty parent) { - Name = "AvaloniaObject:SetValue_" + Parent.Name; - Parameters = new[] {Parent._avaloniaObject, Parent.PropertyType}; + _parent = parent; + Parameters = new[] {_parent._avaloniaObject, _parent.PropertyType}; } - public IXamlIlType ReturnType => Parent._config.WellKnownTypes.Void; + public IXamlIlType TargetType => _parent.DeclaringType; + public PropertySetterBinderParameters BinderParameters { get; } = new PropertySetterBinderParameters(); public IReadOnlyList Parameters { get; } - - public void EmitCall(IXamlIlEmitter emitter) + public void Emit(IXamlIlEmitter emitter) { - var so = Parent._config.WellKnownTypes.Object; - var method = Parent._avaloniaObject + var so = _parent._config.WellKnownTypes.Object; + var method = _parent._avaloniaObject .FindMethod(m => m.IsPublic && !m.IsStatic && m.Name == "SetValue" && m.Parameters.Count == 3 - && m.Parameters[0].Equals(Parent._avaloniaPropertyType) + && m.Parameters[0].Equals(_parent._avaloniaPropertyType) && m.Parameters[1].Equals(so) && m.Parameters[2].IsEnum ); if (method == null) throw new XamlIlTypeSystemException( "Unable to find SetValue(AvaloniaProperty, object, BindingPriority) on AvaloniaObject"); - using (var loc = emitter.LocalsPool.GetLocal(Parent.PropertyType)) + using (var loc = emitter.LocalsPool.GetLocal(_parent.PropertyType)) emitter .Stloc(loc.Local) - .Ldsfld(Parent._field) + .Ldsfld(_parent._field) .Ldloc(loc.Local); - if(Parent.PropertyType.IsValueType) - emitter.Box(Parent.PropertyType); + if(_parent.PropertyType.IsValueType) + emitter.Box(_parent.PropertyType); emitter .Ldc_I4(0) .EmitCall(method); @@ -170,14 +150,24 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers } } - class GetterMethod : Method, IXamlIlCustomEmitMethod + class GetterMethod : IXamlIlCustomEmitMethod { - public GetterMethod(AvaloniaAttachedInstanceProperty parent) : base(parent) + public GetterMethod(AvaloniaAttachedInstanceProperty parent) { + Parent = parent; + DeclaringType = parent._declaringType; Name = "AvaloniaObject:GetValue_" + Parent.Name; Parameters = new[] {parent._avaloniaObject}; } + public AvaloniaAttachedInstanceProperty Parent { get; } + public bool IsPublic => true; + public bool IsStatic => true; + public string Name { get; protected set; } + public IXamlIlType DeclaringType { get; } + + public bool Equals(IXamlIlMethod other) => + other is GetterMethod m && m.Name == Name && m.DeclaringType.Equals(DeclaringType); public IXamlIlType ReturnType => Parent.PropertyType; public IReadOnlyList Parameters { get; } public void EmitCall(IXamlIlEmitter emitter) diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlTransitionsTypeMetadataTransformer.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlTransitionsTypeMetadataTransformer.cs index 11e20a9dab..36c5cc2b98 100644 --- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlTransitionsTypeMetadataTransformer.cs +++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlTransitionsTypeMetadataTransformer.cs @@ -12,7 +12,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers foreach (var ch in on.Children) { if (ch is XamlIlAstXamlPropertyValueNode pn - && pn.Property.GetClrProperty().PropertyType.Equals(context.GetAvaloniaTypes().Transitions)) + && pn.Property.GetClrProperty().Getter?.ReturnType.Equals(context.GetAvaloniaTypes().Transitions) == true) { for (var c = 0; c < pn.Values.Count; c++) { diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs index 83c35605e1..b57c26c241 100644 --- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs +++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs @@ -7,14 +7,17 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers { public IXamlIlType AvaloniaObject { get; } public IXamlIlType IAvaloniaObject { get; } + public IXamlIlType BindingPriority { get; } public IXamlIlType AvaloniaObjectExtensions { get; } public IXamlIlType AvaloniaProperty { get; } public IXamlIlType IBinding { get; } public IXamlIlMethod AvaloniaObjectBindMethod { get; } + public IXamlIlMethod AvaloniaObjectSetValueMethod { get; } public IXamlIlType IDisposable { get; } public XamlIlTypeWellKnownTypes XamlIlTypes { get; } public IXamlIlType Transitions { get; } public IXamlIlType AssignBindingAttribute { get; } + public IXamlIlType UnsetValueType { get; } public AvaloniaXamlIlWellKnownTypes(XamlIlAstTransformationContext ctx) { @@ -23,6 +26,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers IAvaloniaObject = ctx.Configuration.TypeSystem.GetType("Avalonia.IAvaloniaObject"); AvaloniaObjectExtensions = ctx.Configuration.TypeSystem.GetType("Avalonia.AvaloniaObjectExtensions"); AvaloniaProperty = ctx.Configuration.TypeSystem.GetType("Avalonia.AvaloniaProperty"); + BindingPriority = ctx.Configuration.TypeSystem.GetType("Avalonia.Data.BindingPriority"); IBinding = ctx.Configuration.TypeSystem.GetType("Avalonia.Data.IBinding"); IDisposable = ctx.Configuration.TypeSystem.GetType("System.IDisposable"); Transitions = ctx.Configuration.TypeSystem.GetType("Avalonia.Animation.Transitions"); @@ -30,6 +34,9 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers AvaloniaObjectBindMethod = AvaloniaObjectExtensions.FindMethod("Bind", IDisposable, false, IAvaloniaObject, AvaloniaProperty, IBinding, ctx.Configuration.WellKnownTypes.Object); + UnsetValueType = ctx.Configuration.TypeSystem.GetType("Avalonia.UnsetValueType"); + AvaloniaObjectSetValueMethod = AvaloniaObject.FindMethod("SetValue", XamlIlTypes.Void, + false, AvaloniaProperty, XamlIlTypes.Object, BindingPriority); } } diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs index e090b42d17..106541ec5c 100644 --- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs +++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Xml; using Avalonia.Markup.Xaml.Parsers; +using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers; using Avalonia.Utilities; using XamlIl; using XamlIl.Ast; @@ -13,13 +15,25 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions { class XamlIlAvaloniaPropertyHelper { - public static bool Emit(XamlIlEmitContext context, IXamlIlEmitter emitter, IXamlIlProperty property) + public static bool Emit(XamlIlEmitContext context, IXamlIlEmitter emitter, XamlIlAstClrProperty property) { if (property is IXamlIlAvaloniaProperty ap) { emitter.Ldsfld(ap.AvaloniaProperty); return true; } + var type = property.DeclaringType; + var name = property.Name + "Property"; + var found = type.Fields.FirstOrDefault(f => f.IsStatic && f.Name == name); + if (found == null) + return false; + + emitter.Ldsfld(found); + return true; + } + + public static bool Emit(XamlIlEmitContext context, IXamlIlEmitter emitter, IXamlIlProperty property) + { var type = (property.Getter ?? property.Setter).DeclaringType; var name = property.Name + "Property"; var found = type.Fields.FirstOrDefault(f => f.IsStatic && f.Name == name); @@ -54,8 +68,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions } var clrProperty = - ((XamlIlAstClrPropertyReference)new XamlIlPropertyReferenceResolver().Transform(context, - forgedReference)).Property; + ((XamlIlAstClrProperty)new XamlIlPropertyReferenceResolver().Transform(context, + forgedReference)); return new XamlIlAvaloniaPropertyNode(lineInfo, context.Configuration.TypeSystem.GetType("Avalonia.AvaloniaProperty"), clrProperty); @@ -64,13 +78,13 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions class XamlIlAvaloniaPropertyNode : XamlIlAstNode, IXamlIlAstValueNode, IXamlIlAstEmitableNode { - public XamlIlAvaloniaPropertyNode(IXamlIlLineInfo lineInfo, IXamlIlType type, IXamlIlProperty property) : base(lineInfo) + public XamlIlAvaloniaPropertyNode(IXamlIlLineInfo lineInfo, IXamlIlType type, XamlIlAstClrProperty property) : base(lineInfo) { Type = new XamlIlAstClrTypeReference(this, type, false); Property = property; } - public IXamlIlProperty Property { get; } + public XamlIlAstClrProperty Property { get; } public IXamlIlAstTypeReference Type { get; } public XamlIlNodeEmitResult Emit(XamlIlEmitContext context, IXamlIlEmitter codeGen) @@ -81,29 +95,92 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions } } - interface IXamlIlAvaloniaProperty : IXamlIlProperty + interface IXamlIlAvaloniaProperty { IXamlIlField AvaloniaProperty { get; } } - class XamlIlAvaloniaProperty : IXamlIlAvaloniaProperty + class XamlIlAvaloniaProperty : XamlIlAstClrProperty, IXamlIlAvaloniaProperty { - private readonly IXamlIlProperty _original; - public IXamlIlField AvaloniaProperty { get; } - public XamlIlAvaloniaProperty(IXamlIlProperty original, IXamlIlField field) + public XamlIlAvaloniaProperty(XamlIlAstClrProperty original, IXamlIlField field, + AvaloniaXamlIlWellKnownTypes types) + :base(original, original.Name, original.DeclaringType, original.Getter, original.Setters) { - _original = original; AvaloniaProperty = field; + CustomAttributes = original.CustomAttributes; + if (!original.CustomAttributes.Any(ca => ca.Type.Equals(types.AssignBindingAttribute))) + Setters.Insert(0, new BindingSetter(types, original.DeclaringType, field)); + + Setters.Insert(0, new UnsetValueSetter(types, original.DeclaringType, field)); } - public bool Equals(IXamlIlProperty other) => - other is XamlIlAvaloniaProperty p && p.AvaloniaProperty.Equals(AvaloniaProperty); + abstract class AvaloniaPropertyCustomSetter : IXamlIlPropertySetter + { + protected AvaloniaXamlIlWellKnownTypes Types; + protected IXamlIlField AvaloniaProperty; - public string Name => _original.Name; - public IXamlIlType PropertyType => _original.PropertyType; - public IXamlIlMethod Setter => _original.Setter; - public IXamlIlMethod Getter => _original.Getter; - public IReadOnlyList CustomAttributes => _original.CustomAttributes; + public AvaloniaPropertyCustomSetter(AvaloniaXamlIlWellKnownTypes types, + IXamlIlType declaringType, + IXamlIlField avaloniaProperty) + { + Types = types; + AvaloniaProperty = avaloniaProperty; + TargetType = declaringType; + } + + public IXamlIlType TargetType { get; } + + public PropertySetterBinderParameters BinderParameters { get; } = new PropertySetterBinderParameters + { + AllowNull = false + }; + + public IReadOnlyList Parameters { get; set; } + public abstract void Emit(IXamlIlEmitter codegen); + } + + class BindingSetter : AvaloniaPropertyCustomSetter + { + public BindingSetter(AvaloniaXamlIlWellKnownTypes types, + IXamlIlType declaringType, + IXamlIlField avaloniaProperty) : base(types, declaringType, avaloniaProperty) + { + Parameters = new[] {types.IBinding}; + } + + public override void Emit(IXamlIlEmitter emitter) + { + using (var bloc = emitter.LocalsPool.GetLocal(Types.IBinding)) + emitter + .Stloc(bloc.Local) + .Ldsfld(AvaloniaProperty) + .Ldloc(bloc.Local) + // TODO: provide anchor? + .Ldnull(); + emitter.EmitCall(Types.AvaloniaObjectBindMethod, true); + } + } + + class UnsetValueSetter : AvaloniaPropertyCustomSetter + { + public UnsetValueSetter(AvaloniaXamlIlWellKnownTypes types, IXamlIlType declaringType, IXamlIlField avaloniaProperty) + : base(types, declaringType, avaloniaProperty) + { + Parameters = new[] {types.UnsetValueType}; + } + + public override void Emit(IXamlIlEmitter codegen) + { + var unsetValue = Types.AvaloniaProperty.Fields.First(f => f.Name == "UnsetValue"); + codegen + // Ignore the instance and load one from the static field to avoid extra local variable + .Pop() + .Ldsfld(AvaloniaProperty) + .Ldsfld(unsetValue) + .Ldc_I4(0) + .EmitCall(Types.AvaloniaObjectSetValueMethod, true); + } + } } } diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github b/src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github index c0da3c4981..56c401c48f 160000 --- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github +++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github @@ -1 +1 @@ -Subproject commit c0da3c49810316a96b47a00a83ae79d4f28406a1 +Subproject commit 56c401c48f51979201ad810c96a9644705d4ddd9 From f05ad94d518f914c43fecedd4bdf4c83c3803a68 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Sun, 5 May 2019 21:54:27 +0300 Subject: [PATCH 60/73] Updated xamlil --- .../XamlIl/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs | 2 +- src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs index 106541ec5c..452bb05132 100644 --- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs +++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs @@ -133,7 +133,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions public PropertySetterBinderParameters BinderParameters { get; } = new PropertySetterBinderParameters { - AllowNull = false + AllowXNull = false }; public IReadOnlyList Parameters { get; set; } diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github b/src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github index 56c401c48f..bbb8d66e38 160000 --- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github +++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github @@ -1 +1 @@ -Subproject commit 56c401c48f51979201ad810c96a9644705d4ddd9 +Subproject commit bbb8d66e3884e52bdf5385e44caee1e31c942ec1 From f83bb1b4c2f8a2dad8a11965e2db769c0bff2e4c Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Sun, 5 May 2019 22:07:52 +0300 Subject: [PATCH 61/73] Actually show the uri to improve debugging experience --- .../Converters/AvaloniaUriTypeConverter.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaUriTypeConverter.cs b/src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaUriTypeConverter.cs index 6f6a0b8563..3c3a718213 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaUriTypeConverter.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaUriTypeConverter.cs @@ -17,9 +17,10 @@ namespace Avalonia.Markup.Xaml.Converters if (s == null) return null; //On Unix Uri tries to interpret paths starting with "/" as file Uris - if (s.StartsWith("/")) - return new Uri(s, UriKind.Relative); - return new Uri(s); + var kind = s.StartsWith("/") ? UriKind.Relative : UriKind.Absolute; + if (!Uri.TryCreate(s, kind, out var res)) + throw new ArgumentException("Unable to parse URI: " + s); + return res; } } } From 1653c59e20d8ceb6ef43be494fda7c76f5e3ff93 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Sun, 5 May 2019 22:30:23 +0300 Subject: [PATCH 62/73] Updated xamlil --- src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github b/src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github index bbb8d66e38..c7869e864d 160000 --- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github +++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github @@ -1 +1 @@ -Subproject commit bbb8d66e3884e52bdf5385e44caee1e31c942ec1 +Subproject commit c7869e864d52a4ddc498837d238bbf40dfa97f6d From 20574f7c131fd3bdc1f189817ed32c20cbc16b47 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Sun, 5 May 2019 22:42:01 +0300 Subject: [PATCH 63/73] Test for reported TemplatedParent issue --- .../Xaml/XamlIlTests.cs | 62 ++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs index f33054e5ce..419fa18fa9 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs @@ -1,7 +1,14 @@ +using System; +using System.Collections.Generic; using System.ComponentModel; +using System.Globalization; +using System.Linq; using System.Runtime.CompilerServices; using Avalonia.Controls; +using Avalonia.Data.Converters; using Avalonia.Media; +using Avalonia.UnitTests; +using Avalonia.VisualTree; using JetBrains.Annotations; using Xunit; @@ -52,7 +59,60 @@ namespace Avalonia.Markup.Xaml.UnitTests Assert.Null(loaded.Background); } - + + [Fact] + public void RelativeSource_TemplatedParent_Works() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var parsed = (Window)AvaloniaXamlLoader.Parse(@" + + + + +