From 434cbac0552407c3251e02dd50ddcf7cf061b2aa Mon Sep 17 00:00:00 2001 From: Julien Lebosquain Date: Thu, 29 Feb 2024 22:19:39 +0100 Subject: [PATCH] Shared XAML setters cache (#14717) --- .../XamlCompilerTaskExecutor.cs | 80 ++++++++++--------- .../AvaloniaXamlIlRuntimeCompiler.cs | 5 +- .../AvaloniaXamlIlCompiler.cs | 15 ++-- ...aXamlIlOptionMarkupExtensionTransformer.cs | 2 + ...mlIlTransformInstanceAttachedProperties.cs | 2 + .../XamlDocumentTypeBuilderProvider.cs | 9 ++- .../XamlIlBindingPathHelper.cs | 8 +- .../Avalonia.Markup.Xaml.Loader/xamlil.github | 2 +- .../Compiler/MiniCompiler.cs | 9 +-- .../Compiler/RoslynTypeSystem.cs | 13 ++- 10 files changed, 84 insertions(+), 61 deletions(-) diff --git a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs index 4a5b8b7ab2..66b1bab004 100644 --- a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs +++ b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs @@ -18,6 +18,7 @@ using FieldAttributes = Mono.Cecil.FieldAttributes; using MethodAttributes = Mono.Cecil.MethodAttributes; using TypeAttributes = Mono.Cecil.TypeAttributes; using XamlX.IL; +using XamlX.IL.Emitters; namespace Avalonia.Build.Tasks { @@ -126,7 +127,7 @@ namespace Avalonia.Build.Tasks System.Threading.Thread.Sleep(100); } - time.Stop(); + time.Stop(); if (time.Elapsed >= timeout) { engine.LogMessage("Wating attach debugger timeout.", MessageImportance.Normal); @@ -137,7 +138,7 @@ namespace Avalonia.Build.Tasks engine.LogMessage("Debugging cancelled.", MessageImportance.Normal); } } - + // Some transformers might need to parse "avares://" Uri. AssetLoader.RegisterResUriParsers(); @@ -157,15 +158,21 @@ namespace Avalonia.Build.Tasks asm.CustomAttributes.Add(new CustomAttribute(ctor) { ConstructorArguments = { arg1, arg2 } }); } - var clrPropertiesDef = new TypeDefinition(CompiledAvaloniaXamlNamespace, "XamlIlHelpers", - TypeAttributes.Class, asm.MainModule.TypeSystem.Object); - asm.MainModule.Types.Add(clrPropertiesDef); - var indexerAccessorClosure = new TypeDefinition(CompiledAvaloniaXamlNamespace, "!IndexerAccessorFactoryClosure", - TypeAttributes.Class, asm.MainModule.TypeSystem.Object); - asm.MainModule.Types.Add(indexerAccessorClosure); - var trampolineBuilder = new TypeDefinition(CompiledAvaloniaXamlNamespace, "XamlIlTrampolines", - TypeAttributes.Class, asm.MainModule.TypeSystem.Object); - asm.MainModule.Types.Add(trampolineBuilder); + TypeDefinition AddClass(string name, TypeAttributes extraAttributes = 0) + { + var typeDef = new TypeDefinition( + CompiledAvaloniaXamlNamespace, + name, + TypeAttributes.Class | extraAttributes, + asm.MainModule.TypeSystem.Object); + asm.MainModule.Types.Add(typeDef); + return typeDef; + } + + var clrPropertiesDef = AddClass("XamlIlHelpers"); + var indexerAccessorClosure = AddClass("!IndexerAccessorFactoryClosure"); + var trampolineBuilder = AddClass("XamlIlTrampolines"); + var dynamicSettersBuilder = typeSystem.CreateTypeBuilder(AddClass("XamlDynamicSetters")); var (xamlLanguage , emitConfig) = AvaloniaXamlIlLanguage.Configure(typeSystem); var diagnostics = new List(); @@ -194,14 +201,17 @@ namespace Avalonia.Build.Tasks diagnosticsHandler); - var contextDef = new TypeDefinition(CompiledAvaloniaXamlNamespace, "XamlIlContext", - TypeAttributes.Class, asm.MainModule.TypeSystem.Object); - asm.MainModule.Types.Add(contextDef); + var contextDef = AddClass("XamlIlContext"); var contextClass = XamlILContextDefinition.GenerateContextClass(typeSystem.CreateTypeBuilder(contextDef), typeSystem, xamlLanguage, emitConfig); - var compiler = new AvaloniaXamlIlCompiler(compilerConfig, emitConfig, contextClass) { EnableIlVerification = verifyIl, DefaultCompileBindings = defaultCompileBindings }; + var compiler = new AvaloniaXamlIlCompiler(compilerConfig, emitConfig, contextClass) + { + EnableIlVerification = verifyIl, + DefaultCompileBindings = defaultCompileBindings, + DynamicSetterContainerProvider = new DefaultXamlDynamicSetterContainerProvider(dynamicSettersBuilder) + }; var editorBrowsableAttribute = typeSystem .GetTypeReference(typeSystem.FindType("System.ComponentModel.EditorBrowsableAttribute")) @@ -215,10 +225,8 @@ namespace Avalonia.Build.Tasks typeSystem.GetTypeReference(runtimeHelpers).Resolve().Methods .First(x => x.Name == "CreateRootServiceProviderV3")); var serviceProviderType = createRootServiceProviderMethod.ReturnType; - - var loaderDispatcherDef = new TypeDefinition(CompiledAvaloniaXamlNamespace, "!XamlLoader", - TypeAttributes.Class | TypeAttributes.Public, asm.MainModule.TypeSystem.Object); + var loaderDispatcherDef = AddClass("!XamlLoader", TypeAttributes.Public); loaderDispatcherDef.CustomAttributes.Add(new CustomAttribute(editorBrowsableCtor) { @@ -257,7 +265,6 @@ namespace Avalonia.Build.Tasks }; loaderDispatcherDef.Methods.Add(loaderDispatcherMethod); loaderDispatcherDef.Methods.Add(loaderDispatcherMethodOld); - asm.MainModule.Types.Add(loaderDispatcherDef); var stringEquals = asm.MainModule.ImportReference(asm.MainModule.TypeSystem.String.Resolve().Methods.First( @@ -270,17 +277,15 @@ namespace Avalonia.Build.Tasks bool CompileGroup(IResourceGroup group) { - var typeDef = new TypeDefinition(CompiledAvaloniaXamlNamespace, "!"+ group.Name, - TypeAttributes.Class | TypeAttributes.Public, asm.MainModule.TypeSystem.Object); + var typeDef = AddClass("!" + group.Name, TypeAttributes.Public); var transformFailed = false; typeDef.CustomAttributes.Add(new CustomAttribute(editorBrowsableCtor) { ConstructorArguments = {new CustomAttributeArgument(editorBrowsableCtor.Parameters[0].ParameterType, 1)} }); - asm.MainModule.Types.Add(typeDef); var builder = typeSystem.CreateTypeBuilder(typeDef); - + IReadOnlyCollection parsedXamlDocuments = new List(); foreach (var res in group.Resources.Where(CheckXamlName).OrderBy(x => x.FilePath.ToLowerInvariant())) { @@ -325,7 +330,7 @@ namespace Avalonia.Build.Tasks else throw new XamlParseException("Invalid value for x:ClassModifier. Expected value are: Public, NotPublic (internal).", classModifierDirective); } - + var classDirective = initialRoot.Children.OfType() .FirstOrDefault(d => d.Namespace == XamlNamespaces.Xaml2006 && d.Name == "Class"); IXamlType classType = null; @@ -348,7 +353,7 @@ namespace Avalonia.Build.Tasks "XAML file x:ClassModifier doesn't match the x:Class type modifiers.", precompileDirective); } - + compiler.OverrideRootType(parsed, new XamlAstClrTypeReference(classDirective, classType, false)); initialRoot.Children.Remove(classDirective); @@ -380,6 +385,7 @@ namespace Avalonia.Build.Tasks classTypeDefinition == null && classModifierPublic.Value ? XamlVisibility.Public : XamlVisibility.Private), + buildName == null ? null : builder, buildName == null ? null : compiler.DefineBuildMethod( @@ -407,7 +413,7 @@ namespace Avalonia.Build.Tasks transformFailed = true; engine.LogError(AvaloniaXamlDiagnosticCodes.TransformError, "", e); } - + var hasAnyError = ReportDiagnostics(engine, diagnostics) || transformFailed; if (hasAnyError) { @@ -426,22 +432,20 @@ namespace Avalonia.Build.Tasks var parsed = document.XamlDocument; var classType = document.ClassType; - var populateBuilder = document.TypeBuilderProvider.TypeBuilder; + var populateBuilder = document.TypeBuilderProvider.PopulateDeclaringType; try { var classTypeDefinition = classType == null ? null : typeSystem.GetTypeReference(classType).Resolve(); - compiler.Compile(parsed, + compiler.Compile(parsed, contextClass, document.TypeBuilderProvider.PopulateMethod, + populateBuilder, document.TypeBuilderProvider.BuildMethod, + builder, builder.DefineSubType(compilerConfig.WellKnownTypes.Object, "NamespaceInfo:" + res.Name, XamlVisibility.Public), - (closureName, closureBaseType) => - populateBuilder.DefineSubType(closureBaseType, closureName, XamlVisibility.Private), - (closureName, returnType, parameterTypes) => - populateBuilder.DefineDelegateSubType(closureName, XamlVisibility.Private, returnType, parameterTypes), res.Uri, res ); @@ -480,10 +484,10 @@ namespace Avalonia.Build.Tasks MethodAttributes.Static | MethodAttributes.Private, asm.MainModule.TypeSystem.Void); if (hasSystemProviderArg) { - trampoline.Parameters.Add(new ParameterDefinition(serviceProviderType)); + trampoline.Parameters.Add(new ParameterDefinition(serviceProviderType)); } trampoline.Parameters.Add(new ParameterDefinition(classTypeDefinition)); - + classTypeDefinition.Methods.Add(trampoline); var regularStart = Instruction.Create(OpCodes.Nop); @@ -498,7 +502,7 @@ namespace Avalonia.Build.Tasks trampoline.Body.Instructions.Add(regularStart); trampoline.Body.Instructions.Add(Instruction.Create(hasSystemProviderArg ? OpCodes.Ldarg_0 : OpCodes.Ldnull)); trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Call, createRootServiceProviderMethod)); - trampoline.Body.Instructions.Add(Instruction.Create(hasSystemProviderArg ? OpCodes.Ldarg_1 : OpCodes.Ldarg_0)); + trampoline.Body.Instructions.Add(Instruction.Create(hasSystemProviderArg ? OpCodes.Ldarg_1 : OpCodes.Ldarg_0)); trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Call, compiledPopulateMethod)); trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Ret)); CopyDebugDocument(trampoline, compiledPopulateMethod); @@ -650,10 +654,10 @@ namespace Avalonia.Build.Tasks dupe.Name += "_dup" + dupeCounter++; } } - + return true; } - + if (avares.Resources.Count(CheckXamlName) != 0) { if (!CompileGroup(avares)) @@ -698,7 +702,7 @@ namespace Avalonia.Build.Tasks foreach (var ogType in compiledTypes) { var wrappedOgType = sourceTypeSystem.TargetAssembly.FindType(ogType.FullName); - + var clrPropertiesDef = new TypeDefinition(ogType.Namespace, ogType.Name, TypeAttributes.Class | TypeAttributes.Public, asm.MainModule.TypeSystem.Object); asm.MainModule.Types.Add(clrPropertiesDef); diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs index bb3427e75a..5ed8a2b883 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs @@ -242,7 +242,7 @@ namespace Avalonia.Markup.Xaml.XamlIl }, CodeMappings = AvaloniaXamlDiagnosticCodes.XamlXDiagnosticCodeToAvalonia }; - + var compiler = new AvaloniaXamlIlCompiler(new AvaloniaXamlIlCompilerConfiguration(_sreTypeSystem, asm, _sreMappings, _sreXmlns, AvaloniaXamlIlLanguage.CustomValueConverter, new XamlIlClrPropertyInfoEmitter(_sreTypeSystem.CreateTypeBuilder(clrPropertyBuilder)), @@ -292,6 +292,7 @@ namespace Avalonia.Markup.Xaml.XamlIl () => new XamlDocumentTypeBuilderProvider( builder, compiler.DefinePopulateMethod(builder, parsed, AvaloniaXamlIlCompiler.PopulateName, XamlVisibility.Public), + document.RootInstance is null ? builder : null, document.RootInstance is null ? compiler.DefineBuildMethod(builder, parsed, AvaloniaXamlIlCompiler.BuildName, XamlVisibility.Public) : null))); @@ -305,7 +306,7 @@ namespace Avalonia.Markup.Xaml.XamlIl var createdTypes = parsedDocuments.Select(document => { compiler.Compile(document.XamlDocument, document.TypeBuilderProvider, document.Uri, document.FileSource); - return _sreTypeSystem.GetType(document.TypeBuilderProvider.TypeBuilder.CreateType()); + return _sreTypeSystem.GetType(document.TypeBuilderProvider.PopulateDeclaringType.CreateType()); }).ToArray(); clrPropertyBuilder.CreateTypeInfo(); diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs index ba23ad21ee..bcec486254 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs @@ -187,14 +187,17 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions public void Compile(XamlDocument document, XamlDocumentTypeBuilderProvider typeBuilderProvider, string baseUri, IFileSource fileSource) { - var tb = typeBuilderProvider.TypeBuilder; - - Compile(document, _contextType, typeBuilderProvider.PopulateMethod, typeBuilderProvider.BuildMethod, + Compile( + document, + _contextType, + typeBuilderProvider.PopulateMethod, + typeBuilderProvider.PopulateDeclaringType, + typeBuilderProvider.BuildMethod, + typeBuilderProvider.BuildDeclaringType, _configuration.TypeMappings.XmlNamespaceInfoProvider == null ? null : - tb.DefineSubType(_configuration.WellKnownTypes.Object, - "__AvaloniaXamlIlNsInfo", XamlVisibility.Private), (name, bt) => tb.DefineSubType(bt, name, XamlVisibility.Private), - (s, returnType, parameters) => tb.DefineDelegateSubType(s, XamlVisibility.Private, returnType, parameters), baseUri, + typeBuilderProvider.PopulateDeclaringType.DefineSubType(_configuration.WellKnownTypes.Object, "__AvaloniaXamlIlNsInfo", XamlVisibility.Private), + baseUri, fileSource); } diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlOptionMarkupExtensionTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlOptionMarkupExtensionTransformer.cs index 06db71f3eb..20f819240e 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlOptionMarkupExtensionTransformer.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlOptionMarkupExtensionTransformer.cs @@ -330,6 +330,8 @@ internal class AvaloniaXamlIlOptionMarkupExtensionTransformer : IXamlAstTransfor public string Name => "ProvideValue"; public bool IsPublic => true; + public bool IsPrivate => false; + public bool IsFamily => false; public bool IsStatic => false; public IXamlType ReturnType => ExtensionNodeContainer.GetReturnType(); public IReadOnlyList Parameters { get; } diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlTransformInstanceAttachedProperties.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlTransformInstanceAttachedProperties.cs index f87e73a783..62b28326d4 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlTransformInstanceAttachedProperties.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlTransformInstanceAttachedProperties.cs @@ -163,6 +163,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers } public AvaloniaAttachedInstanceProperty Parent { get; } public bool IsPublic => true; + public bool IsPrivate => false; + public bool IsFamily => false; public bool IsStatic => true; public string Name { get; protected set; } public IXamlType DeclaringType { get; } diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlDocumentTypeBuilderProvider.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlDocumentTypeBuilderProvider.cs index 4701403fb9..a8f798d1eb 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlDocumentTypeBuilderProvider.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlDocumentTypeBuilderProvider.cs @@ -8,16 +8,19 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions; internal sealed class XamlDocumentTypeBuilderProvider { public XamlDocumentTypeBuilderProvider( - IXamlTypeBuilder typeBuilder, + IXamlTypeBuilder populateDeclaringType, IXamlMethodBuilder populateMethod, + IXamlTypeBuilder? buildDeclaringType, IXamlMethodBuilder? buildMethod) { - TypeBuilder = typeBuilder; + PopulateDeclaringType = populateDeclaringType; PopulateMethod = populateMethod; + BuildDeclaringType = buildDeclaringType; BuildMethod = buildMethod; } - public IXamlTypeBuilder TypeBuilder { get; } + public IXamlTypeBuilder PopulateDeclaringType { get; } public IXamlMethodBuilder PopulateMethod { get; } + public IXamlTypeBuilder? BuildDeclaringType { get; } public IXamlMethodBuilder? BuildMethod { get; } } diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlBindingPathHelper.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlBindingPathHelper.cs index a4fb79c859..51675bbb83 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlBindingPathHelper.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlBindingPathHelper.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Text; using System.Linq; using System.Reflection.Emit; using Avalonia.Markup.Parsers; @@ -12,7 +11,6 @@ using XamlX.TypeSystem; using XamlX; using XamlX.Emit; using XamlX.IL; -using Avalonia.Utilities; using XamlIlEmitContext = XamlX.Emit.XamlEmitContextWithLocals; using System.Xml.Linq; @@ -667,7 +665,11 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions { // In this case, we need to emit our own delegate type. string delegateTypeName = context.Configuration.IdentifierGenerator.GenerateIdentifierPart(); - specificDelegateType = newDelegateTypeBuilder = context.DefineDelegateSubType(delegateTypeName, Method.ReturnType, Method.Parameters); + specificDelegateType = newDelegateTypeBuilder = context.DeclaringType.DefineDelegateSubType( + delegateTypeName, + XamlVisibility.Private, + Method.ReturnType, + Method.Parameters); } codeGen diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github b/src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github index 320e57fa1c..b357355b9d 160000 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github @@ -1 +1 @@ -Subproject commit 320e57fa1c95d10de33d9a96a323d1bc27563fa1 +Subproject commit b357355b9df3e781cf194f2f2ce21669d798bb14 diff --git a/src/tools/Avalonia.Generators/Compiler/MiniCompiler.cs b/src/tools/Avalonia.Generators/Compiler/MiniCompiler.cs index 9bf82ae5bd..b0421cd245 100644 --- a/src/tools/Avalonia.Generators/Compiler/MiniCompiler.cs +++ b/src/tools/Avalonia.Generators/Compiler/MiniCompiler.cs @@ -1,8 +1,6 @@ using System; -using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; -using XamlX; using XamlX.Compiler; using XamlX.Emit; using XamlX.Transform; @@ -23,7 +21,7 @@ internal sealed class MiniCompiler : XamlCompiler mappings.XmlnsAttributes.Add(typeSystem.GetType(additionalType)); var diagnosticsHandler = new XamlDiagnosticsHandler(); - + var configuration = new TransformerConfiguration( typeSystem, typeSystem.Assemblies.First(), @@ -45,10 +43,7 @@ internal sealed class MiniCompiler : XamlCompiler protected override XamlEmitContext InitCodeGen( IFileSource file, - Func> createSubType, - Func, - IXamlTypeBuilder> createDelegateType, + IXamlTypeBuilder declaringType, object codeGen, XamlRuntimeContext context, bool needContextLocal) => diff --git a/src/tools/Avalonia.Generators/Compiler/RoslynTypeSystem.cs b/src/tools/Avalonia.Generators/Compiler/RoslynTypeSystem.cs index 495d960ab5..72ce2cca2f 100644 --- a/src/tools/Avalonia.Generators/Compiler/RoslynTypeSystem.cs +++ b/src/tools/Avalonia.Generators/Compiler/RoslynTypeSystem.cs @@ -128,6 +128,13 @@ internal class RoslynType : IXamlType public IXamlAssembly Assembly => _assembly; + public bool IsPublic => _symbol.DeclaredAccessibility == Accessibility.Public; + + public bool IsNestedPrivate => _symbol.DeclaredAccessibility == Accessibility.Private; + + public IXamlType DeclaringType => + _symbol.ContainingType is { } containingType ? new RoslynType(containingType, _assembly) : null; + public IReadOnlyList Properties => _symbol.GetMembers() .Where(member => member.Kind == SymbolKind.Property) @@ -259,7 +266,11 @@ internal class RoslynMethod : IXamlMethod public string Name => _symbol.Name; - public bool IsPublic => true; + public bool IsPublic => _symbol.DeclaredAccessibility == Accessibility.Public; + + public bool IsPrivate => _symbol.DeclaredAccessibility == Accessibility.Private; + + public bool IsFamily => _symbol.DeclaredAccessibility == Accessibility.Protected; public bool IsStatic => false;