Browse Source

Merge pull request #7247 from jkoritzinsky/compiled-bindings-default

Add feature switch to enable switching compiled bindings on by default.
feature/remove-legacy-renderers
Max Katz 3 years ago
committed by GitHub
parent
commit
24bc7a6887
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      packages/Avalonia/AvaloniaBuildTasks.targets
  2. 2
      src/Avalonia.Build.Tasks/BuildEngineErrorCode.cs
  3. 5
      src/Avalonia.Build.Tasks/CompileAvaloniaXamlTask.cs
  4. 66
      src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
  5. 11
      src/Avalonia.DesignerSupport/DesignWindowLoader.cs
  6. 28
      src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaRuntimeXamlLoader.cs
  7. 32
      src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs
  8. 1
      src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
  9. 11
      src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs
  10. 36
      src/Markup/Avalonia.Markup.Xaml/RuntimeXamlLoaderConfiguration.cs
  11. 6
      src/tools/Avalonia.Designer.HostApp/DesignXamlLoader.cs
  12. 21
      tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs
  13. 4
      tests/Avalonia.Markup.Xaml.UnitTests/XamlTestBase.cs

4
packages/Avalonia/AvaloniaBuildTasks.targets

@ -4,6 +4,7 @@
<_AvaloniaUseExternalMSBuild Condition="'$(_AvaloniaForceInternalMSBuild)' == 'true'">false</_AvaloniaUseExternalMSBuild>
<AvaloniaXamlReportImportance Condition="'$(AvaloniaXamlReportImportance)' == ''">low</AvaloniaXamlReportImportance>
<_AvaloniaSkipXamlCompilation Condition="'$(_AvaloniaSkipXamlCompilation)' == ''">false</_AvaloniaSkipXamlCompilation>
<AvaloniaUseCompiledBindingsByDefault Condition="'$(AvaloniaUseCompiledBindingsByDefault)' == ''">false</AvaloniaUseCompiledBindingsByDefault>
</PropertyGroup>
<!-- Unfortunately we have to update default items in .targets since custom nuget props are improted before Microsoft.NET.Sdk.DefaultItems.props -->
@ -43,7 +44,7 @@
<PropertyGroup>
<BuildAvaloniaResourcesDependsOn>$(BuildAvaloniaResourcesDependsOn);AddAvaloniaResources;ResolveReferences;_GenerateAvaloniaResourcesDependencyCache</BuildAvaloniaResourcesDependsOn>
</PropertyGroup>
<Target Name="_GenerateAvaloniaResourcesDependencyCache" BeforeTargets="GenerateAvaloniaResources">
<ItemGroup>
<CustomAdditionalGenerateAvaloniaResourcesInputs Include="$(IntermediateOutputPath)/Avalonia/Resources.Inputs.cache" />
@ -106,6 +107,7 @@
DelaySign="$(DelaySign)"
SkipXamlCompilation="$(_AvaloniaSkipXamlCompilation)"
DebuggerLaunch="$(AvaloniaXamlIlDebuggerLaunch)"
DefaultCompileBindings="$(AvaloniaUseCompiledBindingsByDefault)"
/>
<Exec
Condition="'$(_AvaloniaUseExternalMSBuild)' == 'true'"

2
src/Avalonia.Build.Tasks/BuildEngineErrorCode.cs

@ -5,5 +5,7 @@ namespace Avalonia.Build.Tasks
InvalidXAML = 1,
DuplicateXClass = 2,
LegacyResmScheme = 3,
Unknown = 9999
}
}

5
src/Avalonia.Build.Tasks/CompileAvaloniaXamlTask.cs

@ -36,7 +36,7 @@ namespace Avalonia.Build.Tasks
var res = XamlCompilerTaskExecutor.Compile(BuildEngine, input,
File.ReadAllLines(ReferencesFilePath).Where(l => !string.IsNullOrWhiteSpace(l)).ToArray(),
ProjectDirectory, OutputPath, VerifyIl, outputImportance,
ProjectDirectory, OutputPath, VerifyIl, DefaultCompileBindings, outputImportance,
(SignAssembly && !DelaySign) ? AssemblyOriginatorKeyFile : null, SkipXamlCompilation, DebuggerLaunch);
if (!res.Success)
return false;
@ -71,6 +71,9 @@ namespace Avalonia.Build.Tasks
public string OutputPath { get; set; }
public bool VerifyIl { get; set; }
public bool DefaultCompileBindings { get; set; }
public bool SkipXamlCompilation { get; set; }
public string AssemblyOriginatorKeyFile { get; set; }

66
src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs

@ -39,43 +39,52 @@ namespace Avalonia.Build.Tasks
public static CompileResult Compile(IBuildEngine engine, string input, string[] references,
string projectDirectory,
string output, bool verifyIl, MessageImportance logImportance, string strongNameKey,
string output, bool verifyIl, bool defaultCompileBindings, MessageImportance logImportance, string strongNameKey,
bool skipXamlCompilation)
{
return Compile(engine, input, references, projectDirectory, output, verifyIl, logImportance, strongNameKey, skipXamlCompilation, debuggerLaunch:false);
return Compile(engine, input, references, projectDirectory, output, verifyIl, defaultCompileBindings, logImportance, strongNameKey, skipXamlCompilation, debuggerLaunch:false);
}
internal static CompileResult Compile(IBuildEngine engine, string input, string[] references,
string projectDirectory,
string output, bool verifyIl, MessageImportance logImportance, string strongNameKey, bool skipXamlCompilation, bool debuggerLaunch)
string output, bool verifyIl, bool defaultCompileBindings, MessageImportance logImportance, string strongNameKey, bool skipXamlCompilation, bool debuggerLaunch)
{
var typeSystem = new CecilTypeSystem(
references.Where(r => !r.ToLowerInvariant().EndsWith("avalonia.build.tasks.dll")),
input);
var asm = typeSystem.TargetAssemblyDefinition;
if (!skipXamlCompilation)
try
{
var compileRes = CompileCore(engine, typeSystem, projectDirectory, verifyIl, logImportance, debuggerLaunch);
if (compileRes == null)
return new CompileResult(true);
if (compileRes == false)
return new CompileResult(false);
}
var typeSystem = new CecilTypeSystem(
references.Where(r => !r.ToLowerInvariant().EndsWith("avalonia.build.tasks.dll")),
input);
var writerParameters = new WriterParameters { WriteSymbols = asm.MainModule.HasSymbols };
if (!string.IsNullOrWhiteSpace(strongNameKey))
writerParameters.StrongNameKeyBlob = File.ReadAllBytes(strongNameKey);
var asm = typeSystem.TargetAssemblyDefinition;
if (!skipXamlCompilation)
{
var compileRes = CompileCore(engine, typeSystem, projectDirectory, verifyIl, defaultCompileBindings,
logImportance, debuggerLaunch);
if (compileRes == null)
return new CompileResult(true);
if (compileRes == false)
return new CompileResult(false);
}
asm.Write(output, writerParameters);
var writerParameters = new WriterParameters { WriteSymbols = asm.MainModule.HasSymbols };
if (!string.IsNullOrWhiteSpace(strongNameKey))
writerParameters.StrongNameKeyBlob = File.ReadAllBytes(strongNameKey);
return new CompileResult(true, true);
asm.Write(output, writerParameters);
return new CompileResult(true, true);
}
catch (Exception ex)
{
engine.LogError(BuildEngineErrorCode.Unknown, "", ex.Message);
return new CompileResult(false);
}
}
static bool? CompileCore(IBuildEngine engine, CecilTypeSystem typeSystem,
string projectDirectory, bool verifyIl,
string projectDirectory, bool verifyIl,
bool defaultCompileBindings,
MessageImportance logImportance
, bool debuggerLaunch = false)
{
@ -113,7 +122,16 @@ namespace Avalonia.Build.Tasks
if (avares.Resources.Count(CheckXamlName) == 0)
// Nothing to do
return null;
if (typeSystem.FindType("System.Reflection.AssemblyMetadataAttribute") is {} asmMetadata)
{
var ctor = asm.MainModule.ImportReference(typeSystem.GetTypeReference(asmMetadata).Resolve()
.GetConstructors().First(c => c.Parameters.Count == 2).Resolve());
var strType = asm.MainModule.ImportReference(typeof(string));
var arg1 = new CustomAttributeArgument(strType, "AvaloniaUseCompiledBindingsByDefault");
var arg2 = new CustomAttributeArgument(strType, defaultCompileBindings.ToString());
asm.CustomAttributes.Add(new CustomAttribute(ctor) { ConstructorArguments = { arg1, arg2 } });
}
var clrPropertiesDef = new TypeDefinition("CompiledAvaloniaXaml", "XamlIlHelpers",
TypeAttributes.Class, asm.MainModule.TypeSystem.Object);
asm.MainModule.Types.Add(clrPropertiesDef);
@ -143,7 +161,7 @@ namespace Avalonia.Build.Tasks
var contextClass = XamlILContextDefinition.GenerateContextClass(typeSystem.CreateTypeBuilder(contextDef), typeSystem,
xamlLanguage, emitConfig);
var compiler = new AvaloniaXamlIlCompiler(compilerConfig, emitConfig, contextClass) { EnableIlVerification = verifyIl };
var compiler = new AvaloniaXamlIlCompiler(compilerConfig, emitConfig, contextClass) { EnableIlVerification = verifyIl, DefaultCompileBindings = defaultCompileBindings };
var editorBrowsableAttribute = typeSystem
.GetTypeReference(typeSystem.FindType("System.ComponentModel.EditorBrowsableAttribute"))

11
src/Avalonia.DesignerSupport/DesignWindowLoader.cs

@ -35,7 +35,16 @@ namespace Avalonia.DesignerSupport
}
var localAsm = assemblyPath != null ? Assembly.LoadFile(Path.GetFullPath(assemblyPath)) : null;
var loaded = loader.Load(stream, localAsm, null, baseUri, true);
var useCompiledBindings = localAsm?.GetCustomAttributes<AssemblyMetadataAttribute>()
.FirstOrDefault(a => a.Key == "AvaloniaUseCompiledBindingsByDefault")?.Value;
var loaded = loader.Load(stream, new RuntimeXamlLoaderConfiguration
{
LocalAssembly = localAsm,
BaseUri = baseUri,
DesignMode = true,
UseCompiledBindingsByDefault = bool.TryParse(useCompiledBindings, out var parsedValue ) && parsedValue
});
var style = loaded as IStyle;
var resources = loaded as ResourceDictionary;
if (style != null)

