Browse Source

Implement multiple documents loading and transforming in AvaloniaRuntimeXamlLoader

pull/9537/head
Max Katz 3 years ago
parent
commit
4460de529e
  1. 3
      src/Avalonia.DesignerSupport/DesignWindowLoader.cs
  2. 46
      src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaRuntimeXamlLoader.cs
  3. 133
      src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs
  4. 48
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs
  5. 78
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/IXamlAstGroupTransformer.cs
  6. 17
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/IXamlDocumentResource.cs
  7. 9
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs
  8. 21
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlDocumentParseException.cs
  9. 40
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlDocumentResource.cs
  10. 1
      src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
  11. 7
      src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs
  12. 13
      src/Markup/Avalonia.Markup.Xaml/RuntimeXamlLoaderConfiguration.cs
  13. 70
      src/Markup/Avalonia.Markup.Xaml/RuntimeXamlLoaderDocument.cs
  14. 6
      src/tools/Avalonia.Designer.HostApp/DesignXamlLoader.cs
  15. 6
      tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs
  16. 4
      tests/Avalonia.Markup.Xaml.UnitTests/XamlTestBase.cs

3
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
});

46
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);
}

133
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);

48
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
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
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
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
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
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
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" />

7
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
});
}
}

13
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
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; }
}

6
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);
}
}
}

6
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));

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

Loading…
Cancel
Save