Ver Fonte

Implement multiple documents loading and transforming in AvaloniaRuntimeXamlLoader

Max Katz há 2 anos atrás
pai
commit
4460de529e

+ 1 - 2
src/Avalonia.DesignerSupport/DesignWindowLoader.cs

@@ -38,10 +38,9 @@ namespace Avalonia.DesignerSupport
                 var useCompiledBindings = localAsm?.GetCustomAttributes<AssemblyMetadataAttribute>()
                     .FirstOrDefault(a => a.Key == "AvaloniaUseCompiledBindingsByDefault")?.Value;
 
-                var loaded = loader.Load(stream, new RuntimeXamlLoaderConfiguration
+                var loaded = loader.Load(new RuntimeXamlLoaderDocument(baseUri, stream), new RuntimeXamlLoaderConfiguration
                 {
                     LocalAssembly = localAsm,
-                    BaseUri = baseUri,
                     DesignMode = true,
                     UseCompiledBindingsByDefault = bool.TryParse(useCompiledBindings, out var parsedValue ) && parsedValue 
                 });

+ 20 - 26
src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaRuntimeXamlLoader.cs

@@ -1,9 +1,10 @@
 using System;
+using System.Collections.Generic;
 using System.IO;
 using System.Reflection;
 using System.Text;
 using Avalonia.Markup.Xaml.XamlIl;
-
+#nullable enable
 namespace Avalonia.Markup.Xaml
 {
     public static class AvaloniaRuntimeXamlLoader
@@ -17,31 +18,15 @@ namespace Avalonia.Markup.Xaml
         /// <param name="uri">The URI of the XAML being loaded.</param>
         /// <param name="designMode">Indicates whether the XAML is being loaded in design mode.</param>
         /// <returns>The loaded object.</returns>
-        public static object Load(string xaml, Assembly localAssembly = null, object rootInstance = null, Uri uri = null, bool designMode = false)
+        public static object Load(string xaml, Assembly? localAssembly = null, object? rootInstance = null, Uri? uri = null, bool designMode = false)
         {
-            Contract.Requires<ArgumentNullException>(xaml != null);
+            xaml = xaml ?? throw new ArgumentNullException(nameof(xaml));
 
             using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(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.
@@ -52,9 +37,10 @@ namespace Avalonia.Markup.Xaml
         /// <param name="uri">The URI of the XAML being loaded.</param>
         /// <param name="designMode">Indicates whether the XAML is being loaded in design mode.</param>
         /// <returns>The loaded object.</returns>
-        public static object Load(Stream stream, Assembly localAssembly, object rootInstance = null, Uri uri = null,
+        public static object Load(Stream stream, Assembly? localAssembly = null, object? rootInstance = null, Uri? uri = null,
             bool designMode = false)
-            => AvaloniaXamlIlRuntimeCompiler.Load(stream, localAssembly, rootInstance, uri, designMode, false);
+            => AvaloniaXamlIlRuntimeCompiler.Load(new RuntimeXamlLoaderDocument(uri, rootInstance, stream),
+                new RuntimeXamlLoaderConfiguration { DesignMode = designMode, LocalAssembly = localAssembly });
         
         /// <summary>
         /// Loads XAML from a stream.
@@ -62,9 +48,17 @@ namespace Avalonia.Markup.Xaml
         /// <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);
+        public static object Load(RuntimeXamlLoaderDocument document, RuntimeXamlLoaderConfiguration? configuration = null)
+            => AvaloniaXamlIlRuntimeCompiler.Load(document, configuration ?? new RuntimeXamlLoaderConfiguration());
+
+        /// <summary>
+        /// Loads group of XAML files from a stream.
+        /// </summary>
+        /// <param name="documents">Collection of documents.</param>
+        /// <param name="configuration">Xaml loader configuration.</param>
+        /// <returns>The loaded objects per each input document.</returns>
+        public static IReadOnlyList<object> LoadGroup(IReadOnlyCollection<RuntimeXamlLoaderDocument> documents, RuntimeXamlLoaderConfiguration? configuration = null)
+            => AvaloniaXamlIlRuntimeCompiler.LoadGroup(documents, configuration ?? new RuntimeXamlLoaderConfiguration());
 
         /// <summary>
         /// Parse XAML from a string.
@@ -72,7 +66,7 @@ namespace Avalonia.Markup.Xaml
         /// <param name="xaml">The string containing the XAML.</param>
         /// <param name="localAssembly">Default assembly for clr-namespace:.</param>
         /// <returns>The loaded object.</returns>
-        public static object Parse(string xaml, Assembly localAssembly = null)
+        public static object Parse(string xaml, Assembly? localAssembly = null)
             => Load(xaml, localAssembly);
 
         /// <summary>
@@ -82,7 +76,7 @@ namespace Avalonia.Markup.Xaml
         /// <param name="xaml">>The string containing the XAML.</param>
         /// <param name="localAssembly">>Default assembly for clr-namespace:.</param>
         /// <returns>The loaded object.</returns>
-        public static T Parse<T>(string xaml, Assembly localAssembly = null)
+        public static T Parse<T>(string xaml, Assembly? localAssembly = null)
             => (T)Parse(xaml, localAssembly);
             
     }

+ 101 - 32
src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs

@@ -10,6 +10,7 @@ using System.Runtime.InteropServices;
 using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions;
 using Avalonia.Markup.Xaml.XamlIl.Runtime;
 using Avalonia.Platform;
+using XamlX.Ast;
 using XamlX.Transform;
 using XamlX.TypeSystem;
 using XamlX.IL;
@@ -150,12 +151,12 @@ namespace Avalonia.Markup.Xaml.XamlIl
         }
         
 
-        static object LoadSre(string xaml, Assembly localAssembly, object rootInstance, Uri uri, bool isDesignMode, bool useCompiledBindingsByDefault)
+        static object LoadSre(RuntimeXamlLoaderDocument document, RuntimeXamlLoaderConfiguration configuration)
         {
             var success = false;
             try
             {
-                var rv = LoadSreCore(xaml, localAssembly, rootInstance, uri, isDesignMode, useCompiledBindingsByDefault);
+                var rv = LoadSreCore(document, configuration);
                 success = true;
                 return rv;
             }
@@ -166,45 +167,100 @@ namespace Avalonia.Markup.Xaml.XamlIl
             }
         }
 
+        static IReadOnlyList<object> LoadGroupSre(IReadOnlyCollection<RuntimeXamlLoaderDocument> documents,
+            RuntimeXamlLoaderConfiguration configuration)
+        {
+            var success = false;
+            try
+            {
+                var rv = LoadGroupSreCore(documents, configuration);
+                success = true;
+                return rv;
+            }
+            finally
+            {
+                if( _sreCanSave)
+                    DumpRuntimeCompilationResults();
+            }
+        }
         
-        static object LoadSreCore(string xaml, Assembly localAssembly, object rootInstance, Uri uri, bool isDesignMode, bool useCompiledBindingsByDefault)
+        static IReadOnlyList<object> LoadGroupSreCore(IReadOnlyCollection<RuntimeXamlLoaderDocument> documents, RuntimeXamlLoaderConfiguration configuration)
         {
-
             InitializeSre();
+            var localAssembly = configuration.LocalAssembly;
             if (localAssembly?.GetName() != null)
                 EmitIgnoresAccessCheckToAttribute(localAssembly.GetName());
             var asm = localAssembly == null ? null : _sreTypeSystem.GetAssembly(localAssembly);
-            var tb = _sreBuilder.DefineType("Builder_" + Guid.NewGuid().ToString("N") + "_" + uri);
-            var clrPropertyBuilder = tb.DefineNestedType("ClrProperties_" + Guid.NewGuid().ToString("N"));
+            var clrPropertyBuilder = _sreBuilder.DefineType("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))), 
+                    _sreMappings, _sreXmlns, AvaloniaXamlIlLanguage.CustomValueConverter,
+                    new XamlIlClrPropertyInfoEmitter(_sreTypeSystem.CreateTypeBuilder(clrPropertyBuilder)),
+                    new XamlIlPropertyInfoAccessorFactoryEmitter(_sreTypeSystem.CreateTypeBuilder(indexerClosureType)),
+                    new XamlIlTrampolineBuilder(_sreTypeSystem.CreateTypeBuilder(trampolineBuilder))),
                 _sreEmitMappings,
-                _sreContextType) { EnableIlVerification = true, DefaultCompileBindings = useCompiledBindingsByDefault };
+                _sreContextType)
+            {
+                EnableIlVerification = true,
+                DefaultCompileBindings = configuration.UseCompiledBindingsByDefault,
+                IsDesignMode = configuration.DesignMode
+            };
 
-            IXamlType overrideType = null;
-            if (rootInstance != null)
+            var parsedDocuments = new List<XamlDocumentResource>();
+            var rootInstances = new List<object>();
+
+            foreach (var document in documents)
             {
-                overrideType = _sreTypeSystem.GetType(rootInstance.GetType());
+                string xaml;
+                using (var sr = new StreamReader(document.XamlStream))
+                    xaml = sr.ReadToEnd();
+                
+                IXamlType overrideType = null;
+                if (document.RootInstance != null)
+                {
+                    overrideType = _sreTypeSystem.GetType(document.RootInstance.GetType());
+                }
+                
+                var parsed = compiler.Parse(xaml, overrideType);
+                compiler.Transform(parsed);
+
+                var xamlName = GetSafeUriIdentifier(document.BaseUri)
+                               ?? document.RootInstance?.GetType().Name
+                               ?? ((IXamlAstValueNode)parsed.Root).Type.GetClrType().Name;
+                var tb = _sreBuilder.DefineType("Builder_" + Guid.NewGuid().ToString("N") + "_" + xamlName);
+                var builder = _sreTypeSystem.CreateTypeBuilder(tb);
+                parsedDocuments.Add(new XamlDocumentResource(parsed, document.BaseUri?.ToString(), null, null,
+                    builder,
+                    compiler.DefinePopulateMethod(builder, parsed, AvaloniaXamlIlCompiler.PopulateName, true),
+                    compiler.DefineBuildMethod(builder, parsed, AvaloniaXamlIlCompiler.BuildName, true)));
+                rootInstances.Add(document.RootInstance);
             }
 
-            compiler.IsDesignMode = isDesignMode;
-            compiler.ParseAndCompile(xaml, uri?.ToString(), null, _sreTypeSystem.CreateTypeBuilder(tb), overrideType);
-            var created = tb.CreateTypeInfo();
+            compiler.TransformGroup(parsedDocuments);
+
+            var createdTypes = parsedDocuments.Select(document =>
+            {
+                compiler.Compile(document.XamlDocument, document.TypeBuilder, document.PopulateMethod,
+                    document.BuildMethod, document.Uri, document.FileSource);
+                return _sreTypeSystem.GetType(document.TypeBuilder.CreateType());
+            }).ToArray();
+            
             clrPropertyBuilder.CreateTypeInfo();
             indexerClosureType.CreateTypeInfo();
             trampolineBuilder.CreateTypeInfo();
 
-            return LoadOrPopulate(created, rootInstance);
+            return createdTypes.Zip(rootInstances, (l, r) => (l, r)).Select(t => LoadOrPopulate(t.Item1, t.Item2)).ToArray();
+        }
+        
+        static object LoadSreCore(RuntimeXamlLoaderDocument document, RuntimeXamlLoaderConfiguration configuration)
+        {
+            return LoadGroupSreCore(new[] { document }, configuration).Single();
         }
 #endif
 