28
src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaRuntimeXamlLoader.cs

@ -26,6 +26,22 @@ namespace Avalonia.Markup.Xaml
return Load(stream, localAssembly, rootInstance, uri, designMode);
}
}
/// <summary>
/// Loads XAML from a string.
/// </summary>
/// <param name="xaml">The string containing the XAML.</param>
/// <param name="configuration">Xaml loader configuration.</param>
/// <returns>The loaded object.</returns>
public static object Load(string xaml, RuntimeXamlLoaderConfiguration configuration)
{
Contract.Requires<ArgumentNullException>(xaml != null);
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(xaml)))
{
return Load(stream, configuration);
}
}
/// <summary>
/// Loads XAML from a stream.
@ -38,7 +54,17 @@ namespace Avalonia.Markup.Xaml
/// <returns>The loaded object.</returns>
public static object Load(Stream stream, Assembly localAssembly, object rootInstance = null, Uri uri = null,
bool designMode = false)
=> AvaloniaXamlIlRuntimeCompiler.Load(stream, localAssembly, rootInstance, uri, designMode);
=> AvaloniaXamlIlRuntimeCompiler.Load(stream, localAssembly, rootInstance, uri, designMode, false);
/// <summary>
/// Loads XAML from a stream.
/// </summary>
/// <param name="stream">The stream containing the XAML.</param>
/// <param name="configuration">Xaml loader configuration.</param>
/// <returns>The loaded object.</returns>
public static object Load(Stream stream, RuntimeXamlLoaderConfiguration configuration)
=> AvaloniaXamlIlRuntimeCompiler.Load(stream, configuration.LocalAssembly, configuration.RootInstance,
configuration.BaseUri, configuration.DesignMode, configuration.UseCompiledBindingsByDefault);
/// <summary>
/// Parse XAML from a string.

32
src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs

