diff --git a/src/Avalonia.Base/Platform/AssetLoader.cs b/src/Avalonia.Base/Platform/AssetLoader.cs
index a74da2a178..8d3576d180 100644
--- a/src/Avalonia.Base/Platform/AssetLoader.cs
+++ b/src/Avalonia.Base/Platform/AssetLoader.cs
@@ -3,16 +3,22 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
+#if !BUILDTASK
using Avalonia.Platform.Internal;
using Avalonia.Utilities;
+#endif
namespace Avalonia.Platform
{
///
/// Loads assets compiled into the application binary.
///
- public class AssetLoader : IAssetLoader
+ public class AssetLoader
+#if !BUILDTASK
+ : IAssetLoader
+#endif
{
+#if !BUILDTASK
private static IAssemblyDescriptorResolver s_assemblyDescriptorResolver = new AssemblyDescriptorResolver();
private AssemblyDescriptor? _defaultResmAssembly;
@@ -206,7 +212,8 @@ namespace Avalonia.Platform
return null;
}
-
+#endif
+
public static void RegisterResUriParsers()
{
if (!UriParser.IsKnownScheme("avares"))
diff --git a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
index 589472785d..31649fd4b6 100644
--- a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
+++ b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
@@ -231,8 +231,6 @@ namespace Avalonia.Build.Tasks
});
asm.MainModule.Types.Add(typeDef);
var builder = typeSystem.CreateTypeBuilder(typeDef);
-
- var populateMethodsToTransform = new List<(MethodDefinition populateMethod, string resourceFilePath)>();
IReadOnlyCollection parsedXamlDocuments = new List();
foreach (var res in group.Resources.Where(CheckXamlName).OrderBy(x => x.FilePath.ToLowerInvariant()))
@@ -347,23 +345,11 @@ namespace Avalonia.Build.Tasks
res.Uri, res
);
- var compiledPopulateMethod = typeSystem.GetTypeReference(populateBuilder).Resolve()
- .Methods.First(m => m.Name == populateName);
-
- // Include populate method and all nested methods/closures used in the populate method,
- // So we can replace style/resource includes in all of them.
- populateMethodsToTransform.Add((compiledPopulateMethod, res.FilePath));
- populateMethodsToTransform.AddRange(compiledPopulateMethod.Body.Instructions
- .Where(b => b.OpCode == OpCodes.Call || b.OpCode == OpCodes.Callvirt || b.OpCode == OpCodes.Ldftn)
- .Select(b => b.Operand)
- .OfType()
- .Where(m => compiledPopulateMethod.DeclaringType.NestedTypes.Contains(m.DeclaringType))
- .Select(m => m.Resolve())
- .Where(m => m.HasBody)
- .Select(m => (m, res.FilePath)));
-
if (classTypeDefinition != null)
{
+ var compiledPopulateMethod = typeSystem.GetTypeReference(populateBuilder).Resolve()
+ .Methods.First(m => m.Name == document.PopulateMethod.Name);
+
var designLoaderFieldType = typeSystem
.GetType("System.Action`1")
.MakeGenericType(typeSystem.GetType("System.Object"));
@@ -512,14 +498,6 @@ namespace Avalonia.Build.Tasks
res.Remove();
}
- foreach (var (populateMethod, resourceFilePath) in populateMethodsToTransform)
- {
- if (!TransformXamlIncludes(engine, typeSystem, populateMethod, resourceFilePath, createRootServiceProviderMethod))
- {
- return false;
- }
- }
-
// Technically that's a hack, but it fixes corert incompatibility caused by deterministic builds
int dupeCounter = 1;
foreach (var grp in typeDef.NestedTypes.GroupBy(x => x.Name))
@@ -596,116 +574,5 @@ namespace Avalonia.Build.Tasks
return true;
}
-
- private static bool TransformXamlIncludes(
- IBuildEngine engine, CecilTypeSystem typeSystem,
- MethodDefinition populateMethod, string resourceFilePath,
- MethodReference createRootServiceProviderMethod)
- {
- var asm = typeSystem.TargetAssemblyDefinition;
- foreach (var instruction in populateMethod.Body.Instructions.ToArray())
- {
- const string resolveStyleIncludeName = "ResolveStyleInclude";
- const string resolveResourceInclude = "ResolveResourceInclude";
- if (instruction.OpCode == OpCodes.Call
- && instruction.Operand is MethodReference
- {
- Name: resolveStyleIncludeName or resolveResourceInclude,
- DeclaringType: { Name: "XamlIlRuntimeHelpers" }
- })
- {
- int lineNumber = 0, linePosition = 0;
- bool instructionsModified = false;
- try
- {
- var assetSource = (string)instruction.Previous.Previous.Previous.Operand;
- lineNumber = GetConstValue(instruction.Previous.Previous);
- linePosition = GetConstValue(instruction.Previous);
-
- var index = populateMethod.Body.Instructions.IndexOf(instruction);
-
- assetSource = assetSource.Replace("avares://", "");
-
- var assemblyNameSeparator = assetSource.IndexOf('/');
- var fileNameSeparator = assetSource.LastIndexOf('/');
- if (assemblyNameSeparator < 0 || fileNameSeparator < 0)
- {
- throw new InvalidProgramException(
- $"Invalid asset source \"{assetSource}\". It must contain assembly name and relative path.");
- }
-
- var assetAssemblyName = assetSource.Substring(0, assemblyNameSeparator);
- var assetAssembly = typeSystem.FindAssembly(assetAssemblyName)
- ?? throw new InvalidProgramException(
- $"Unable to resolve assembly \"{assetAssemblyName}\"");
-
- var fileName = Path.GetFileNameWithoutExtension(assetSource.Replace('/', '.'));
- if (assetAssembly.FindType(fileName) is { } type
- && type.FindConstructor() is { } ctor)
- {
- var ctorMethod =
- asm.MainModule.ImportReference(typeSystem.GetMethodReference(ctor));
- instructionsModified = true;
- populateMethod.Body.Instructions[index - 3] = Instruction.Create(OpCodes.Nop);
- populateMethod.Body.Instructions[index - 2] = Instruction.Create(OpCodes.Nop);
- populateMethod.Body.Instructions[index - 1] = Instruction.Create(OpCodes.Nop);
- populateMethod.Body.Instructions[index] = Instruction.Create(OpCodes.Newobj, ctorMethod);
- }
- else
- {
- var resources = assetAssembly.FindType("CompiledAvaloniaXaml.!AvaloniaResources")
- ?? throw new InvalidOperationException(
- $"Unable to resolve \"!AvaloniaResources\" type on \"{assetAssemblyName}\" assembly");
-
- var relativeName = "Build:" + assetSource.Substring(assemblyNameSeparator);
- var buildMethod = resources.FindMethod(m => m.Name == relativeName)
- ?? throw new InvalidOperationException(
- $"Unable to resolve build method \"{relativeName}\" resource on the \"{assetAssemblyName}\" assembly");
-
- var methodReference = asm.MainModule.ImportReference(typeSystem.GetMethodReference(buildMethod));
- instructionsModified = true;
- populateMethod.Body.Instructions[index - 3] = Instruction.Create(OpCodes.Nop);
- populateMethod.Body.Instructions[index - 2] = Instruction.Create(OpCodes.Nop);
- populateMethod.Body.Instructions[index - 1] =
- Instruction.Create(OpCodes.Call, createRootServiceProviderMethod);
- populateMethod.Body.Instructions[index] = Instruction.Create(OpCodes.Call, methodReference);
- }
- }
- catch (Exception e)
- {
- if (instructionsModified)
- {
- engine.LogErrorEvent(new BuildErrorEventArgs("Avalonia", "XAMLIL", resourceFilePath,
- lineNumber, linePosition, lineNumber, linePosition,
- e.Message, "", "Avalonia"));
- return false;
- }
- else
- {
- engine.LogWarningEvent(new BuildWarningEventArgs("Avalonia", "XAMLIL",
- resourceFilePath,
- lineNumber, linePosition, lineNumber, linePosition,
- e.Message, "", "Avalonia"));
- }
- }
-
- static int GetConstValue(Instruction instruction)
- {
- if (instruction.OpCode is { Code : >= Code.Ldc_I4_0 and <= Code.Ldc_I4_8 })
- {
- return instruction.OpCode.Code - Code.Ldc_I4_0;
- }
- if (instruction.Operand is not null)
- {
- return Convert.ToInt32(instruction.Operand);
- }
-
- return 0;
- }
- }
- }
-
- return true;
- }
}
}
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs
index ccda94ff1a..b8350c3f11 100644
--- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs
@@ -56,8 +56,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
new AvaloniaXamlIlSetterTransformer(),
new AvaloniaXamlIlConstructorServiceProviderTransformer(),
new AvaloniaXamlIlTransitionsTypeMetadataTransformer(),
- new AvaloniaXamlIlResolveByNameMarkupExtensionReplacer(),
- new AvaloniaXamlIlAssetIncludeTransformer()
+ new AvaloniaXamlIlResolveByNameMarkupExtensionReplacer()
);
InsertBefore(
new AvaloniaXamlIlOptionMarkupExtensionTransformer());
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/XamlIncludeGroupTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/XamlIncludeGroupTransformer.cs
new file mode 100644
index 0000000000..2d30cd18b8
--- /dev/null
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/XamlIncludeGroupTransformer.cs
@@ -0,0 +1,133 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers;
+using Avalonia.Platform;
+using XamlX;
+using XamlX.Ast;
+using XamlX.Emit;
+using XamlX.IL;
+using XamlX.Transform;
+using XamlX.Transform.Transformers;
+using XamlX.TypeSystem;
+
+namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.GroupTransformers;
+
+internal class AvaloniaXamlIncludeTransformer : IXamlAstGroupTransformer
+{
+ public IXamlAstNode Transform(AstGroupTransformationContext context, IXamlAstNode node)
+ {
+ if (node is not XamlValueWithManipulationNode valueNode
+ || valueNode.Value is not XamlAstNewClrObjectNode objectNode
+ || (objectNode.Type.GetClrType() != context.GetAvaloniaTypes().StyleInclude
+ && objectNode.Type.GetClrType() != context.GetAvaloniaTypes().ResourceInclude))
+ {
+ return node;
+ }
+
+ var nodeTypeName = objectNode.Type.GetClrType().Name;
+ if (valueNode.Manipulation is not XamlObjectInitializationNode
+ {
+ Manipulation: XamlPropertyAssignmentNode { Property: { Name: "Source" } } sourceProperty
+ })
+ {
+ return context.ParseError($"Source property must be set on the \"{nodeTypeName}\" node.", node);
+ }
+
+ if (sourceProperty.Values.OfType().FirstOrDefault() is not { } sourceUriNode
+ || sourceUriNode.Type.GetClrType() != context.GetAvaloniaTypes().Uri
+ || sourceUriNode.Arguments.FirstOrDefault() is not XamlConstantNode { Constant: string originalAssetPath })
+ {
+ // TODO: make it a compiler warning
+ // Source value can be set with markup extension instead of the Uri object node, we don't support it here yet.
+ return node;
+ }
+
+ if (originalAssetPath.StartsWith("avares://"))
+ {
+ }
+ else if (originalAssetPath.StartsWith("/"))
+ {
+ var baseUrl = context.CurrentDocument.Uri ?? throw new InvalidOperationException("CurrentDocument URI is null.");
+ originalAssetPath = baseUrl.Substring(0, baseUrl.LastIndexOf('/')) + originalAssetPath;
+ }
+ else
+ {
+ return context.ParseError(
+ $"Avalonia supports only \"avares://\" sources or relative sources starting with \"/\" on the \"{nodeTypeName}\" node.",
+ node);
+ }
+
+ AssetLoader.RegisterResUriParsers();
+
+ originalAssetPath = Uri.UnescapeDataString(new Uri(originalAssetPath).AbsoluteUri);
+ var assetPath = originalAssetPath.Replace("avares://", "");
+ var assemblyNameSeparator = assetPath.IndexOf('/');
+ var assembly = assetPath.Substring(0, assemblyNameSeparator);
+ var fullTypeName = Path.GetFileNameWithoutExtension(assetPath.Replace('/', '.'));
+
+ if (context.Documents.FirstOrDefault(d => string.Equals(d.Uri, originalAssetPath, StringComparison.InvariantCultureIgnoreCase)) is {} targetDocument)
+ {
+ if (targetDocument.ClassType is not null)
+ {
+ return FromType(context, targetDocument.ClassType, node);
+ }
+
+ if (targetDocument.BuildMethod is null)
+ {
+ return context.ParseError($"\"{originalAssetPath}\" cannot be instantiated.", node);
+ }
+
+ return FromMethod(context, targetDocument.BuildMethod, node);
+ }
+
+
+ if (context.Configuration.TypeSystem.FindAssembly(assembly) is not { } assetAssembly)
+ {
+ return context.ParseError($"Assembly \"{assembly}\" was not found from the \"{originalAssetPath}\" source.", node);
+ }
+
+ if (assetAssembly.FindType(fullTypeName) is { } type
+ && type.FindMethod(m => m.Name == "!XamlIlPopulate") is not null)
+ {
+ return FromType(context, type, node);
+ }
+ else
+ {
+ var avaResType = assetAssembly.FindType("CompiledAvaloniaXaml.!AvaloniaResources");
+ if (avaResType is null)
+ {
+ return context.ParseError(
+ $"Unable to resolve \"!AvaloniaResources\" type on \"{assembly}\" assembly.", node);
+ }
+
+ var relativeName = "Build:" + assetPath.Substring(assemblyNameSeparator);
+ var buildMethod = avaResType.FindMethod(m => m.Name == relativeName);
+ if (buildMethod is null)
+ {
+ return context.ParseError(
+ $"Unable to resolve build method \"{relativeName}\" resource on the \"{assembly}\" assembly.",
+ node);
+ }
+
+ return FromMethod(context, buildMethod, node);
+ }
+ }
+
+ private static IXamlAstNode FromType(AstTransformationContext context, IXamlType type, IXamlLineInfo li)
+ {
+ IXamlAstNode newObjNode = new XamlAstObjectNode(li, new XamlAstClrTypeReference(li, type, false));
+ newObjNode = new AvaloniaXamlIlConstructorServiceProviderTransformer().Transform(context, newObjNode);
+ newObjNode = new ConstructableObjectTransformer().Transform(context, newObjNode);
+ return new NewObjectTransformer().Transform(context, newObjNode);
+ }
+
+ private static IXamlAstNode FromMethod(AstTransformationContext context, IXamlMethod method, IXamlLineInfo li)
+ {
+ var sp = context.Configuration.TypeMappings.ServiceProvider;
+ return new XamlStaticOrTargetedReturnMethodCallNode(li, method,
+ new[] { new AvaloniaXamlIlConstructorServiceProviderTransformer.InjectServiceProviderNode(sp, li, false) });
+ }
+}
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlAssetIncludeTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlAssetIncludeTransformer.cs
deleted file mode 100644
index 377a9e72d9..0000000000
--- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlAssetIncludeTransformer.cs
+++ /dev/null
@@ -1,93 +0,0 @@
-using System.Linq;
-using XamlX;
-using XamlX.Ast;
-using XamlX.Emit;
-using XamlX.IL;
-using XamlX.Transform;
-using XamlX.TypeSystem;
-
-namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers;
-
-internal class AvaloniaXamlIlAssetIncludeTransformer : IXamlAstTransformer
-{
- public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node)
- {
- if (node is not XamlAstObjectNode objectNode
- || (objectNode.Type.GetClrType() != context.GetAvaloniaTypes().StyleInclude
- && objectNode.Type.GetClrType() != context.GetAvaloniaTypes().ResourceInclude))
- {
- return node;
- }
-
- var nodeTypeName = objectNode.Type.GetClrType().Name;
-
- var sourceProperty = objectNode.Children.OfType().FirstOrDefault(n => n.Property.GetClrProperty().Name == "Source");
- var directives = objectNode.Children.OfType().ToList();
- if (sourceProperty is null
- || objectNode.Children.Count != (directives.Count + 1))
- {
- throw new XamlParseException($"Unexpected property on the {nodeTypeName} node", node);
- }
-
- if (sourceProperty.Values.OfType().FirstOrDefault() is not { } sourceTextNode)
- {
- // TODO: make it a compiler warning
- // Source value can be set with markup extension instead of a text node, we don't support it here yet.
- return node;
- }
-
- var originalAssetPath = sourceTextNode.Text;
- if (!(originalAssetPath.StartsWith("avares://") || originalAssetPath.StartsWith("/")))
- {
- return node;
- }
-
- var runtimeHelpers = context.GetAvaloniaTypes().RuntimeHelpers;
- var markerMethodName = "Resolve" + nodeTypeName;
- var markerMethod = runtimeHelpers.FindMethod(m => m.Name == markerMethodName && m.Parameters.Count == 3);
- if (markerMethod is null)
- {
- throw new XamlParseException($"Marker method \"{markerMethodName}\" was not found for the \"{nodeTypeName}\" node", node);
- }
-
- return new XamlValueWithManipulationNode(
- node,
- new AssetIncludeMethodNode(node, markerMethod, originalAssetPath),
- new XamlManipulationGroupNode(node, directives));
- }
-
- private class AssetIncludeMethodNode : XamlAstNode, IXamlAstValueNode, IXamlAstILEmitableNode
- {
- private readonly IXamlMethod _method;
- private readonly string _originalAssetPath;
-
- public AssetIncludeMethodNode(
- IXamlAstNode original, IXamlMethod method, string originalAssetPath)
- : base(original)
- {
- _method = method;
- _originalAssetPath = originalAssetPath;
- }
-
- public IXamlAstTypeReference Type => new XamlAstClrTypeReference(this, _method.ReturnType, false);
-
- public XamlILNodeEmitResult Emit(XamlEmitContext context, IXamlILEmitter codeGen)
- {
- var absoluteSource = _originalAssetPath;
- if (absoluteSource.StartsWith("/"))
- {
- // Avoid Uri class here to avoid potential problems with escaping.
- // Keeping string as close to the original as possible.
- var absoluteBaseUrl = context.RuntimeContext.BaseUrl;
- absoluteSource = absoluteBaseUrl.Substring(0, absoluteBaseUrl.LastIndexOf('/')) + absoluteSource;
- }
-
- codeGen.Ldstr(absoluteSource);
- codeGen.Ldc_I4(Line);
- codeGen.Ldc_I4(Position);
- codeGen.EmitCall(_method);
-
- return XamlILNodeEmitResult.Type(0, _method.ReturnType);
- }
- }
-}
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlConstructorServiceProviderTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlConstructorServiceProviderTransformer.cs
index 35e2624ff9..de654c4595 100644
--- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlConstructorServiceProviderTransformer.cs
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlConstructorServiceProviderTransformer.cs
@@ -21,7 +21,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
c.IsPublic && !c.IsStatic && c.Parameters.Count == 1 && c.Parameters[0]
.Equals(sp)))
{
- on.Arguments.Add(new InjectServiceProviderNode(sp, on));
+ on.Arguments.Add(new InjectServiceProviderNode(sp, on, true));
}
}
}
@@ -29,19 +29,33 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
return node;
}
- class InjectServiceProviderNode : XamlAstNode, IXamlAstValueNode,IXamlAstNodeNeedsParentStack,
+ internal class InjectServiceProviderNode : XamlAstNode, IXamlAstValueNode,IXamlAstNodeNeedsParentStack,
IXamlAstEmitableNode
{
- public InjectServiceProviderNode(IXamlType type, IXamlLineInfo lineInfo) : base(lineInfo)
+ private readonly bool _inheritContext;
+
+ public InjectServiceProviderNode(IXamlType type, IXamlLineInfo lineInfo, bool inheritContext) : base(lineInfo)
{
+ _inheritContext = inheritContext;
Type = new XamlAstClrTypeReference(lineInfo, type, false);
}
public IXamlAstTypeReference Type { get; }
- public bool NeedsParentStack => true;
+ public bool NeedsParentStack => _inheritContext;
public XamlILNodeEmitResult Emit(XamlEmitContext context, IXamlILEmitter codeGen)
{
- codeGen.Ldloc(context.ContextLocal);
+ if (_inheritContext)
+ {
+ codeGen.Ldloc(context.ContextLocal);
+ }
+ else
+ {
+ var method = context.GetAvaloniaTypes().RuntimeHelpers
+ .FindMethod(m => m.Name == "CreateRootServiceProviderV2");
+ codeGen.EmitCall(method);
+ context.RuntimeContext.Factory(codeGen);
+ }
+
return XamlILNodeEmitResult.Type(0, Type.GetClrType());
}
}
diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs
index f547888d6e..d07c354945 100644
--- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs
+++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs
@@ -17,16 +17,6 @@ namespace Avalonia.Markup.Xaml.XamlIl.Runtime
{
public static class XamlIlRuntimeHelpers
{
- public static IStyle ResolveStyleInclude(string absoluteSource, int line, int position)
- {
- return new StyleInclude((Uri)null) { Source = new Uri(absoluteSource) }.Loaded;
- }
-
- public static IResourceDictionary ResolveResourceInclude(string absoluteSource, int line, int position)
- {
- return new ResourceInclude((Uri)null) { Source = new Uri(absoluteSource) }.Loaded;
- }
-
public static Func DeferredTransformationFactoryV1(Func builder,
IServiceProvider provider)
{