31 changed files with 827 additions and 34 deletions
@ -0,0 +1,10 @@ |
|||
using System; |
|||
|
|||
namespace Avalonia.Metadata |
|||
{ |
|||
[AttributeUsage(AttributeTargets.Class)] |
|||
public class UsableDuringInitializationAttribute : Attribute |
|||
{ |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,47 @@ |
|||
using System; |
|||
using System.ComponentModel; |
|||
using System.Linq; |
|||
using Avalonia.Markup.Xaml.XamlIl.Runtime; |
|||
using Portable.Xaml.Markup; |
|||
|
|||
namespace Avalonia.Markup.Xaml |
|||
{ |
|||
internal static class Extensions |
|||
{ |
|||
public static T GetService<T>(this IServiceProvider sp) => (T)sp.GetService(typeof(T)); |
|||
|
|||
|
|||
public static Uri GetContextBaseUri(this IServiceProvider ctx) |
|||
{ |
|||
var properService = ctx.GetService<IUriContext>(); |
|||
if (properService != null) |
|||
return properService.BaseUri; |
|||
// Ugly hack with casts
|
|||
return Portable.Xaml.ComponentModel.TypeDescriptorExtensions.GetBaseUri((ITypeDescriptorContext)ctx); |
|||
} |
|||
|
|||
public static T GetFirstParent<T>(this IServiceProvider ctx) where T : class |
|||
{ |
|||
var parentStack = ctx.GetService<IAvaloniaXamlIlParentStackProvider>(); |
|||
if (parentStack != null) |
|||
return parentStack.Parents.OfType<T>().FirstOrDefault(); |
|||
return Portable.Xaml.ComponentModel.TypeDescriptorExtensions.GetFirstAmbientValue<T>((ITypeDescriptorContext)ctx); |
|||
} |
|||
|
|||
public static T GetLastParent<T>(this IServiceProvider ctx) where T : class |
|||
{ |
|||
var parentStack = ctx.GetService<IAvaloniaXamlIlParentStackProvider>(); |
|||
if (parentStack != null) |
|||
return parentStack.Parents.OfType<T>().LastOrDefault(); |
|||
return Portable.Xaml.ComponentModel.TypeDescriptorExtensions.GetLastOrDefaultAmbientValue<T>( |
|||
(ITypeDescriptorContext)ctx); |
|||
} |
|||
|
|||
public static Type ResolveType(this IServiceProvider ctx, string namespacePrefix, string type) |
|||
{ |
|||
var tr = ctx.GetService<IXamlTypeResolver>(); |
|||
string name = string.IsNullOrEmpty(namespacePrefix) ? type : $"{namespacePrefix}:{type}"; |
|||
return tr?.Resolve(name); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,160 @@ |
|||
using System; |
|||
using System.Globalization; |
|||
using System.IO; |
|||
using System.Linq; |
|||
using System.Linq.Expressions; |
|||
using System.Reflection; |
|||
using System.Reflection.Emit; |
|||
using System.Runtime.InteropServices; |
|||
using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions; |
|||
using Avalonia.Platform; |
|||
using XamlIl.Ast; |
|||
using XamlIl.Transform; |
|||
using XamlIl.TypeSystem; |
|||
|
|||
namespace Avalonia.Markup.Xaml.XamlIl |
|||
{ |
|||
public static class AvaloniaXamlIlRuntimeCompiler |
|||
{ |
|||
private static SreTypeSystem _typeSystem; |
|||
private static ModuleBuilder _builder; |
|||
private static XamlIlLanguageTypeMappings _mappings; |
|||
private static XamlIlXmlnsMappings _xmlns; |
|||
private static AssemblyBuilder _asm; |
|||
private static bool _canSave; |
|||
|
|||
public static void DumpRuntimeCompilationResults() |
|||
{ |
|||
var saveMethod = _asm.GetType().GetMethods() |
|||
.FirstOrDefault(m => m.Name == "Save" && m.GetParameters().Length == 1); |
|||
if (saveMethod == null) |
|||
throw new PlatformNotSupportedException(); |
|||
try |
|||
{ |
|||
_builder.CreateGlobalFunctions(); |
|||
saveMethod.Invoke(_asm, new Object[] {"XamlIlLoader.ildump"}); |
|||
} |
|||
catch |
|||
{ |
|||
//Ignore
|
|||
} |
|||
} |
|||
|
|||
static void Initialize() |
|||
{ |
|||
if (_typeSystem == null) |
|||
_typeSystem = new SreTypeSystem(); |
|||
if (_builder == null) |
|||
{ |
|||
_canSave = !AvaloniaLocator.Current.GetService<IRuntimePlatform>().GetRuntimeInfo().IsCoreClr; |
|||
var name = new AssemblyName(Guid.NewGuid().ToString("N")); |
|||
if (_canSave) |
|||
{ |
|||
var define = AppDomain.CurrentDomain.GetType().GetMethods() |
|||
.FirstOrDefault(m => m.Name == "DefineDynamicAssembly" |
|||
&& m.GetParameters().Length == 3 && |
|||
m.GetParameters()[2].ParameterType == typeof(string)); |
|||
if (define != null) |
|||
_asm = (AssemblyBuilder)define.Invoke(AppDomain.CurrentDomain, new object[] |
|||
{ |
|||
name, (AssemblyBuilderAccess)3, |
|||
Path.GetDirectoryName(typeof(AvaloniaXamlIlRuntimeCompiler).Assembly.GetModules()[0] |
|||
.FullyQualifiedName) |
|||
}); |
|||
else |
|||
_canSave = false; |
|||
} |
|||
|
|||
if(_asm == null) |
|||
_asm = AssemblyBuilder.DefineDynamicAssembly(name, |
|||
AssemblyBuilderAccess.RunAndCollect); |
|||
|
|||
_builder = _asm.DefineDynamicModule("XamlIlLoader.ildump"); |
|||
|
|||
} |
|||
|
|||
if (_mappings == null) |
|||
_mappings = AvaloniaXamlIlLanguage.Configure(_typeSystem); |
|||
if (_xmlns == null) |
|||
_xmlns = XamlIlXmlnsMappings.Resolve(_typeSystem, _mappings); |
|||
} |
|||
|
|||
public static object Load(Stream stream, Assembly localAssembly, object rootInstance, Uri uri) |
|||
{ |
|||
var success = false; |
|||
try |
|||
{ |
|||
var rv = LoadCore(stream, localAssembly, rootInstance, uri); |
|||
success = true; |
|||
return rv; |
|||
} |
|||
finally |
|||
{ |
|||
if(!success && _canSave) |
|||
DumpRuntimeCompilationResults(); |
|||
} |
|||
} |
|||
|
|||
static object LoadCore(Stream stream, Assembly localAssembly, object rootInstance, Uri uri) |
|||
{ |
|||
string xaml; |
|||
using (var sr = new StreamReader(stream)) |
|||
xaml = sr.ReadToEnd(); |
|||
Initialize(); |
|||
var asm = localAssembly == null ? null : _typeSystem.GetAssembly(localAssembly); |
|||
var compiler = new AvaloniaXamlIlCompiler(new XamlIlTransformerConfiguration(_typeSystem, asm, |
|||
_mappings, _xmlns, CustomValueConverter)); |
|||
var tb = _builder.DefineType("Builder_" + Guid.NewGuid().ToString("N") + "_" + uri); |
|||
|
|||
IXamlIlType overrideType = null; |
|||
if (rootInstance != null) |
|||
{ |
|||
overrideType = _typeSystem.GetType(rootInstance.GetType()); |
|||
} |
|||
|
|||
compiler.ParseAndCompile(xaml, uri?.ToString(), _typeSystem.CreateTypeBuilder(tb), overrideType); |
|||
var created = tb.CreateTypeInfo(); |
|||
|
|||
var isp = Expression.Parameter(typeof(IServiceProvider)); |
|||
if (rootInstance == null) |
|||
{ |
|||
var createCb = Expression.Lambda<Func<IServiceProvider, object>>( |
|||
Expression.Convert(Expression.Call( |
|||
created.GetMethod(AvaloniaXamlIlCompiler.BuildName), isp), typeof(object)), isp).Compile(); |
|||
return createCb(null); |
|||
} |
|||
else |
|||
{ |
|||
var epar = Expression.Parameter(typeof(object)); |
|||
var populate = created.GetMethod(AvaloniaXamlIlCompiler.PopulateName); |
|||
isp = Expression.Parameter(typeof(IServiceProvider)); |
|||
var populateCb = Expression.Lambda<Action<IServiceProvider, object>>( |
|||
Expression.Call(populate, isp, Expression.Convert(epar, populate.GetParameters()[1].ParameterType)), |
|||
isp, epar).Compile(); |
|||
populateCb(null, rootInstance); |
|||
return rootInstance; |
|||
} |
|||
} |
|||
|
|||
private static bool CustomValueConverter(XamlIlAstTransformationContext context, |
|||
IXamlIlAstValueNode node, IXamlIlType type, out IXamlIlAstValueNode result) |
|||
{ |
|||
if (type.FullName == "System.TimeSpan" |
|||
&& node is XamlIlAstTextNode tn |
|||
&& !tn.Text.Contains(":")) |
|||
{ |
|||
var seconds = double.Parse(tn.Text, CultureInfo.InvariantCulture); |
|||
result = new XamlIlStaticOrTargetedReturnMethodCallNode(tn, |
|||
type.FindMethod("FromSeconds", type, false, context.Configuration.WellKnownTypes.Double), |
|||
new[] |
|||
{ |
|||
new XamlIlConstantNode(tn, context.Configuration.WellKnownTypes.Double, seconds) |
|||
}); |
|||
return true; |
|||
} |
|||
|
|||
result = null; |
|||
return false; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
using System.Linq; |
|||
using XamlIl.Transform; |
|||
using XamlIl.TypeSystem; |
|||
|
|||
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions |
|||
{ |
|||
public class AvaloniaPropertyDescriptorEmitter |
|||
{ |
|||
public static bool Emit(XamlIlEmitContext context, IXamlIlEmitter emitter, IXamlIlProperty property) |
|||
{ |
|||
var type = (property.Getter ?? property.Setter).DeclaringType; |
|||
var name = property.Name + "Property"; |
|||
var found = type.Fields.FirstOrDefault(f => f.IsStatic && f.Name == name); |
|||
if (found == null) |
|||
return false; |
|||
|
|||
emitter.Ldsfld(found); |
|||
return true; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,61 @@ |
|||
using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers; |
|||
using XamlIl; |
|||
using XamlIl.Ast; |
|||
using XamlIl.Parsers; |
|||
using XamlIl.Transform; |
|||
using XamlIl.Transform.Transformers; |
|||
using XamlIl.TypeSystem; |
|||
|
|||
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions |
|||
{ |
|||
public class AvaloniaXamlIlCompiler : XamlIlCompiler |
|||
{ |
|||
public AvaloniaXamlIlCompiler(XamlIlTransformerConfiguration configuration) : base(configuration, true) |
|||
{ |
|||
|
|||
// Before everything else
|
|||
|
|||
Transformers.Insert(0, new XNameTransformer()); |
|||
Transformers.Insert(1, new IgnoredDirectivesTransformer()); |
|||
|
|||
|
|||
// Targeted
|
|||
|
|||
Transformers.Insert(Transformers.FindIndex(x => x is XamlIlXamlPropertyValueTransformer), |
|||
new KnownPseudoMarkupExtensionsTransformer()); |
|||
|
|||
// After everything else
|
|||
|
|||
Transformers.Add(new AddNameScopeRegistration()); |
|||
|
|||
} |
|||
|
|||
public const string PopulateName = "__AvaloniaXamlIlPopulate"; |
|||
public const string BuildName = "__AvaloniaXamlIlBuild"; |
|||
|
|||
public void ParseAndCompile(string xaml, string baseUri, IXamlIlTypeBuilder tb, IXamlIlType overrideRootType) |
|||
{ |
|||
var parsed = XDocumentXamlIlParser.Parse(xaml); |
|||
|
|||
if (overrideRootType != null) |
|||
{ |
|||
var rootObject = (XamlIlAstObjectNode)parsed.Root; |
|||
|
|||
var originalType = XamlIlTypeReferenceResolver.ResolveType(CreateTransformationContext(parsed, true), |
|||
(XamlIlAstXmlTypeReference)rootObject.Type, true); |
|||
|
|||
if (!originalType.IsAssignableFrom(overrideRootType)) |
|||
throw new XamlIlLoadException( |
|||
$"Unable to substitute {originalType.GetFqn()} with {overrideRootType.GetFqn()}", rootObject); |
|||
rootObject.Type = new XamlIlAstClrTypeReference(rootObject, overrideRootType); |
|||
} |
|||
|
|||
Transform(parsed); |
|||
Compile(parsed, tb, PopulateName, BuildName, |
|||
"__AvaloniaXamlIlContext", "__AvaloniaXamlIlNsInfo", baseUri); |
|||
|
|||
} |
|||
|
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,153 @@ |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using XamlIl.Transform; |
|||
using XamlIl.TypeSystem; |
|||
|
|||
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions |
|||
{ |
|||
/* |
|||
This file is used in the build task. |
|||
ONLY use types from netstandard and XamlIl. NO dependencies on Avalonia are allowed. Only strings. |
|||
No, nameof isn't welcome here either |
|||
*/ |
|||
|
|||
class AvaloniaXamlIlLanguage |
|||
{ |
|||
public static XamlIlLanguageTypeMappings Configure(IXamlIlTypeSystem typeSystem) |
|||
{ |
|||
var runtimeHelpers = typeSystem.GetType("Avalonia.Markup.Xaml.XamlIl.Runtime.XamlIlRuntimeHelpers"); |
|||
var assignBindingAttribute = typeSystem.GetType("Avalonia.Data.AssignBindingAttribute"); |
|||
var bindingType = typeSystem.GetType("Avalonia.Data.IBinding"); |
|||
var rv = new XamlIlLanguageTypeMappings(typeSystem) |
|||
{ |
|||
SupportInitialize = typeSystem.GetType("Avalonia.ISupportInitialize"), |
|||
XmlnsAttributes = |
|||
{ |
|||
typeSystem.GetType("Avalonia.Metadata.XmlnsDefinitionAttribute"), |
|||
typeSystem.FindType("Portable.Xaml.Markup.XmlnsDefinitionAttribute") |
|||
}, |
|||
ContentAttributes = |
|||
{ |
|||
typeSystem.GetType("Avalonia.Metadata.ContentAttribute") |
|||
}, |
|||
ProvideValueTarget = typeSystem.GetType("Portable.Xaml.Markup.IProvideValueTarget"), |
|||
RootObjectProvider = typeSystem.GetType("Portable.Xaml.IRootObjectProvider"), |
|||
UriContextProvider = typeSystem.GetType("Portable.Xaml.Markup.IUriContext"), |
|||
ParentStackProvider = |
|||
typeSystem.GetType("Avalonia.Markup.Xaml.XamlIl.Runtime.IAvaloniaXamlIlParentStackProvider"), |
|||
|
|||
XmlNamespaceInfoProvider = |
|||
typeSystem.GetType("Avalonia.Markup.Xaml.XamlIl.Runtime.IAvaloniaXamlIlXmlNamespaceInfoProvider"), |
|||
DeferredContentPropertyAttributes = {typeSystem.GetType("Avalonia.Metadata.TemplateContentAttribute")}, |
|||
DeferredContentExecutorCustomization = |
|||
runtimeHelpers.FindMethod(m => m.Name == "DeferredTransformationFactoryV1"), |
|||
UsableDuringInitializationAttributes = |
|||
{ |
|||
typeSystem.GetType("Portable.Xaml.Markup.UsableDuringInitializationAttribute"), |
|||
typeSystem.GetType("Avalonia.Metadata.UsableDuringInitializationAttribute"), |
|||
}, |
|||
MarkupExtensionCustomResultTypes = |
|||
{ |
|||
bindingType |
|||
}, |
|||
MarkupExtensionCustomResultHandler = |
|||
runtimeHelpers.FindMethod(m => m.Name == "ApplyNonMatchingMarkupExtensionV1"), |
|||
InnerServiceProviderFactoryMethod = |
|||
runtimeHelpers.FindMethod(m => m.Name == "CreateInnerServiceProviderV1"), |
|||
ShouldIgnoreMarkupExtensionCustomResultForProperty = (prop, customType) => |
|||
{ |
|||
if (customType.Equals(bindingType) && |
|||
prop.CustomAttributes.Any(a => a.Type.Equals(assignBindingAttribute))) |
|||
return true; |
|||
return false; |
|||
}, |
|||
ProvideValueTargetPropertyEmitter = AvaloniaPropertyDescriptorEmitter.Emit, |
|||
}; |
|||
rv.CustomAttributeResolver = new AttributeResolver(typeSystem, rv); |
|||
return rv; |
|||
} |
|||
|
|||
class AttributeResolver : IXamlIlCustomAttributeResolver |
|||
{ |
|||
private readonly IXamlIlType _typeConverterAttribute; |
|||
|
|||
private readonly List<KeyValuePair<IXamlIlType, IXamlIlType>> _converters = |
|||
new List<KeyValuePair<IXamlIlType, IXamlIlType>>(); |
|||
|
|||
private readonly IXamlIlType _avaloniaList; |
|||
private readonly IXamlIlType _avaloniaListConverter; |
|||
|
|||
|
|||
public AttributeResolver(IXamlIlTypeSystem typeSystem, XamlIlLanguageTypeMappings mappings) |
|||
{ |
|||
_typeConverterAttribute = mappings.TypeConverterAttributes.First(); |
|||
|
|||
void AddType(IXamlIlType type, IXamlIlType conv) |
|||
=> _converters.Add(new KeyValuePair<IXamlIlType, IXamlIlType>(type, conv)); |
|||
|
|||
void Add(string type, string conv) |
|||
=> AddType(typeSystem.GetType(type), typeSystem.GetType(conv)); |
|||
|
|||
|
|||
Add("Avalonia.AvaloniaProperty","Avalonia.Markup.Xaml.Converters.AvaloniaPropertyTypeConverter"); |
|||
Add("Avalonia.Media.Imaging.IBitmap","Avalonia.Markup.Xaml.Converters.BitmapTypeConverter"); |
|||
var ilist = typeSystem.GetType("System.Collections.Generic.IList`1"); |
|||
AddType(ilist.MakeGenericType(typeSystem.GetType("Avalonia.Point")), |
|||
typeSystem.GetType("Avalonia.Markup.Xaml.Converters.PointsListTypeConverter")); |
|||
Add("Avalonia.Controls.Templates.IMemberSelector", |
|||
"Avalonia.Markup.Xaml.Converters.MemberSelectorTypeConverter"); |
|||
Add("Avalonia.Styling.Selector","Avalonia.Markup.Xaml.Converters.SelectorTypeConverter"); |
|||
Add("Avalonia.Controls.WindowIcon","Avalonia.Markup.Xaml.Converters.IconTypeConverter"); |
|||
Add("System.Globalization.CultureInfo", "System.ComponentModel.CultureInfoConverter"); |
|||
Add("System.Uri", "Avalonia.Markup.Xaml.Converters.AvaloniaUriTypeConverter"); |
|||
Add("System.TimeSpan", "Avalonia.Markup.Xaml.Converters.TimeSpanTypeConverter"); |
|||
Add("Avalonia.Media.FontFamily","Avalonia.Markup.Xaml.Converters.FontFamilyTypeConverter"); |
|||
_avaloniaList = typeSystem.GetType("Avalonia.Collections.AvaloniaList`1"); |
|||
_avaloniaListConverter = typeSystem.GetType("Avalonia.Collections.AvaloniaListConverter`1"); |
|||
} |
|||
|
|||
IXamlIlType LookupConverter(IXamlIlType type) |
|||
{ |
|||
foreach(var p in _converters) |
|||
if (p.Key.Equals(type)) |
|||
return p.Value; |
|||
if (type.GenericTypeDefinition?.Equals(_avaloniaList) == true) |
|||
return _avaloniaListConverter.MakeGenericType(type.GenericArguments[0]); |
|||
return null; |
|||
} |
|||
|
|||
class ConstructedAttribute : IXamlIlCustomAttribute |
|||
{ |
|||
public bool Equals(IXamlIlCustomAttribute other) => false; |
|||
|
|||
public IXamlIlType Type { get; } |
|||
public List<object> Parameters { get; } |
|||
public Dictionary<string, object> Properties { get; } |
|||
|
|||
public ConstructedAttribute(IXamlIlType type, List<object> parameters, Dictionary<string, object> properties) |
|||
{ |
|||
Type = type; |
|||
Parameters = parameters ?? new List<object>(); |
|||
Properties = properties ?? new Dictionary<string, object>(); |
|||
} |
|||
} |
|||
|
|||
public IXamlIlCustomAttribute GetCustomAttribute(IXamlIlType type, IXamlIlType attributeType) |
|||
{ |
|||
if (attributeType.Equals(_typeConverterAttribute)) |
|||
{ |
|||
var conv = LookupConverter(type); |
|||
if (conv != null) |
|||
return new ConstructedAttribute(_typeConverterAttribute, new List<object>() {conv}, null); |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
|
|||
public IXamlIlCustomAttribute GetCustomAttribute(IXamlIlProperty property, IXamlIlType attributeType) |
|||
{ |
|||
return null; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,4 @@ |
|||
### Important |
|||
|
|||
Only use XamlIl and netstandard types here since these sources will be later |
|||
used by MSBuild task |
|||
@ -0,0 +1,79 @@ |
|||
using XamlIl.Ast; |
|||
using XamlIl.Transform; |
|||
using XamlIl.TypeSystem; |
|||
|
|||
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers |
|||
{ |
|||
public class AddNameScopeRegistration : IXamlIlAstTransformer |
|||
{ |
|||
public IXamlIlAstNode Transform(XamlIlAstTransformationContext context, IXamlIlAstNode node) |
|||
{ |
|||
if (node is XamlIlPropertyAssignmentNode pa |
|||
&& pa.Property.Name == "Name" |
|||
&& pa.Property.Setter.DeclaringType.FullName == "Avalonia.StyledElement") |
|||
{ |
|||
return new ScopeRegistrationNode(pa); |
|||
|
|||
} |
|||
|
|||
return node; |
|||
} |
|||
|
|||
class ScopeRegistrationNode : XamlIlAstNode, IXamlIlAstManipulationNode, IXamlIlAstEmitableNode |
|||
{ |
|||
public IXamlIlAstValueNode Value { get; set; } |
|||
private IXamlIlProperty _property; |
|||
|
|||
public ScopeRegistrationNode(XamlIlPropertyAssignmentNode pa) : base(pa) |
|||
{ |
|||
_property = pa.Property; |
|||
Value = pa.Value; |
|||
} |
|||
|
|||
public override void VisitChildren(XamlIlAstVisitorDelegate visitor) |
|||
=> Value = (IXamlIlAstValueNode)Value.Visit(visitor); |
|||
|
|||
public XamlIlNodeEmitResult Emit(XamlIlEmitContext context, IXamlIlEmitter codeGen) |
|||
{ |
|||
using (var nameLoc = context.GetLocal(context.Configuration.WellKnownTypes.String)) |
|||
using (var targetLoc = context.GetLocal(context.Configuration.WellKnownTypes.Object)) |
|||
|
|||
{ |
|||
var exts = context.Configuration.TypeSystem.GetType("Avalonia.Controls.NameScopeExtensions"); |
|||
var findNameScope = exts.FindMethod(m => m.Name == "FindNameScope"); |
|||
var registerMethod = findNameScope.ReturnType.FindMethod(m => m.Name == "Register"); |
|||
var exit = codeGen.DefineLabel(); |
|||
var call = codeGen.DefineLabel(); |
|||
|
|||
context.Emit(Value, codeGen, _property.PropertyType); |
|||
|
|||
codeGen |
|||
// var name = {EmitName()}
|
|||
.Stloc(nameLoc.Local) |
|||
// var target = {target}
|
|||
.Dup() |
|||
.Stloc(targetLoc.Local) |
|||
// {target}.Name = name
|
|||
.Dup() |
|||
.Ldloc(nameLoc.Local) |
|||
.EmitCall(_property.Setter) |
|||
// var scope = {target}.FindNameScope()
|
|||
.EmitCall(findNameScope) |
|||
// if({scope} != null) goto call;
|
|||
.Dup() |
|||
.Brtrue(call) |
|||
// goto: exit
|
|||
.Pop() |
|||
.Br(exit) |
|||
// call: {scope}.Register(name, target);
|
|||
.MarkLabel(call) |
|||
.Ldloc(nameLoc.Local) |
|||
.Ldloc(targetLoc.Local) |
|||
.EmitCall(registerMethod) |
|||
.MarkLabel(exit); |
|||
} |
|||
return XamlIlNodeEmitResult.Void; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
using System.Linq; |
|||
using XamlIl; |
|||
using XamlIl.Ast; |
|||
using XamlIl.Transform; |
|||
|
|||
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers |
|||
{ |
|||
public class IgnoredDirectivesTransformer : IXamlIlAstTransformer |
|||
{ |
|||
public IXamlIlAstNode Transform(XamlIlAstTransformationContext context, IXamlIlAstNode node) |
|||
{ |
|||
if (node is XamlIlAstObjectNode no) |
|||
{ |
|||
foreach (var d in no.Children.OfType<XamlIlAstXmlDirective>().ToList()) |
|||
{ |
|||
if (d.Namespace == XamlNamespaces.Xaml2006) |
|||
{ |
|||
if (d.Name == "Class") |
|||
no.Children.Remove(d); |
|||
} |
|||
} |
|||
} |
|||
|
|||
return node; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
using System.Collections.Generic; |
|||
using XamlIl.Ast; |
|||
using XamlIl.Transform; |
|||
|
|||
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers |
|||
{ |
|||
public class KnownPseudoMarkupExtensionsTransformer : IXamlIlAstTransformer |
|||
{ |
|||
private static readonly List<string> s_knownPseudoExtensions = new List<string> |
|||
{ |
|||
"Avalonia.Data.TemplateBinding", |
|||
"Avalonia.Data.MultiBinding", |
|||
}; |
|||
|
|||
public IXamlIlAstNode Transform(XamlIlAstTransformationContext context, IXamlIlAstNode node) |
|||
{ |
|||
if (node is XamlIlAstXamlPropertyValueNode pn |
|||
&& pn.Values.Count == 1 |
|||
&& s_knownPseudoExtensions.Contains(pn.Values[0].Type.GetClrType().FullName)) |
|||
return new XamlIlMarkupExtensionNode(node, pn.Property.GetClrProperty(), |
|||
null, pn.Values[0], null); |
|||
else |
|||
return node; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,33 @@ |
|||
using XamlIl; |
|||
using XamlIl.Ast; |
|||
using XamlIl.Transform; |
|||
|
|||
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers |
|||
{ |
|||
public class XNameTransformer : IXamlIlAstTransformer |
|||
{ |
|||
|
|||
/// <summary>
|
|||
/// Converts x:Name directives to regular Name assignments
|
|||
/// </summary>
|
|||
/// <returns></returns>
|
|||
public IXamlIlAstNode Transform(XamlIlAstTransformationContext context, IXamlIlAstNode node) |
|||
{ |
|||
if (node is XamlIlAstObjectNode on) |
|||
{ |
|||
foreach (var ch in on.Children) |
|||
{ |
|||
if (ch is XamlIlAstXmlDirective d |
|||
&& d.Namespace == XamlNamespaces.Xaml2006 |
|||
&& d.Name == "Name") |
|||
return new XamlIlAstXamlPropertyValueNode(d, |
|||
new XamlIlAstNamePropertyReference(d, on.Type, "Name", on.Type), |
|||
d.Values); |
|||
} |
|||
} |
|||
|
|||
return node; |
|||
|
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Avalonia.Markup.Xaml.XamlIl.Runtime |
|||
{ |
|||
public interface IAvaloniaXamlIlParentStackProvider |
|||
{ |
|||
IEnumerable<object> Parents { get; } |
|||
} |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Avalonia.Markup.Xaml.XamlIl.Runtime |
|||
{ |
|||
public interface IAvaloniaXamlIlXmlNamespaceInfoProvider |
|||
{ |
|||
IReadOnlyDictionary<string, IReadOnlyList<AvaloniaXamlIlXmlNamespaceInfo>> XmlNamespaces { get; } |
|||
} |
|||
|
|||
public class AvaloniaXamlIlXmlNamespaceInfo |
|||
{ |
|||
public string ClrNamespace { get; set; } |
|||
public string ClrAssemblyName { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,121 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Reflection; |
|||
using Avalonia.Controls; |
|||
using Avalonia.Data; |
|||
using Portable.Xaml.Markup; |
|||
// ReSharper disable UnusedMember.Global
|
|||
// ReSharper disable UnusedParameter.Global
|
|||
|
|||
namespace Avalonia.Markup.Xaml.XamlIl.Runtime |
|||
{ |
|||
public static class XamlIlRuntimeHelpers |
|||
{ |
|||
public static Func<IServiceProvider, object> DeferredTransformationFactoryV1(Func<IServiceProvider, object> builder, |
|||
IServiceProvider provider) |
|||
{ |
|||
// We need to preserve the first IResourceNode from the parent XAML,
|
|||
// so Static/DynamicResource extensions can get it
|
|||
var resourceNode = provider.GetService<IAvaloniaXamlIlParentStackProvider>().Parents.OfType<IResourceNode>() |
|||
.FirstOrDefault(); |
|||
|
|||
return sp => builder(new DeferredParentServiceProvider(sp, resourceNode)); |
|||
} |
|||
|
|||
class DeferredParentServiceProvider : IAvaloniaXamlIlParentStackProvider, IServiceProvider |
|||
{ |
|||
private readonly IServiceProvider _parentProvider; |
|||
private readonly IResourceNode _parentResourceNode; |
|||
|
|||
public DeferredParentServiceProvider(IServiceProvider parentProvider, IResourceNode parentResourceNode) |
|||
{ |
|||
_parentProvider = parentProvider; |
|||
_parentResourceNode = parentResourceNode; |
|||
} |
|||
|
|||
public IEnumerable<object> Parents => GetParents(); |
|||
|
|||
IEnumerable<object> GetParents() |
|||
{ |
|||
if(_parentResourceNode == null) |
|||
yield break; |
|||
yield return _parentResourceNode; |
|||
} |
|||
|
|||
public object GetService(Type serviceType) |
|||
{ |
|||
if (serviceType == typeof(IAvaloniaXamlIlParentStackProvider)) |
|||
return this; |
|||
return _parentProvider?.GetService(serviceType); |
|||
} |
|||
} |
|||
|
|||
|
|||
public static void ApplyNonMatchingMarkupExtensionV1(object target, object property, IServiceProvider prov, |
|||
object value) |
|||
{ |
|||
if (value is IBinding b) |
|||
{ |
|||
if (property is AvaloniaProperty p) |
|||
((AvaloniaObject)target).Bind(p, b); |
|||
else |
|||
throw new ArgumentException("Attempt to apply binding to non-avalonia property " + property); |
|||
} |
|||
else |
|||
throw new ArgumentException("Don't know what to do with " + value.GetType()); |
|||
} |
|||
|
|||
public static IServiceProvider CreateInnerServiceProviderV1(IServiceProvider compiled) |
|||
=> new InnerServiceProvider(compiled); |
|||
|
|||
class InnerServiceProvider : IServiceProvider |
|||
{ |
|||
private readonly IServiceProvider _compiledProvider; |
|||
private XamlTypeResolver _resolver; |
|||
|
|||
public InnerServiceProvider(IServiceProvider compiledProvider) |
|||
{ |
|||
_compiledProvider = compiledProvider; |
|||
} |
|||
public object GetService(Type serviceType) |
|||
{ |
|||
if (serviceType == typeof(IXamlTypeResolver)) |
|||
return _resolver ?? (_resolver = new XamlTypeResolver( |
|||
_compiledProvider.GetService<IAvaloniaXamlIlXmlNamespaceInfoProvider>())); |
|||
return null; |
|||
} |
|||
} |
|||
|
|||
class XamlTypeResolver : IXamlTypeResolver |
|||
{ |
|||
private readonly IAvaloniaXamlIlXmlNamespaceInfoProvider _nsInfo; |
|||
|
|||
public XamlTypeResolver(IAvaloniaXamlIlXmlNamespaceInfoProvider nsInfo) |
|||
{ |
|||
_nsInfo = nsInfo; |
|||
} |
|||
|
|||
public Type Resolve(string qualifiedTypeName) |
|||
{ |
|||
var sp = qualifiedTypeName.Split(new[] {':'}, 2); |
|||
var (ns, name) = sp.Length == 1 ? ("", qualifiedTypeName) : (sp[0], sp[1]); |
|||
var namespaces = _nsInfo.XmlNamespaces; |
|||
var dic = (Dictionary<string, IReadOnlyList<AvaloniaXamlIlXmlNamespaceInfo>>)namespaces; |
|||
if (!namespaces.TryGetValue(ns, out var lst)) |
|||
throw new ArgumentException("Unable to resolve namespace for type " + qualifiedTypeName); |
|||
foreach (var entry in lst) |
|||
{ |
|||
var asm = Assembly.Load(new AssemblyName(entry.ClrAssemblyName)); |
|||
var resolved = asm.GetType(entry.ClrNamespace + "." + name); |
|||
if (resolved != null) |
|||
return resolved; |
|||
} |
|||
|
|||
throw new ArgumentException( |
|||
$"Unable to resolve type {qualifiedTypeName} from any of the following locations: " + |
|||
string.Join(",", lst.Select(e => $"`{e.ClrAssemblyName}:{e.ClrNamespace}.{name}`"))); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1 @@ |
|||
Subproject commit 89bc4d268d7ee48d0907cec24175a99759d0bc4a |
|||
Loading…
Reference in new issue