-            static object LoadOrPopulate(Type created, object rootInstance)
+        static object LoadOrPopulate(Type created, object rootInstance)
         {
             var isp = Expression.Parameter(typeof(IServiceProvider));
 
@@ -249,19 +305,37 @@ namespace Avalonia.Markup.Xaml.XamlIl
             }
         }
         
-        public static object Load(Stream stream, Assembly localAssembly, object rootInstance, Uri uri,
-            bool isDesignMode, bool useCompiledBindingsByDefault)
+        public static object Load(RuntimeXamlLoaderDocument document, RuntimeXamlLoaderConfiguration configuration)
         {
+#if RUNTIME_XAML_CECIL
             string xaml;
-            using (var sr = new StreamReader(stream))
+            using (var sr = new StreamReader(document.XamlStream))
                 xaml = sr.ReadToEnd();
+            return LoadCecil(xaml, configuration.LocalAssembly, document.RootInstance,document.BaseUri, configuration.UseCompiledBindingsByDefault);
+#else
+            return LoadSre(document, configuration);
+#endif
+        }
+
+        public static IReadOnlyList<object> LoadGroup(IReadOnlyCollection<RuntimeXamlLoaderDocument> documents, RuntimeXamlLoaderConfiguration configuration)
+        {
 #if RUNTIME_XAML_CECIL
-            return LoadCecil(xaml, localAssembly, rootInstance, uri, useCompiledBindingsByDefault);
+            throw new NotImplementedException("Load group was not implemented for the Cecil backend");
 #else
-            return LoadSre(xaml, localAssembly, rootInstance, uri, isDesignMode, useCompiledBindingsByDefault);
+            return LoadGroupSre(documents, configuration);
 #endif
         }
 
