From 06a68751a4f8ba99e1115cb9f5c079f7ba0e8ea6 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Sun, 23 Oct 2022 01:11:53 -0400 Subject: [PATCH] Move IsOnPlatform method to the xaml helpers class --- .../Pages/PlatformInfoPage.xaml.cs | 3 +- .../AvaloniaXamlIlLanguageParseIntrinsics.cs | 48 +++++++++---------- .../AvaloniaXamlIlOnPlatformTransformer.cs | 46 ++++++++++++------ .../AvaloniaXamlIlWellKnownTypes.cs | 11 ++++- .../MarkupExtensions/OnPlatformExtension.cs | 18 ------- .../XamlIl/Runtime/XamlIlRuntimeHelpers.cs | 28 +++++++---- .../OnPlatformExtensionTests.cs | 30 ++++++++++-- 7 files changed, 112 insertions(+), 72 deletions(-) diff --git a/samples/ControlCatalog/Pages/PlatformInfoPage.xaml.cs b/samples/ControlCatalog/Pages/PlatformInfoPage.xaml.cs index fdd3c6f771..1f37451782 100644 --- a/samples/ControlCatalog/Pages/PlatformInfoPage.xaml.cs +++ b/samples/ControlCatalog/Pages/PlatformInfoPage.xaml.cs @@ -1,5 +1,4 @@ -using Avalonia; -using Avalonia.Controls; +using Avalonia.Controls; using Avalonia.Markup.Xaml; using ControlCatalog.ViewModels; diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguageParseIntrinsics.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguageParseIntrinsics.cs index d907bcbef9..023e978afa 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguageParseIntrinsics.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguageParseIntrinsics.cs @@ -48,10 +48,10 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions try { var thickness = Thickness.Parse(text); - + result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Thickness, types.ThicknessFullConstructor, new[] { thickness.Left, thickness.Top, thickness.Right, thickness.Bottom }); - + return true; } catch @@ -65,10 +65,10 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions try { var point = Point.Parse(text); - + result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Point, types.PointFullConstructor, new[] { point.X, point.Y }); - + return true; } catch @@ -76,16 +76,16 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a point", node); } } - + if (type.Equals(types.Vector)) { try { var vector = Vector.Parse(text); - + result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Vector, types.VectorFullConstructor, new[] { vector.X, vector.Y }); - + return true; } catch @@ -93,16 +93,16 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a vector", node); } } - + if (type.Equals(types.Size)) { try { var size = Size.Parse(text); - + result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Size, types.SizeFullConstructor, new[] { size.Width, size.Height }); - + return true; } catch @@ -110,16 +110,16 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a size", node); } } - + if (type.Equals(types.Matrix)) { try { var matrix = Matrix.Parse(text); - + result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Matrix, types.MatrixFullConstructor, new[] { matrix.M11, matrix.M12, matrix.M21, matrix.M22, matrix.M31, matrix.M32 }); - + return true; } catch @@ -127,16 +127,16 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a matrix", node); } } - + if (type.Equals(types.CornerRadius)) { try { var cornerRadius = CornerRadius.Parse(text); - + result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.CornerRadius, types.CornerRadiusFullConstructor, new[] { cornerRadius.TopLeft, cornerRadius.TopRight, cornerRadius.BottomRight, cornerRadius.BottomLeft }); - + return true; } catch @@ -144,7 +144,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a corner radius", node); } } - + if (type.Equals(types.Color)) { if (!Color.TryParse(text, out Color color)) @@ -165,9 +165,9 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions try { var relativePoint = RelativePoint.Parse(text); - + var relativePointTypeRef = new XamlAstClrTypeReference(node, types.RelativePoint, false); - + result = new XamlAstNewClrObjectNode(node, relativePointTypeRef, types.RelativePointFullConstructor, new List { new XamlConstantNode(node, types.XamlIlTypes.Double, relativePoint.Point.X), @@ -188,9 +188,9 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions try { var gridLength = GridLength.Parse(text); - + result = new AvaloniaXamlIlGridLengthAstNode(node, types, gridLength); - + return true; } catch @@ -201,12 +201,12 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions if (type.Equals(types.Cursor)) { - if (TypeSystemHelpers.TryGetEnumValueNode(types.StandardCursorType, text, node, out var enumConstantNode)) + if (TypeSystemHelpers.TryGetEnumValueNode(types.StandardCursorType, text, node, false, out var enumConstantNode)) { var cursorTypeRef = new XamlAstClrTypeReference(node, types.Cursor, false); result = new XamlAstNewClrObjectNode(node, cursorTypeRef, types.CursorTypeConstructor, new List { enumConstantNode }); - + return true; } } @@ -275,7 +275,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions } private static bool ConvertDefinitionList( - IXamlAstValueNode node, + IXamlAstValueNode node, string text, AvaloniaXamlIlWellKnownTypes types, IXamlType listType, diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlOnPlatformTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlOnPlatformTransformer.cs index 78f18393ba..d28cddcbcf 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlOnPlatformTransformer.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlOnPlatformTransformer.cs @@ -37,7 +37,7 @@ internal class AvaloniaXamlIlOnPlatformTransformer : IXamlAstTransformer } IXamlAstNode defaultValue = null; - var values = new Dictionary(); + var values = new Dictionary(); var directives = objectNode.Children.OfType().ToArray(); @@ -75,23 +75,22 @@ internal class AvaloniaXamlIlOnPlatformTransformer : IXamlAstTransformer foreach (var platform in platformStr.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)) { - values.Add(platform.Trim().ToUpperInvariant(), transformed); + values.Add(ConvertPlatformNode(platform.Trim().ToUpperInvariant(), onObj), transformed); } } } else { - var platformStr = extProp.Property.GetClrProperty().Name.Trim().ToUpperInvariant(); var transformed = TransformNode(targetPropertyNode.Property, extProp.Values, typeArgument, directives, extProp); - if (platformStr.Equals("default", StringComparison.OrdinalIgnoreCase)) + if (platformStr.Equals("DEFAULT", StringComparison.OrdinalIgnoreCase)) { defaultValue = transformed; } - else + else if (platformStr != "CONTENT") { - values.Add(platformStr, transformed); + values.Add(ConvertPlatformNode(platformStr, extProp), transformed); } } } @@ -104,6 +103,26 @@ internal class AvaloniaXamlIlOnPlatformTransformer : IXamlAstTransformer return node; + XamlConstantNode ConvertPlatformNode(string platform, IXamlLineInfo li) + { + var osTypeEnum = context.GetAvaloniaTypes().OperatingSystemType; + if (platform.Equals("MACOS", StringComparison.OrdinalIgnoreCase)) + { + platform = "OSX"; + } + if (platform.Equals("WINDOWS", StringComparison.OrdinalIgnoreCase)) + { + platform = "WINNT"; + } + + if (TypeSystemHelpers.TryGetEnumValueNode(osTypeEnum, platform, li, true, out var enumConstantNode)) + { + return enumConstantNode; + } + + throw new XamlParseException($"Unable to parse platform name: \"{platform}\"", li); + } + XamlAstXamlPropertyValueNode TransformNode( IXamlAstPropertyReference property, IReadOnlyCollection values, @@ -140,11 +159,11 @@ internal class AvaloniaXamlIlOnPlatformTransformer : IXamlAstTransformer { private IXamlAstNode _defaultValue; private readonly IXamlAstNode[] _values; - private readonly string[] _valuePlatforms; + private readonly XamlConstantNode[] _valuePlatforms; public XamlIlOnPlatformExtensionNode( IXamlAstNode defaultValue, - IDictionary values, + IDictionary values, IXamlAstTypeReference targetType, IXamlLineInfo info) : base(info) { @@ -158,6 +177,7 @@ internal class AvaloniaXamlIlOnPlatformTransformer : IXamlAstTransformer { _defaultValue = _defaultValue?.Visit(visitor); VisitList(_values, visitor); + VisitList(_valuePlatforms, visitor); } public IXamlAstTypeReference Type { get; } @@ -165,11 +185,8 @@ internal class AvaloniaXamlIlOnPlatformTransformer : IXamlAstTransformer public XamlILNodeEmitResult Emit(XamlEmitContext context, IXamlILEmitter codeGen) { - var operatingSystemClass = - context.Configuration.TypeSystem.GetType( - "Avalonia.Markup.Xaml.MarkupExtensions.OnPlatformExtensionHelper"); - var isOSPlatformMethod = operatingSystemClass - .FindMethod(m => m.IsStatic && m.Parameters.Count == 1 && m.Name == "IsOSPlatform"); + var osTypeEnum = context.GetAvaloniaTypes().OperatingSystemType; + var isOSPlatformMethod = context.GetAvaloniaTypes().IsOnPlatformMethod; var ret = codeGen.DefineLabel(); @@ -179,7 +196,8 @@ internal class AvaloniaXamlIlOnPlatformTransformer : IXamlAstTransformer var propertyNode = _values[index]; var next = codeGen.DefineLabel(); - codeGen.Ldstr(platform); + codeGen.Ldloc(context.ContextLocal); + context.Emit(platform, codeGen, osTypeEnum); codeGen.EmitCall(isOSPlatformMethod); codeGen.Brfalse(next); context.Emit(propertyNode, codeGen, null); diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs index dbfbe0e070..1de4b2542a 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs @@ -9,6 +9,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers { class AvaloniaXamlIlWellKnownTypes { + public IXamlType RuntimeHelpers { get; } public IXamlType AvaloniaObject { get; } public IXamlType IAvaloniaObject { get; } public IXamlType BindingPriority { get; } @@ -102,8 +103,13 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers public IXamlType ResourceDictionary { get; } public IXamlMethod ResourceDictionaryDeferredAdd { get; } + public IXamlType OperatingSystemType { get; } + public IXamlMethod IsOnPlatformMethod { get; } + public AvaloniaXamlIlWellKnownTypes(TransformerConfiguration cfg) { + RuntimeHelpers = cfg.TypeSystem.GetType("Avalonia.Markup.Xaml.XamlIl.Runtime.XamlIlRuntimeHelpers"); + XamlIlTypes = cfg.WellKnownTypes; AvaloniaObject = cfg.TypeSystem.GetType("Avalonia.AvaloniaObject"); IAvaloniaObject = cfg.TypeSystem.GetType("Avalonia.IAvaloniaObject"); @@ -227,6 +233,9 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers cfg.TypeSystem.GetType("System.Func`2").MakeGenericType( cfg.TypeSystem.GetType("System.IServiceProvider"), XamlIlTypes.Object)); + + OperatingSystemType = cfg.TypeSystem.GetType("Avalonia.Platform.OperatingSystemType"); + IsOnPlatformMethod = RuntimeHelpers.FindMethod(m => m.IsStatic && m.Parameters.Count == 2 && m.Name == "IsOnPlatform"); } } @@ -239,7 +248,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers ctx.SetItem(rv = new AvaloniaXamlIlWellKnownTypes(ctx.Configuration)); return rv; } - + public static AvaloniaXamlIlWellKnownTypes GetAvaloniaTypes(this XamlEmitContext ctx) { if (ctx.TryGetItem(out var rv)) diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/OnPlatformExtension.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/OnPlatformExtension.cs index 607f3ecc07..c4c32939fe 100644 --- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/OnPlatformExtension.cs +++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/OnPlatformExtension.cs @@ -1,30 +1,12 @@ #nullable enable using System; using System.Collections.Generic; -using System.Globalization; using System.Linq; -using System.Runtime.InteropServices; using Avalonia.Metadata; using Avalonia.Platform; namespace Avalonia.Markup.Xaml.MarkupExtensions; -public static class OnPlatformExtensionHelper -{ - // TEMPORARY, to replace with XAML compiler namespace helpers - public static bool IsOSPlatform(string platform) - { - var runtimeInfo = AvaloniaLocator.Current.GetRequiredService().GetRuntimeInfo(); - return platform switch - { - "WINDOWS" => runtimeInfo.OperatingSystem == OperatingSystemType.WinNT, - "MACOS" => runtimeInfo.OperatingSystem == OperatingSystemType.OSX, - _ => runtimeInfo.OperatingSystem.ToString().Equals(platform, StringComparison.OrdinalIgnoreCase) - }; - //return RuntimeInformation.IsOSPlatform(OSPlatform.Create(platform)); - } -} - public class On { public string Platform { get; set; } = "Unknown"; diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs index ddf11f2a0c..3e03be3a47 100644 --- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs +++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs @@ -1,11 +1,12 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using System.Reflection; using Avalonia.Controls; using Avalonia.Controls.Templates; using Avalonia.Data; +using Avalonia.Platform; + // ReSharper disable UnusedMember.Global // ReSharper disable UnusedParameter.Global @@ -18,7 +19,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.Runtime { return DeferredTransformationFactoryV2(builder, provider); } - + public static Func DeferredTransformationFactoryV2(Func builder, IServiceProvider provider) { @@ -31,7 +32,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.Runtime var scope = parentScope != null ? new ChildNameScope(parentScope) : (INameScope)new NameScope(); var obj = builder(new DeferredParentServiceProvider(sp, resourceNodes, rootObject, scope)); scope.Complete(); - + if(typeof(T) == typeof(IControl)) return new ControlTemplateResult((IControl)obj, scope); @@ -107,9 +108,9 @@ namespace Avalonia.Markup.Xaml.XamlIl.Runtime throw new ArgumentException("Don't know what to do with " + value.GetType()); } - public static IServiceProvider CreateInnerServiceProviderV1(IServiceProvider compiled) + public static IServiceProvider CreateInnerServiceProviderV1(IServiceProvider compiled) => new InnerServiceProvider(compiled); - + class InnerServiceProvider : IServiceProvider { private readonly IServiceProvider _compiledProvider; @@ -136,7 +137,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.Runtime { _nsInfo = nsInfo; } - + public Type Resolve(string qualifiedTypeName) { var sp = qualifiedTypeName.Split(new[] {':'}, 2); @@ -166,23 +167,27 @@ namespace Avalonia.Markup.Xaml.XamlIl.Runtime return new RootServiceProvider(new NameScope()); } #line default - + class RootServiceProvider : IServiceProvider, IAvaloniaXamlIlParentStackProvider { private readonly INameScope _nameScope; + private readonly IRuntimePlatform _runtimePlatform; public RootServiceProvider(INameScope nameScope) { _nameScope = nameScope; + _runtimePlatform = AvaloniaLocator.Current.GetRequiredService(); } - + public object GetService(Type serviceType) { if (serviceType == typeof(INameScope)) return _nameScope; if (serviceType == typeof(IAvaloniaXamlIlParentStackProvider)) return this; - + if (serviceType == typeof(IRuntimePlatform)) + return _runtimePlatform; + return null; } @@ -195,5 +200,10 @@ namespace Avalonia.Markup.Xaml.XamlIl.Runtime } } } + + public static bool IsOnPlatform(IServiceProvider serviceProvider, OperatingSystemType platform) + { + return serviceProvider.GetService().GetRuntimeInfo().OperatingSystem == platform; + } } } diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/OnPlatformExtensionTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/OnPlatformExtensionTests.cs index 834a5ef274..09233e7dde 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/OnPlatformExtensionTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/OnPlatformExtensionTests.cs @@ -1,7 +1,4 @@ -using System; -using System.Globalization; -using Avalonia.Controls; -using Avalonia.Data.Converters; +using Avalonia.Controls; using Avalonia.Media; using Avalonia.Platform; using Xunit; @@ -330,6 +327,31 @@ public class OnPlatformExtensionTests : XamlTestBase } } + [Fact] + public void BindingExtension_Works_Inside_Of_OnPlatform() + { + using (AvaloniaLocator.EnterScope()) + { + AvaloniaLocator.CurrentMutable.Bind() + .ToConstant(new TestRuntimePlatform(OperatingSystemType.WinNT)); + + var xaml = @" + + + foobar + + + +"; + + var window = (UserControl)AvaloniaRuntimeXamlLoader.Load(xaml); + var textBlock = window.FindControl("textBlock"); + + Assert.Equal("foobar", textBlock.Text); + } + } + private class TestRuntimePlatform : StandardRuntimePlatform { private readonly OperatingSystemType _operatingSystemType;