From 7639ebebae746782ac2b8f2472e551ee5105a2fb Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 22 Dec 2021 16:59:51 -0600 Subject: [PATCH 1/6] Add feature switch to enable switching compiled bindings on by default. --- packages/Avalonia/AvaloniaBuildTasks.targets | 6 ++++++ .../CompileAvaloniaXamlTask.cs | 4 +++- .../XamlCompilerTaskExecutor.cs | 13 ++++++------ .../AvaloniaXamlIlRuntimeCompiler.cs | 20 ++++++++++++------- 4 files changed, 29 insertions(+), 14 deletions(-) diff --git a/packages/Avalonia/AvaloniaBuildTasks.targets b/packages/Avalonia/AvaloniaBuildTasks.targets index d43a5c1624..d64c2c5afc 100644 --- a/packages/Avalonia/AvaloniaBuildTasks.targets +++ b/packages/Avalonia/AvaloniaBuildTasks.targets @@ -5,6 +5,7 @@ low <_AvaloniaPatchComInterop Condition="'$(_AvaloniaPatchComInterop)' == ''">false <_AvaloniaSkipXamlCompilation Condition="'$(_AvaloniaSkipXamlCompilation)' == ''">false + false @@ -20,6 +21,10 @@ + + + + !string.IsNullOrWhiteSpace(l)).ToArray(), - ProjectDirectory, OutputPath, VerifyIl, outputImportance, + ProjectDirectory, OutputPath, VerifyIl, DefaultCompileBindings, outputImportance, (SignAssembly && !DelaySign) ? AssemblyOriginatorKeyFile : null, EnableComInteropPatching, SkipXamlCompilation, DebuggerLaunch); if (!res.Success) @@ -72,6 +72,8 @@ namespace Avalonia.Build.Tasks public string OutputPath { get; set; } public bool VerifyIl { get; set; } + + public bool DefaultCompileBindings { get; set; } public bool EnableComInteropPatching { get; set; } public bool SkipXamlCompilation { get; set; } diff --git a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs index 593d79471e..52b58c2318 100644 --- a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs +++ b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs @@ -39,15 +39,15 @@ namespace Avalonia.Build.Tasks public static CompileResult Compile(IBuildEngine engine, string input, string[] references, string projectDirectory, - string output, bool verifyIl, MessageImportance logImportance, string strongNameKey, bool patchCom, + string output, bool verifyIl, bool defaultCompileBindings, MessageImportance logImportance, string strongNameKey, bool patchCom, bool skipXamlCompilation) { - return Compile(engine, input, references, projectDirectory, output, verifyIl, logImportance, strongNameKey, patchCom, skipXamlCompilation, debuggerLaunch:false); + return Compile(engine, input, references, projectDirectory, output, verifyIl, defaultCompileBindings, logImportance, strongNameKey, patchCom, skipXamlCompilation, debuggerLaunch:false); } internal static CompileResult Compile(IBuildEngine engine, string input, string[] references, string projectDirectory, - string output, bool verifyIl, MessageImportance logImportance, string strongNameKey, bool patchCom, bool skipXamlCompilation, bool debuggerLaunch) + string output, bool verifyIl, bool defaultCompileBindings, MessageImportance logImportance, string strongNameKey, bool patchCom, bool skipXamlCompilation, bool debuggerLaunch) { var typeSystem = new CecilTypeSystem(references .Where(r => !r.ToLowerInvariant().EndsWith("avalonia.build.tasks.dll")) @@ -57,7 +57,7 @@ namespace Avalonia.Build.Tasks if (!skipXamlCompilation) { - var compileRes = CompileCore(engine, typeSystem, projectDirectory, verifyIl, logImportance, debuggerLaunch); + var compileRes = CompileCore(engine, typeSystem, projectDirectory, verifyIl, defaultCompileBindings, logImportance, debuggerLaunch); if (compileRes == null && !patchCom) return new CompileResult(true); if (compileRes == false) @@ -78,7 +78,8 @@ namespace Avalonia.Build.Tasks } static bool? CompileCore(IBuildEngine engine, CecilTypeSystem typeSystem, - string projectDirectory, bool verifyIl, + string projectDirectory, bool verifyIl, + bool defaultCompileBindings, MessageImportance logImportance , bool debuggerLaunch = false) { @@ -143,7 +144,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")) diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs index ece90762cb..be293331df 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs @@ -24,6 +24,8 @@ namespace Avalonia.Markup.Xaml.XamlIl { static class AvaloniaXamlIlRuntimeCompiler { + private const string UseCompileBindingsByDefaultConfigSwitch = "Avalonia.UseCompiledBindingsByDefault"; + #if !RUNTIME_XAML_CECIL private static SreTypeSystem _sreTypeSystem; private static Type _ignoresAccessChecksFromAttribute; @@ -178,13 +180,14 @@ namespace Avalonia.Markup.Xaml.XamlIl var clrPropertyBuilder = tb.DefineNestedType("ClrProperties_" + Guid.NewGuid().ToString("N")); var indexerClosureType = _sreBuilder.DefineType("IndexerClosure_" + Guid.NewGuid().ToString("N")); + bool compileBindingsByDefault = AppContext.TryGetSwitch(UseCompileBindingsByDefaultConfigSwitch, out var compileBindingsSwitchValue) && compileBindingsSwitchValue; + var compiler = new AvaloniaXamlIlCompiler(new AvaloniaXamlIlCompilerConfiguration(_sreTypeSystem, asm, _sreMappings, _sreXmlns, AvaloniaXamlIlLanguage.CustomValueConverter, new XamlIlClrPropertyInfoEmitter(_sreTypeSystem.CreateTypeBuilder(clrPropertyBuilder)), new XamlIlPropertyInfoAccessorFactoryEmitter(_sreTypeSystem.CreateTypeBuilder(indexerClosureType))), _sreEmitMappings, - _sreContextType) { EnableIlVerification = true }; - + _sreContextType) { EnableIlVerification = true, DefaultCompileBindings = compileBindingsByDefault }; IXamlType overrideType = null; if (rootInstance != null) @@ -200,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)); @@ -299,8 +302,6 @@ namespace Avalonia.Markup.Xaml.XamlIl { overrideType = _cecilTypeSystem.GetType(rootInstance.GetType().FullName); } - - var safeUri = uri.ToString() .Replace(":", "_") @@ -324,13 +325,18 @@ namespace Avalonia.Markup.Xaml.XamlIl asm.MainModule.Types.Add(contextDef); var tb = _cecilTypeSystem.CreateTypeBuilder(def); + + bool compileBindingsByDefault = AppContext.TryGetSwitch(UseCompileBindingsByDefaultConfigSwitch, out var compileBindingsSwitchValue) && compileBindingsSwitchValue; 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 = compileBindingsByDefault + }; compiler.ParseAndCompile(xaml, uri.ToString(), tb, overrideType); var asmPath = Path.Combine(_cecilEmitDir, safeUri + ".dll"); using(var f = File.Create(asmPath)) From 55539582383ac4eb899833a4249814140f68c64c Mon Sep 17 00:00:00 2001 From: Max Katz Date: Sun, 13 Nov 2022 23:07:46 -0500 Subject: [PATCH 2/6] Remove RuntimeHostConfigurationOption --- packages/Avalonia/AvaloniaBuildTasks.targets | 4 -- .../XamlCompilerTaskExecutor.cs | 2 +- .../AvaloniaRuntimeXamlLoader.cs | 60 ++++++++++++++++++- .../AvaloniaXamlIlRuntimeCompiler.cs | 26 ++++---- .../CompiledBindingExtensionTests.cs | 21 +++++++ 5 files changed, 91 insertions(+), 22 deletions(-) diff --git a/packages/Avalonia/AvaloniaBuildTasks.targets b/packages/Avalonia/AvaloniaBuildTasks.targets index 53b668b523..1f56bba3bd 100644 --- a/packages/Avalonia/AvaloniaBuildTasks.targets +++ b/packages/Avalonia/AvaloniaBuildTasks.targets @@ -20,10 +20,6 @@ - - - - + /// Loads XAML from a string. + /// + /// The string containing the XAML. + /// Xaml loader configuration. + /// The loaded object. + public static object Load(string xaml, RuntimeXamlLoaderConfiguration configuration) + { + Contract.Requires(xaml != null); + + using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(xaml))) + { + return Load(stream, configuration); + } + } /// /// Loads XAML from a stream. @@ -38,7 +56,17 @@ namespace Avalonia.Markup.Xaml /// The loaded object. 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); + + /// + /// Loads XAML from a stream. + /// + /// The stream containing the XAML. + /// Xaml loader configuration. + /// The loaded object. + public static object Load(Stream stream, RuntimeXamlLoaderConfiguration configuration) + => AvaloniaXamlIlRuntimeCompiler.Load(stream, configuration.LocalAssembly, configuration.RootInstance, + configuration.BaseUri, configuration.DesignMode, configuration.UseCompiledBindingsByDefault); /// /// Parse XAML from a string. @@ -60,4 +88,34 @@ namespace Avalonia.Markup.Xaml => (T)Parse(xaml, localAssembly); } + + public class RuntimeXamlLoaderConfiguration + { + /// + /// The URI of the XAML being loaded. + /// + public Uri? BaseUri { get; set; } + + /// + /// Default assembly for clr-namespace:. + /// + public Assembly LocalAssembly { get; set; } + + /// + /// The optional instance into which the XAML should be loaded. + /// + public object? RootInstance { get; set; } + + /// + /// Defines is CompiledBinding should be used by default. + /// Default is 'false'. + /// + public bool UseCompiledBindingsByDefault { get; set; } = false; + + /// + /// Indicates whether the XAML is being loaded in design mode. + /// Default is 'false'. + /// + public bool DesignMode { get; set; } = false; + } } diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs index 3c7fc157a4..b4b258e53e 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs @@ -24,8 +24,6 @@ namespace Avalonia.Markup.Xaml.XamlIl { static class AvaloniaXamlIlRuntimeCompiler { - private const string UseCompileBindingsByDefaultConfigSwitch = "Avalonia.UseCompiledBindingsByDefault"; - #if !RUNTIME_XAML_CECIL private static SreTypeSystem _sreTypeSystem; private static Type _ignoresAccessChecksFromAttribute; @@ -152,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; } @@ -169,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(); @@ -180,16 +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")); - - bool compileBindingsByDefault = AppContext.TryGetSwitch(UseCompileBindingsByDefaultConfigSwitch, out var compileBindingsSwitchValue) && compileBindingsSwitchValue; - + 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, DefaultCompileBindings = compileBindingsByDefault }; + _sreContextType) { EnableIlVerification = true, DefaultCompileBindings = useCompiledBindingsByDefault }; IXamlType overrideType = null; if (rootInstance != null) @@ -254,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 } @@ -296,7 +292,7 @@ namespace Avalonia.Markup.Xaml.XamlIl } private static Dictionary _cecilGeneratedCache = new Dictionary(); - 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"); @@ -330,8 +326,6 @@ namespace Avalonia.Markup.Xaml.XamlIl var tb = _cecilTypeSystem.CreateTypeBuilder(def); - bool compileBindingsByDefault = AppContext.TryGetSwitch(UseCompileBindingsByDefaultConfigSwitch, out var compileBindingsSwitchValue) && compileBindingsSwitchValue; - var compiler = new AvaloniaXamlIlCompiler(new XamlIlTransformerConfiguration(_cecilTypeSystem, localAssembly == null ? null : _cecilTypeSystem.FindAssembly(localAssembly.GetName().Name), _cecilMappings, XamlIlXmlnsMappings.Resolve(_cecilTypeSystem, _cecilMappings), @@ -339,7 +333,7 @@ namespace Avalonia.Markup.Xaml.XamlIl _cecilEmitMappings, _cecilTypeSystem.CreateTypeBuilder(contextDef)) { - DefaultCompileBindings = compileBindingsByDefault + DefaultCompileBindings = useCompiledBindingsByDefault }; compiler.ParseAndCompile(xaml, uri.ToString(), tb, overrideType); var asmPath = Path.Combine(_cecilEmitDir, safeUri + ".dll"); diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs index 215ae4d54f..87e4c77686 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs @@ -1569,6 +1569,27 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions } } + [Fact] + public void Uses_RuntimeLoader_Configuration_To_Enabled_Compiled() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" +"; + var control = (AssignBindingControl)AvaloniaRuntimeXamlLoader.Load(xaml, new RuntimeXamlLoaderConfiguration + { + UseCompiledBindingsByDefault = true + }); + var compiledPath = ((CompiledBindingExtension)control.X).Path; + + var node = Assert.IsType(Assert.Single(compiledPath.Elements)); + Assert.Equal(typeof(string), node.Property.PropertyType); + } + } + void Throws(string type, Action cb) { try From dc0c7e756aaf95bd5efd0d2a0e00648544751b96 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Sun, 13 Nov 2022 23:42:46 -0500 Subject: [PATCH 3/6] Respect AvaloniaUseCompiledBindingsByDefault in previewer --- packages/Avalonia/AvaloniaBuildTasks.targets | 12 ++++++- .../DesignWindowLoader.cs | 11 +++++- .../AvaloniaRuntimeXamlLoader.cs | 30 ---------------- .../Avalonia.Markup.Xaml.csproj | 1 + .../AvaloniaXamlLoader.cs | 11 +++--- .../RuntimeXamlLoaderConfiguration.cs | 34 +++++++++++++++++++ .../DesignXamlLoader.cs | 6 ++-- .../XamlTestBase.cs | 4 +-- 8 files changed, 68 insertions(+), 41 deletions(-) create mode 100644 src/Markup/Avalonia.Markup.Xaml/RuntimeXamlLoaderConfiguration.cs diff --git a/packages/Avalonia/AvaloniaBuildTasks.targets b/packages/Avalonia/AvaloniaBuildTasks.targets index 1f56bba3bd..961992ebbc 100644 --- a/packages/Avalonia/AvaloniaBuildTasks.targets +++ b/packages/Avalonia/AvaloniaBuildTasks.targets @@ -44,7 +44,17 @@ $(BuildAvaloniaResourcesDependsOn);AddAvaloniaResources;ResolveReferences;_GenerateAvaloniaResourcesDependencyCache - + + + + + <_Parameter1>AvaloniaUseCompiledBindingsByDefault + <_Parameter2>$(AvaloniaUseCompiledBindingsByDefault) + + + + diff --git a/src/Avalonia.DesignerSupport/DesignWindowLoader.cs b/src/Avalonia.DesignerSupport/DesignWindowLoader.cs index 811f9c7baa..1e1b550a15 100644 --- a/src/Avalonia.DesignerSupport/DesignWindowLoader.cs +++ b/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() + .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 tempBool) && tempBool + }); var style = loaded as IStyle; var resources = loaded as ResourceDictionary; if (style != null) diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaRuntimeXamlLoader.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaRuntimeXamlLoader.cs index 65baa8c486..c8464d83af 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaRuntimeXamlLoader.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaRuntimeXamlLoader.cs @@ -88,34 +88,4 @@ namespace Avalonia.Markup.Xaml => (T)Parse(xaml, localAssembly); } - - public class RuntimeXamlLoaderConfiguration - { - /// - /// The URI of the XAML being loaded. - /// - public Uri? BaseUri { get; set; } - - /// - /// Default assembly for clr-namespace:. - /// - public Assembly LocalAssembly { get; set; } - - /// - /// The optional instance into which the XAML should be loaded. - /// - public object? RootInstance { get; set; } - - /// - /// Defines is CompiledBinding should be used by default. - /// Default is 'false'. - /// - public bool UseCompiledBindingsByDefault { get; set; } = false; - - /// - /// Indicates whether the XAML is being loaded in design mode. - /// Default is 'false'. - /// - public bool DesignMode { get; set; } = false; - } } diff --git a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj index f5bf14c2a4..070f0c1cc3 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj +++ b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj @@ -44,6 +44,7 @@ + diff --git a/src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs b/src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs index e5c6b72d12..578af64abb 100644 --- a/src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs +++ b/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); } /// @@ -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 + }); } } diff --git a/src/Markup/Avalonia.Markup.Xaml/RuntimeXamlLoaderConfiguration.cs b/src/Markup/Avalonia.Markup.Xaml/RuntimeXamlLoaderConfiguration.cs new file mode 100644 index 0000000000..aae19e67a6 --- /dev/null +++ b/src/Markup/Avalonia.Markup.Xaml/RuntimeXamlLoaderConfiguration.cs @@ -0,0 +1,34 @@ +using System; +using System.Reflection; + +namespace Avalonia.Markup.Xaml; + +public class RuntimeXamlLoaderConfiguration +{ + /// + /// The URI of the XAML being loaded. + /// + public Uri? BaseUri { get; set; } + + /// + /// Default assembly for clr-namespace:. + /// + public Assembly LocalAssembly { get; set; } + + /// + /// The optional instance into which the XAML should be loaded. + /// + public object? RootInstance { get; set; } + + /// + /// Defines is CompiledBinding should be used by default. + /// Default is 'false'. + /// + public bool UseCompiledBindingsByDefault { get; set; } = false; + + /// + /// Indicates whether the XAML is being loaded in design mode. + /// Default is 'false'. + /// + public bool DesignMode { get; set; } = false; +} diff --git a/src/tools/Avalonia.Designer.HostApp/DesignXamlLoader.cs b/src/tools/Avalonia.Designer.HostApp/DesignXamlLoader.cs index 7af29a56a1..7009151998 100644 --- a/src/tools/Avalonia.Designer.HostApp/DesignXamlLoader.cs +++ b/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); } } } diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/XamlTestBase.cs b/tests/Avalonia.Markup.Xaml.UnitTests/XamlTestBase.cs index 2bc82d1353..2fc4867b35 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/XamlTestBase.cs +++ b/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); } } } From 0d60f972f9752cafb82ddd5079a682e8430bfcae Mon Sep 17 00:00:00 2001 From: Max Katz Date: Sun, 13 Nov 2022 23:46:30 -0500 Subject: [PATCH 4/6] Fix nullable --- .../Avalonia.Markup.Xaml.Loader/AvaloniaRuntimeXamlLoader.cs | 2 -- .../Avalonia.Markup.Xaml/RuntimeXamlLoaderConfiguration.cs | 4 +++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaRuntimeXamlLoader.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaRuntimeXamlLoader.cs index c8464d83af..9393bb0aa4 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaRuntimeXamlLoader.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaRuntimeXamlLoader.cs @@ -4,8 +4,6 @@ using System.Reflection; using System.Text; using Avalonia.Markup.Xaml.XamlIl; -#nullable enable - namespace Avalonia.Markup.Xaml { public static class AvaloniaRuntimeXamlLoader diff --git a/src/Markup/Avalonia.Markup.Xaml/RuntimeXamlLoaderConfiguration.cs b/src/Markup/Avalonia.Markup.Xaml/RuntimeXamlLoaderConfiguration.cs index aae19e67a6..9caf94fba6 100644 --- a/src/Markup/Avalonia.Markup.Xaml/RuntimeXamlLoaderConfiguration.cs +++ b/src/Markup/Avalonia.Markup.Xaml/RuntimeXamlLoaderConfiguration.cs @@ -3,6 +3,8 @@ using System.Reflection; namespace Avalonia.Markup.Xaml; +#nullable enable + public class RuntimeXamlLoaderConfiguration { /// @@ -13,7 +15,7 @@ public class RuntimeXamlLoaderConfiguration /// /// Default assembly for clr-namespace:. /// - public Assembly LocalAssembly { get; set; } + public Assembly? LocalAssembly { get; set; } /// /// The optional instance into which the XAML should be loaded. From 66ad721763ffee9ee8424028195b6cf1fad95472 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Mon, 14 Nov 2022 00:58:54 -0500 Subject: [PATCH 5/6] Inject AvaloniaUseCompiledBindingsByDefault from Cecil --- packages/Avalonia/AvaloniaBuildTasks.targets | 10 ---- .../BuildEngineErrorCode.cs | 2 + .../XamlCompilerTaskExecutor.cs | 54 ++++++++++++------- 3 files changed, 38 insertions(+), 28 deletions(-) diff --git a/packages/Avalonia/AvaloniaBuildTasks.targets b/packages/Avalonia/AvaloniaBuildTasks.targets index 961992ebbc..01bf303a20 100644 --- a/packages/Avalonia/AvaloniaBuildTasks.targets +++ b/packages/Avalonia/AvaloniaBuildTasks.targets @@ -45,16 +45,6 @@ $(BuildAvaloniaResourcesDependsOn);AddAvaloniaResources;ResolveReferences;_GenerateAvaloniaResourcesDependencyCache - - - - <_Parameter1>AvaloniaUseCompiledBindingsByDefault - <_Parameter2>$(AvaloniaUseCompiledBindingsByDefault) - - - - diff --git a/src/Avalonia.Build.Tasks/BuildEngineErrorCode.cs b/src/Avalonia.Build.Tasks/BuildEngineErrorCode.cs index 3b8a390881..43365346b3 100644 --- a/src/Avalonia.Build.Tasks/BuildEngineErrorCode.cs +++ b/src/Avalonia.Build.Tasks/BuildEngineErrorCode.cs @@ -5,5 +5,7 @@ namespace Avalonia.Build.Tasks InvalidXAML = 1, DuplicateXClass = 2, LegacyResmScheme = 3, + + Unknown = 9999 } } diff --git a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs index 8d0addc717..83def84989 100644 --- a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs +++ b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs @@ -49,29 +49,37 @@ namespace Avalonia.Build.Tasks string projectDirectory, 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, defaultCompileBindings, 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, @@ -115,6 +123,16 @@ namespace Avalonia.Build.Tasks // Nothing to do return null; + if (asm.MainModule.TryGetTypeReference("System.Reflection.AssemblyMetadataAttribute", out var asmMetadata)) + { + var ctor = asm.MainModule.ImportReference(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); From e1c7e8b5ecf0d6ef08a4abcfff201ab71b295a2d Mon Sep 17 00:00:00 2001 From: Max Katz Date: Mon, 14 Nov 2022 01:34:46 -0500 Subject: [PATCH 6/6] Apply changes after review --- src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs | 5 ++--- src/Avalonia.DesignerSupport/DesignWindowLoader.cs | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs index 83def84989..3493fc06ed 100644 --- a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs +++ b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs @@ -122,10 +122,9 @@ namespace Avalonia.Build.Tasks if (avares.Resources.Count(CheckXamlName) == 0) // Nothing to do return null; - - if (asm.MainModule.TryGetTypeReference("System.Reflection.AssemblyMetadataAttribute", out var asmMetadata)) + if (typeSystem.FindType("System.Reflection.AssemblyMetadataAttribute") is {} asmMetadata) { - var ctor = asm.MainModule.ImportReference(asmMetadata.Resolve() + 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"); diff --git a/src/Avalonia.DesignerSupport/DesignWindowLoader.cs b/src/Avalonia.DesignerSupport/DesignWindowLoader.cs index 1e1b550a15..9a901f909a 100644 --- a/src/Avalonia.DesignerSupport/DesignWindowLoader.cs +++ b/src/Avalonia.DesignerSupport/DesignWindowLoader.cs @@ -43,7 +43,7 @@ namespace Avalonia.DesignerSupport LocalAssembly = localAsm, BaseUri = baseUri, DesignMode = true, - UseCompiledBindingsByDefault = bool.TryParse(useCompiledBindings, out var tempBool) && tempBool + UseCompiledBindingsByDefault = bool.TryParse(useCompiledBindings, out var parsedValue ) && parsedValue }); var style = loaded as IStyle; var resources = loaded as ResourceDictionary;