+        private static string GetSafeUriIdentifier(Uri uri)
+        {
+            return uri?.ToString()
+                .Replace(":", "_")
+                .Replace("/", "_")
+                .Replace("?", "_")
+                .Replace("=", "_")
+                .Replace(".", "_");
+        }
+        
 #if RUNTIME_XAML_CECIL
         private static Dictionary<string, (Action<IServiceProvider, object> populate, Func<IServiceProvider, object>
                 build)>
@@ -303,12 +377,7 @@ namespace Avalonia.Markup.Xaml.XamlIl
                 overrideType = _cecilTypeSystem.GetType(rootInstance.GetType().FullName);
             }
            
-            var safeUri = uri.ToString()
-                .Replace(":", "_")
-                .Replace("/", "_")
-                .Replace("?", "_")
-                .Replace("=", "_")
-                .Replace(".", "_");
+            var safeUri = GetSafeUriIdentifier(uri);
             if (_cecilGeneratedCache.TryGetValue(safeUri, out var cached))
                 return LoadOrPopulate(cached, rootInstance);
             
@@ -335,7 +404,7 @@ namespace Avalonia.Markup.Xaml.XamlIl
                 {
                     DefaultCompileBindings = useCompiledBindingsByDefault
                 };
-            compiler.ParseAndCompile(xaml, uri.ToString(), tb, overrideType);
+            compiler.ParseAndCompile(xaml, uri.ToString(), null, tb, overrideType);
             var asmPath = Path.Combine(_cecilEmitDir, safeUri + ".dll");
             using(var f = File.Create(asmPath))
                 asm.Write(f);

