Skip to content

Commit

Permalink
[X] Simplify OnPlatformExtension (#4829)
Browse files Browse the repository at this point in the history
When the platform is known at compile time, and it's known by default as
we are multitargetting, replace OnPlatfomExtension by the actual value.

This is done early in the parsing so every other optimization is
unaffected.

From a perf point of view, there are multiple gains:
- OnPlatformExtension is executed at runtime, and makes use of
  reflection. Removing OnPlatformExtensions from the tree reduces this
  to nothing.
- Values in OnPlatformExtension never benefitted from
  compiledtypeconverters
- not generating the IL for the OnPlatform is huge win too. The unitest
  goes from 189 instructions to 42.

fixes #4716
  • Loading branch information
StephaneDelcroix committed Mar 10, 2022
1 parent 48502e5 commit 43ca83b
Show file tree
Hide file tree
Showing 8 changed files with 158 additions and 16 deletions.
3 changes: 2 additions & 1 deletion .nuspec/Microsoft.Maui.Controls.targets
Original file line number Diff line number Diff line change
Expand Up @@ -152,8 +152,9 @@
OptimizeIL = "true"
DebugSymbols = "$(DebugSymbols)"
DebugType = "$(DebugType)"
DefaultCompile = "true"
DefaultCompile = "true"
ValidateOnly = "$(_MauiXamlCValidateOnly)"
TargetFramework = "$(TargetFramework)"
KeepXamlResources = "$(MauiKeepXamlResources)" />
<Touch Files="$(IntermediateOutputPath)XamlC.stamp" AlwaysCreate="True" />
<ItemGroup>
Expand Down
4 changes: 3 additions & 1 deletion src/Controls/src/Build.Tasks/XamlCTask.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ public class XamlCTask : XamlTask
{
bool hasCompiledXamlResources;
public bool KeepXamlResources { get; set; }
public bool OptimizeIL { get; set; }
public bool OptimizeIL { get; set; } = true;
public bool DefaultCompile { get; set; }
public bool ForceCompile { get; set; }
public string TargetFramework { get; set; }

public IAssemblyResolver DefaultAssemblyResolver { get; set; }

Expand Down Expand Up @@ -271,6 +272,7 @@ bool TryCoreCompile(MethodDefinition initComp, ILRootNode rootnode, string xamlF
rootnode.Accept(new XamlNodeVisitor((node, parent) => node.Parent = parent), null);
rootnode.Accept(new ExpandMarkupsVisitor(visitorContext), null);
rootnode.Accept(new PruneIgnoredNodesVisitor(), null);
rootnode.Accept(new SimplifyOnPlatformVisitor(TargetFramework), null);
rootnode.Accept(new CreateObjectVisitor(visitorContext), null);
rootnode.Accept(new SetNamescopesAndRegisterNamesVisitor(visitorContext), null);
rootnode.Accept(new SetFieldVisitor(visitorContext), null);
Expand Down
16 changes: 8 additions & 8 deletions src/Controls/src/Xaml/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@
[assembly: InternalsVisibleTo("CommunityToolkit.Maui.Markup.UnitTests")]
[assembly: Preserve]

[assembly: XmlnsDefinition("http://schemas.microsoft.com/dotnet/2021/maui", "Microsoft.Maui.Controls.Xaml")]
[assembly: XmlnsDefinition("http://schemas.microsoft.com/dotnet/2021/maui/design", "Microsoft.Maui.Controls.Xaml")]
[assembly: XmlnsDefinition("http://schemas.microsoft.com/winfx/2006/xaml", "Microsoft.Maui.Controls.Xaml")]
[assembly: XmlnsDefinition("http://schemas.microsoft.com/winfx/2006/xaml", "System", AssemblyName = "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
[assembly: XmlnsDefinition("http://schemas.microsoft.com/winfx/2006/xaml", "System", AssemblyName = "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
[assembly: XmlnsDefinition("http://schemas.microsoft.com/winfx/2009/xaml", "Microsoft.Maui.Controls.Xaml")]
[assembly: XmlnsDefinition("http://schemas.microsoft.com/winfx/2009/xaml", "System", AssemblyName = "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
[assembly: XmlnsDefinition("http://schemas.microsoft.com/winfx/2009/xaml", "System", AssemblyName = "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
[assembly: XmlnsDefinition(Microsoft.Maui.Controls.Xaml.XamlParser.MauiUri, "Microsoft.Maui.Controls.Xaml")]
[assembly: XmlnsDefinition(Microsoft.Maui.Controls.Xaml.XamlParser.MauiDesignUri, "Microsoft.Maui.Controls.Xaml")]
[assembly: XmlnsDefinition(Microsoft.Maui.Controls.Xaml.XamlParser.X2006Uri, "Microsoft.Maui.Controls.Xaml")]
[assembly: XmlnsDefinition(Microsoft.Maui.Controls.Xaml.XamlParser.X2006Uri, "System", AssemblyName = "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
[assembly: XmlnsDefinition(Microsoft.Maui.Controls.Xaml.XamlParser.X2006Uri, "System", AssemblyName = "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
[assembly: XmlnsDefinition(Microsoft.Maui.Controls.Xaml.XamlParser.X2009Uri, "Microsoft.Maui.Controls.Xaml")]
[assembly: XmlnsDefinition(Microsoft.Maui.Controls.Xaml.XamlParser.X2009Uri, "System", AssemblyName = "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
[assembly: XmlnsDefinition(Microsoft.Maui.Controls.Xaml.XamlParser.X2009Uri, "System", AssemblyName = "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
77 changes: 77 additions & 0 deletions src/Controls/src/Xaml/SimplifyOnPlatformVisitor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#nullable disable

namespace Microsoft.Maui.Controls.Xaml
{
class SimplifyOnPlatformVisitor : IXamlNodeVisitor
{
public SimplifyOnPlatformVisitor(string targetFramework)
{
TargetFramework = targetFramework;
}

public string TargetFramework { get; }

public TreeVisitingMode VisitingMode => TreeVisitingMode.BottomUp;
public bool StopOnDataTemplate => false;
public bool VisitNodeOnDataTemplate => true;
public bool StopOnResourceDictionary => false;
public bool IsResourceDictionary(ElementNode node) => false;
public bool SkipChildren(INode node, INode parentNode) => false;

public void Visit(ValueNode node, INode parentNode)
{
}

public void Visit(MarkupNode node, INode parentNode)
{
}

public void Visit(ElementNode node, INode parentNode)
{
if (node.XmlType.Name != nameof(OnPlatformExtension) || node.XmlType.NamespaceUri != XamlParser.MauiUri)
return;
if (string.IsNullOrEmpty(TargetFramework))
return;

string target = null;
if (TargetFramework.Contains("-android"))
target = nameof(OnPlatformExtension.Android);
if (TargetFramework.Contains("-ios"))
target = nameof(OnPlatformExtension.iOS);
if (TargetFramework.Contains("-macos"))
target = nameof(OnPlatformExtension.macOS);
if (TargetFramework.Contains("-maccatalyst"))
target = nameof(OnPlatformExtension.MacCatalyst);

if (target is null)
return;

if ( node.Properties.TryGetValue(new XmlName("", target), out INode targetNode)
|| node.Properties.TryGetValue(new XmlName("", nameof(OnPlatformExtension.Default)), out targetNode))
{
if (!ApplyPropertiesVisitor.TryGetPropertyName(node, parentNode, out XmlName name))
return;
if (parentNode is IElementNode parentEnode)
parentEnode.Properties[name] = targetNode;
}
else //no prop for target and no Default set
{
if (!ApplyPropertiesVisitor.TryGetPropertyName(node, parentNode, out XmlName name))
return;
//if there's no value for the targetPlatform, ignore the node.
//this is slightly different than what OnPlatform does (return default(T))
if (parentNode is IElementNode parentEnode)
parentEnode.Properties.Remove(name);
}

}

public void Visit(RootNode node, INode parentNode)
{
}

public void Visit(ListNode node, INode parentNode)
{
}
}
}
2 changes: 1 addition & 1 deletion src/Controls/src/Xaml/XamlNodeVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,4 @@ public XamlNodeVisitor(Action<INode, INode> action, TreeVisitingMode visitingMod
public bool SkipChildren(INode node, INode parentNode) => false;
public bool IsResourceDictionary(ElementNode node) => false;
}
}
}
11 changes: 6 additions & 5 deletions src/Controls/tests/Xaml.UnitTests/MockCompiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ namespace Microsoft.Maui.Controls.Xaml.UnitTests
{
public static class MockCompiler
{
public static void Compile(Type type)
public static void Compile(Type type, string targetFramework = null)
{
MethodDefinition _;
Compile(type, out _);
Compile(type, out _, targetFramework);
}

public static void Compile(Type type, out MethodDefinition methdoDefinition)
public static void Compile(Type type, out MethodDefinition methodDefinition, string targetFramework = null)
{
methdoDefinition = null;
methodDefinition = null;
var assembly = type.Assembly.Location;
var refs = from an in type.Assembly.GetReferencedAssemblies()
let a = System.Reflection.Assembly.Load(an)
Expand All @@ -32,12 +32,13 @@ public static void Compile(Type type, out MethodDefinition methdoDefinition)
DebugSymbols = false,
ValidateOnly = true,
Type = type.FullName,
TargetFramework = targetFramework,
BuildEngine = new MSBuild.UnitTests.DummyBuildEngine()
};

if (xamlc.Execute(out IList<Exception> exceptions) || exceptions == null || !exceptions.Any())
{
methdoDefinition = xamlc.InitCompForType;
methodDefinition = xamlc.InitCompForType;
return;
}
if (exceptions.Count > 1)
Expand Down
11 changes: 11 additions & 0 deletions src/Controls/tests/Xaml.UnitTests/OnPlatformOptimization.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Microsoft.Maui.Controls.Xaml.UnitTests.OnPlatformOptimization">
<StackLayout>
<Label x:Name="label0"
Text="{OnPlatform Android='john', iOS='paul', Default='ringo' }"
Padding="{OnPlatform iOS='2,3,4,5'}" />

</StackLayout>
</ContentPage>
50 changes: 50 additions & 0 deletions src/Controls/tests/Xaml.UnitTests/OnPlatformOptimization.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Maui.Controls;
using Microsoft.Maui.Controls.Core.UnitTests;
using Microsoft.Maui.Essentials;
using NUnit.Framework;
using Mono.Cecil.Cil;
using Mono.Cecil;

namespace Microsoft.Maui.Controls.Xaml.UnitTests
{
public partial class OnPlatformOptimization : ContentPage
{
public OnPlatformOptimization()
{
InitializeComponent();
}

public OnPlatformOptimization(bool useCompiledXaml)
{
//this stub will be replaced at compile time
}

[TestFixture]
public class Tests
{
[SetUp] public void Setup() => Device.PlatformServices = new MockPlatformServices();
[TearDown] public void TearDown() => Device.PlatformServices = null;

[Test]
public void OnPlatformExtensionsAreSimplified([Values("net6.0-ios", "net6.0-android")] string targetFramework)
{
MockCompiler.Compile(typeof(OnPlatformOptimization), out var methodDef, targetFramework);
Assert.That(!methodDef.Body.Instructions.Any(instr=>InstructionIsOnPlatformExtensionCtor(methodDef, instr)), "This Xaml still generates a new OnPlatformExtension()");
}

bool InstructionIsOnPlatformExtensionCtor(MethodDefinition methodDef, Mono.Cecil.Cil.Instruction instruction)
{
if (instruction.OpCode != OpCodes.Newobj)
return false;
if (!(instruction.Operand is MethodReference methodRef))
return false;
if (!Build.Tasks.TypeRefComparer.Default.Equals(methodRef.DeclaringType, methodDef.Module.ImportReference(typeof(OnPlatformExtension))))
return false;
return true;
}
}
}
}

0 comments on commit 43ca83b

Please sign in to comment.