@ -150,12 +150,12 @@ namespace Avalonia.Markup.Xaml.XamlIl
}
static object LoadSre(string xaml, Assembly localAssembly, object rootInstance, Uri uri, bool isDesignMode)
static object LoadSre(string xaml, Assembly localAssembly, object rootInstance, Uri uri, bool isDesignMode, bool useCompiledBindingsByDefault)
{
var success = false;
try
{
var rv = LoadSreCore(xaml, localAssembly, rootInstance, uri, isDesignMode);
var rv = LoadSreCore(xaml, localAssembly, rootInstance, uri, isDesignMode, useCompiledBindingsByDefault);
success = true;
return rv;
}
@ -167,7 +167,7 @@ namespace Avalonia.Markup.Xaml.XamlIl
}
static object LoadSreCore(string xaml, Assembly localAssembly, object rootInstance, Uri uri, bool isDesignMode)
static object LoadSreCore(string xaml, Assembly localAssembly, object rootInstance, Uri uri, bool isDesignMode, bool useCompiledBindingsByDefault)
{
InitializeSre();
@ -178,15 +178,14 @@ namespace Avalonia.Markup.Xaml.XamlIl
var clrPropertyBuilder = tb.DefineNestedType("ClrProperties_" + Guid.NewGuid().ToString("N"));
var indexerClosureType = _sreBuilder.DefineType("IndexerClosure_" + Guid.NewGuid().ToString("N"));
var trampolineBuilder = _sreBuilder.DefineType("Trampolines_" + Guid.NewGuid().ToString("N"));
var compiler = new AvaloniaXamlIlCompiler(new AvaloniaXamlIlCompilerConfiguration(_sreTypeSystem, asm,
_sreMappings, _sreXmlns, AvaloniaXamlIlLanguage.CustomValueConverter,
new XamlIlClrPropertyInfoEmitter(_sreTypeSystem.CreateTypeBuilder(clrPropertyBuilder)),
new XamlIlPropertyInfoAccessorFactoryEmitter(_sreTypeSystem.CreateTypeBuilder(indexerClosureType)),
new XamlIlTrampolineBuilder(_sreTypeSystem.CreateTypeBuilder(trampolineBuilder))),
_sreEmitMappings,
_sreContextType) { EnableIlVerification = true };
_sreContextType) { EnableIlVerification = true, DefaultCompileBindings = useCompiledBindingsByDefault };
IXamlType overrideType = null;
if (rootInstance != null)
@ -204,8 +203,8 @@ namespace Avalonia.Markup.Xaml.XamlIl
return LoadOrPopulate(created, rootInstance);
}
#endif
static object LoadOrPopulate(Type created, object rootInstance)
static object LoadOrPopulate(Type created, object rootInstance)
{
var isp = Expression.Parameter(typeof(IServiceProvider));
@ -251,15 +250,15 @@ namespace Avalonia.Markup.Xaml.XamlIl
}
public static object Load(Stream stream, Assembly localAssembly, object rootInstance, Uri uri,
bool isDesignMode)
bool isDesignMode, bool useCompiledBindingsByDefault)
{
string xaml;
using (var sr = new StreamReader(stream))
xaml = sr.ReadToEnd();
#if RUNTIME_XAML_CECIL
return LoadCecil(xaml, localAssembly, rootInstance, uri);
return LoadCecil(xaml, localAssembly, rootInstance, uri, useCompiledBindingsByDefault);
#else
return LoadSre(xaml, localAssembly, rootInstance, uri, isDesignMode);
return LoadSre(xaml, localAssembly, rootInstance, uri, isDesignMode, useCompiledBindingsByDefault);
#endif
}
@ -293,7 +292,7 @@ namespace Avalonia.Markup.Xaml.XamlIl
}
private static Dictionary<string, Type> _cecilGeneratedCache = new Dictionary<string, Type>();
static object LoadCecil(string xaml, Assembly localAssembly, object rootInstance, Uri uri)
static object LoadCecil(string xaml, Assembly localAssembly, object rootInstance, Uri uri, bool useCompiledBindingsByDefault)
{
if (uri == null)
throw new InvalidOperationException("Please, go away");
@ -303,8 +302,6 @@ namespace Avalonia.Markup.Xaml.XamlIl
{
overrideType = _cecilTypeSystem.GetType(rootInstance.GetType().FullName);
}
var safeUri = uri.ToString()
.Replace(":", "_")
@ -328,13 +325,16 @@ namespace Avalonia.Markup.Xaml.XamlIl
asm.MainModule.Types.Add(contextDef);
var tb = _cecilTypeSystem.CreateTypeBuilder(def);
var compiler = new AvaloniaXamlIlCompiler(new XamlIlTransformerConfiguration(_cecilTypeSystem,
localAssembly == null ? null : _cecilTypeSystem.FindAssembly(localAssembly.GetName().Name),
_cecilMappings, XamlIlXmlnsMappings.Resolve(_cecilTypeSystem, _cecilMappings),
AvaloniaXamlIlLanguage.CustomValueConverter),
_cecilEmitMappings,
_cecilTypeSystem.CreateTypeBuilder(contextDef));
_cecilTypeSystem.CreateTypeBuilder(contextDef))
{
DefaultCompileBindings = useCompiledBindingsByDefault
};
compiler.ParseAndCompile(xaml, uri.ToString(), tb, overrideType);
var asmPath = Path.Combine(_cecilEmitDir, safeUri + ".dll");
using(var f = File.Create(asmPath))

1
src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj

@ -44,6 +44,7 @@
<Compile Include="MarkupExtensions\ReflectionBindingExtension.cs" />
<Compile Include="MarkupExtensions\RelativeSourceExtension.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="RuntimeXamlLoaderConfiguration.cs" />
<Compile Include="Styling\StyleInclude.cs" />
<Compile Include="Templates\ControlTemplate.cs" />
<Compile Include="Templates\DataTemplate.cs" />