+ 45 - 3
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs

@@ -1,6 +1,6 @@
 using System.Collections.Generic;
 using System.Linq;
-
+using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.GroupTransformers;
 using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers;
 using XamlX;
 using XamlX.Ast;
@@ -83,6 +83,11 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
 
             Emitters.Add(new AvaloniaNameScopeRegistrationXamlIlNodeEmitter());
             Emitters.Add(new AvaloniaXamlIlRootObjectScope.Emitter());
+            
+            GroupTransformers = new()
+            {
+                new AvaloniaXamlIncludeTransformer()
+            };
         }
         public AvaloniaXamlIlCompiler(TransformerConfiguration configuration,
             XamlLanguageEmitMappings<IXamlILEmitter, XamlILNodeEmitResult> emitMappings,
@@ -115,7 +120,27 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
             set => _bindingTransformer.CompileBindingsByDefault = value;
         }
 
-        public void ParseAndCompile(string xaml, string baseUri, IFileSource fileSource, IXamlTypeBuilder<IXamlILEmitter> tb, IXamlType overrideRootType)
+        public List<IXamlAstGroupTransformer> GroupTransformers { get; }
+
+        public void TransformGroup(IReadOnlyCollection<IXamlDocumentResource> documents, bool strict = true)
+        {
+            var ctx = new AstGroupTransformationContext(documents, _configuration, strict);
+            foreach (var transformer in GroupTransformers)
+            {
+                foreach (var doc in documents)
+                {
+                    var root = doc.XamlDocument.Root;
+                    ctx.CurrentDocument = doc;
+                    ctx.RootObject = (IXamlAstValueNode)root;
+                    ctx.VisitChildren(ctx.RootObject, transformer);
+                    root = ctx.Visit(root, transformer);
+
+                    doc.XamlDocument.Root = root;
+                }
+            }
+        }
+
+        public XamlDocument Parse(string xaml, IXamlType overrideRootType)
         {
             var parsed = XDocumentXamlParser.Parse(xaml, new Dictionary<string, string>
             {
@@ -148,9 +173,26 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
 
             OverrideRootType(parsed, rootType);
 
+            return parsed;
+        }
+
+        public void Compile(XamlDocument document, IXamlTypeBuilder<IXamlILEmitter> tb, IXamlMethodBuilder<IXamlILEmitter> populateMethod, IXamlMethodBuilder<IXamlILEmitter> buildMethod, string baseUri, IFileSource fileSource)
+        {
+            Compile(document, _contextType, populateMethod, buildMethod,
+                _configuration.TypeMappings.XmlNamespaceInfoProvider == null ?
+                    null :
+                    tb.DefineSubType(_configuration.WellKnownTypes.Object,
+                        "__AvaloniaXamlIlNsInfo", false), (name, bt) => tb.DefineSubType(bt, name, false),
+                (s, returnType, parameters) => tb.DefineDelegateSubType(s, false, returnType, parameters), baseUri,
+                fileSource);
+        }
+        
+        public void ParseAndCompile(string xaml, string baseUri, IFileSource fileSource, IXamlTypeBuilder<IXamlILEmitter> tb, IXamlType overrideRootType)
+        {
+            var parsed = Parse(xaml, overrideRootType);
+
             Transform(parsed);
             Compile(parsed, tb, _contextType, PopulateName, BuildName, "__AvaloniaXamlIlNsInfo", baseUri, fileSource);
-
         }
 
         public void OverrideRootType(XamlDocument doc, IXamlAstTypeReference newType)

+ 78 - 0
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/IXamlAstGroupTransformer.cs

@@ -0,0 +1,78 @@
+using System;
+using System.Collections.Generic;
+using System.Xml;
+using XamlX;
+using XamlX.Ast;
+using XamlX.Transform;
+
+namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.GroupTransformers;
+
+internal class AstGroupTransformationContext : AstTransformationContext
+{
+    public AstGroupTransformationContext(IReadOnlyCollection<IXamlDocumentResource> documents, TransformerConfiguration configuration, bool strictMode = true)
+        : base(configuration, null, strictMode)
+    {
+        Documents = documents;
+    }
+
+    public IXamlDocumentResource CurrentDocument { get; set; }
+    
+    public IReadOnlyCollection<IXamlDocumentResource> Documents { get; }
+    
+    public new IXamlAstNode ParseError(string message, IXamlAstNode node) =>
+        Error(node, new XamlDocumentParseException(CurrentDocument?.FileSource?.FilePath, message, node));
+
+    public new IXamlAstNode ParseError(string message, IXamlAstNode offender, IXamlAstNode ret) =>
+        Error(ret, new XamlDocumentParseException(CurrentDocument?.FileSource?.FilePath, message, offender));
+
+    class Visitor : IXamlAstVisitor
+    {
+        private readonly AstGroupTransformationContext _context;
+        private readonly IXamlAstGroupTransformer _transformer;
+
+        public Visitor(AstGroupTransformationContext context, IXamlAstGroupTransformer transformer)
+        {
+            _context = context;
+            _transformer = transformer;
+        }
+            
+        public IXamlAstNode Visit(IXamlAstNode node)
+        {
+#if Xaml_DEBUG
+                return _transformer.Transform(_context, node);
+#else
+            try
+            {
+                return _transformer.Transform(_context, node);
+            }
+            catch (Exception e) when (!(e is XmlException))
+            {
+                throw new XamlDocumentParseException(
+                    _context.CurrentDocument?.FileSource?.FilePath,
+                    "Internal compiler error while transforming node " + node + ":\n" + e,
+                    node);
+            }
+#endif
+        }
+
+        public void Push(IXamlAstNode node) => _context.PushParent(node);
+
+        public void Pop() => _context.PopParent();
+    }
+    
+    public IXamlAstNode Visit(IXamlAstNode root, IXamlAstGroupTransformer transformer)
+    {
+        root = root.Visit(new Visitor(this, transformer));
+        return root;
+    }
+
+    public void VisitChildren(IXamlAstNode root, IXamlAstGroupTransformer transformer)
+    {
+        root.VisitChildren(new Visitor(this, transformer));
+    }
+}
+
+internal interface IXamlAstGroupTransformer
+{
+    IXamlAstNode Transform(AstGroupTransformationContext context, IXamlAstNode node);
+}

+ 17 - 0
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/IXamlDocumentResource.cs

@@ -0,0 +1,17 @@
+using System;
+using XamlX.Ast;
+using XamlX.TypeSystem;
+
+namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions;
+
+#nullable enable
+
+internal interface IXamlDocumentResource
+{
+    IXamlMethod? BuildMethod { get; }
+    IXamlType? ClassType { get; }
+    string? Uri { get; }
+    IXamlMethod PopulateMethod { get; }
+    IFileSource? FileSource { get; }
+    XamlDocument XamlDocument { get; }
+}

+ 9 - 0
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs

@@ -1,5 +1,6 @@
 using System.Collections.Generic;
 using System.Linq;
+using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.GroupTransformers;
 using XamlX.Emit;
 using XamlX.IL;
 using XamlX.Transform;
@@ -266,5 +267,13 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
             ctx.SetItem(rv = new AvaloniaXamlIlWellKnownTypes(ctx.Configuration));
             return rv;
         }
+        
+        public static AvaloniaXamlIlWellKnownTypes GetAvaloniaTypes(this AstGroupTransformationContext ctx)
+        {
+            if (ctx.TryGetItem<AvaloniaXamlIlWellKnownTypes>(out var rv))
+                return rv;
+            ctx.SetItem(rv = new AvaloniaXamlIlWellKnownTypes(ctx.Configuration));
+            return rv;
+        }
     }
 }