11
src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs

@ -1,8 +1,5 @@
using System;
using System.IO;
using System.Reflection;
using System.Text;
using Avalonia.Markup.Xaml.XamlIl;
using Avalonia.Platform;
namespace Avalonia.Markup.Xaml
@ -14,7 +11,7 @@ namespace Avalonia.Markup.Xaml
{
public interface IRuntimeXamlLoader
{
object Load(Stream stream, Assembly localAsm, object o, Uri baseUri, bool designMode);
object Load(Stream stream, RuntimeXamlLoaderConfiguration configuration);
}
/// <summary>
@ -67,7 +64,11 @@ namespace Avalonia.Markup.Xaml
using (var stream = asset.stream)
{
var absoluteUri = uri.IsAbsoluteUri ? uri : new Uri(baseUri, uri);
return runtimeLoader.Load(stream, asset.assembly, null, absoluteUri, false);
return runtimeLoader.Load(stream, new RuntimeXamlLoaderConfiguration
{
LocalAssembly = asset.assembly,
BaseUri = absoluteUri
});
}
}

36
src/Markup/Avalonia.Markup.Xaml/RuntimeXamlLoaderConfiguration.cs

@ -0,0 +1,36 @@
using System;
using System.Reflection;
namespace Avalonia.Markup.Xaml;
#nullable enable
public class RuntimeXamlLoaderConfiguration
{
/// <summary>
/// The URI of the XAML being loaded.
/// </summary>
public Uri? BaseUri { get; set; }
/// <summary>
/// Default assembly for clr-namespace:.
/// </summary>
public Assembly? LocalAssembly { get; set; }
/// <summary>
/// The optional instance into which the XAML should be loaded.
/// </summary>
public object? RootInstance { get; set; }
/// <summary>
/// Defines is CompiledBinding should be used by default.
/// Default is 'false'.
/// </summary>
public bool UseCompiledBindingsByDefault { get; set; } = false;
/// <summary>
/// Indicates whether the XAML is being loaded in design mode.
/// Default is 'false'.
/// </summary>
public bool DesignMode { get; set; } = false;
}

6
src/tools/Avalonia.Designer.HostApp/DesignXamlLoader.cs

@ -8,9 +8,11 @@ namespace Avalonia.Designer.HostApp
{
class DesignXamlLoader : AvaloniaXamlLoader.IRuntimeXamlLoader
{
public object Load(Stream stream, Assembly localAsm, object o, Uri baseUri, bool designMode)
public object Load(Stream stream, RuntimeXamlLoaderConfiguration configuration)
{
return AvaloniaXamlIlRuntimeCompiler.Load(stream, localAsm, o, baseUri, designMode);
return AvaloniaXamlIlRuntimeCompiler.Load(stream,
configuration.LocalAssembly, configuration.RootInstance, configuration.BaseUri,
configuration.DesignMode, configuration.UseCompiledBindingsByDefault);
}
}
}

21
tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs

@ -1603,6 +1603,27 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
}
}
[Fact]
public void Uses_RuntimeLoader_Configuration_To_Enabled_Compiled()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var xaml = @"
<local:AssignBindingControl xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
xmlns:local='clr-namespace:Avalonia.Markup.Xaml.UnitTests.MarkupExtensions;assembly=Avalonia.Markup.Xaml.UnitTests'
X='{Binding StringProperty, DataType=local:TestDataContext}' />";
var control = (AssignBindingControl)AvaloniaRuntimeXamlLoader.Load(xaml, new RuntimeXamlLoaderConfiguration
{
UseCompiledBindingsByDefault = true
});
var compiledPath = ((CompiledBindingExtension)control.X).Path;
var node = Assert.IsType<PropertyElement>(Assert.Single(compiledPath.Elements));
Assert.Equal(typeof(string), node.Property.PropertyType);
}
}
void Throws(string type, Action cb)
{
try

4
tests/Avalonia.Markup.Xaml.UnitTests/XamlTestBase.cs

@ -20,8 +20,8 @@ namespace Avalonia.Markup.Xaml.UnitTests
class TestXamlLoaderShim : AvaloniaXamlLoader.IRuntimeXamlLoader
{
public object Load(Stream stream, Assembly localAsm, object o, Uri baseUri, bool designMode)
=> AvaloniaRuntimeXamlLoader.Load(stream, localAsm, o, baseUri, designMode);
public object Load(Stream stream, RuntimeXamlLoaderConfiguration configuration)
=> AvaloniaRuntimeXamlLoader.Load(stream, configuration);
}
}
}

Loading…
Cancel
Save