+ 21 - 0
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlDocumentParseException.cs

@@ -0,0 +1,21 @@
+using XamlX;
+using XamlX.Ast;
+
+namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions;
+
+internal class XamlDocumentParseException : XamlParseException
+{
+    public string FilePath { get; }
+
+    public XamlDocumentParseException(string path, XamlParseException parseException)
+        : base(parseException.Message, parseException.LineNumber, parseException.LinePosition)
+    {
+        FilePath = path;
+    }
+    
+    public XamlDocumentParseException(string path, string message, IXamlLineInfo lineInfo)
+        : base(message, lineInfo.Line, lineInfo.Position)
+    {
+        FilePath = path;
+    }
+}

+ 40 - 0
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlDocumentResource.cs

@@ -0,0 +1,40 @@
+using System;
+using XamlX.Ast;
+using XamlX.IL;
+using XamlX.TypeSystem;
+#nullable enable
+
+namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions;
+
+internal class XamlDocumentResource : IXamlDocumentResource
+{
+    public XamlDocumentResource(
+        XamlDocument xamlDocument,
+        string? uri,
+        IFileSource? fileSource,
+        IXamlType? classType,
+        IXamlTypeBuilder<IXamlILEmitter> typeBuilder,
+        IXamlMethodBuilder<IXamlILEmitter> populateMethod,
+        IXamlMethodBuilder<IXamlILEmitter>? buildMethod)
+    {
+        XamlDocument = xamlDocument;
+        Uri = uri;
+        FileSource = fileSource;
+        ClassType = classType;
+        TypeBuilder = typeBuilder;
+        PopulateMethod = populateMethod;
+        BuildMethod = buildMethod;
+    }
+
+    public XamlDocument XamlDocument { get; }
+    public string? Uri { get; }
+    public IFileSource? FileSource { get; }
+
+    public IXamlType? ClassType { get; }
+    public IXamlTypeBuilder<IXamlILEmitter> TypeBuilder { get; }
+    public IXamlMethodBuilder<IXamlILEmitter> PopulateMethod { get; }
+    public IXamlMethodBuilder<IXamlILEmitter>? BuildMethod { get; }
+
+    IXamlMethod? IXamlDocumentResource.BuildMethod => BuildMethod;
+    IXamlMethod IXamlDocumentResource.PopulateMethod => PopulateMethod;
+}

+ 1 - 0
src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj

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

+ 3 - 4
src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs

@@ -11,7 +11,7 @@ namespace Avalonia.Markup.Xaml
     {
         public interface IRuntimeXamlLoader
         {
-            object Load(Stream stream, RuntimeXamlLoaderConfiguration configuration);
+            object Load(RuntimeXamlLoaderDocument document, RuntimeXamlLoaderConfiguration configuration);
         }
         
         /// <summary>
@@ -64,10 +64,9 @@ namespace Avalonia.Markup.Xaml
                 using (var stream = asset.stream)
                 {
                     var absoluteUri = uri.IsAbsoluteUri ? uri : new Uri(baseUri, uri);
-                    return runtimeLoader.Load(stream, new RuntimeXamlLoaderConfiguration
+                    return runtimeLoader.Load(new RuntimeXamlLoaderDocument(absoluteUri, stream), new RuntimeXamlLoaderConfiguration
                     {
-                        LocalAssembly = asset.assembly,
-                        BaseUri = absoluteUri
+                        LocalAssembly = asset.assembly
                     });
                 }
             }

+ 1 - 12
src/Markup/Avalonia.Markup.Xaml/RuntimeXamlLoaderConfiguration.cs

@@ -1,4 +1,3 @@
-using System;
 using System.Reflection;
 
 namespace Avalonia.Markup.Xaml;
@@ -7,21 +6,11 @@ namespace Avalonia.Markup.Xaml;
 
 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'.

+ 70 - 0
src/Markup/Avalonia.Markup.Xaml/RuntimeXamlLoaderDocument.cs

@@ -0,0 +1,70 @@
+#nullable enable
+using System;
+using System.IO;
+using System.Text;
+
+namespace Avalonia.Markup.Xaml;
+
+public class RuntimeXamlLoaderDocument
+{
+    public RuntimeXamlLoaderDocument(string xaml)
+    {
+        XamlStream = new MemoryStream(Encoding.UTF8.GetBytes(xaml));
+    }
+
+    public RuntimeXamlLoaderDocument(Uri? baseUri, string xaml)
+        : this(xaml)
+    {
+        BaseUri = baseUri;
+    }
+
+    public RuntimeXamlLoaderDocument(object? rootInstance, string xaml)
+        : this(xaml)
+    {
+        RootInstance = rootInstance;
+    }
+    
+    public RuntimeXamlLoaderDocument(Uri? baseUri, object? rootInstance, string xaml)
+        : this(baseUri, xaml)
+    {
+        RootInstance = rootInstance;
+    }
+    
+    public RuntimeXamlLoaderDocument(Stream stream)
+    {
+        XamlStream = stream;
+    }
+
+    public RuntimeXamlLoaderDocument(Uri? baseUri, Stream stream)
+        : this(stream)
+    {
+        BaseUri = baseUri;
+    }
+
+    public RuntimeXamlLoaderDocument(object? rootInstance, Stream stream)
+        : this(stream)
+    {
+        RootInstance = rootInstance;
+    }
+
+    public RuntimeXamlLoaderDocument(Uri? baseUri, object? rootInstance, Stream stream)
+        : this(baseUri, stream)
+    {
+        RootInstance = rootInstance;
+    }
+    
+    /// <summary>
+    /// The URI of the XAML being loaded.
+    /// </summary>
+    public Uri? BaseUri { get; set; }
+
+    /// <summary>
+    /// The optional instance into which the XAML should be loaded.
+    /// </summary>
+    public object? RootInstance { get; set; }
+    
+    /// <summary>
+    /// The stream containing the XAML.
+    /// </summary>
+    public Stream XamlStream { get; set; }
+}

+ 2 - 4
src/tools/Avalonia.Designer.HostApp/DesignXamlLoader.cs

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

+ 2 - 4
tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs

@@ -1642,10 +1642,8 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
         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 control = (AssignBindingControl)AvaloniaRuntimeXamlLoader.Load(new RuntimeXamlLoaderDocument(xaml),
+                    new RuntimeXamlLoaderConfiguration { UseCompiledBindingsByDefault = true });
                 var compiledPath = ((CompiledBindingExtension)control.X).Path;
 
                 var node = Assert.IsType<PropertyElement>(Assert.Single(compiledPath.Elements));

+ 2 - 2
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, RuntimeXamlLoaderConfiguration configuration) 
-                => AvaloniaRuntimeXamlLoader.Load(stream, configuration);
+            public object Load(RuntimeXamlLoaderDocument document, RuntimeXamlLoaderConfiguration configuration) 
+                => AvaloniaRuntimeXamlLoader.Load(document, configuration);
         }
     }
 }