Browse Source

WIP

xamlil-debug-info
Nikita Tsukanov 7 years ago
parent
commit
f14aac365b
  1. 3
      .gitmodules
  2. 2
      samples/ControlCatalog/Pages/ButtonSpinnerPage.xaml.cs
  3. 10
      src/Avalonia.Base/Metadata/UsableDuringInitializationAttribute.cs
  4. 3
      src/Avalonia.Styling/Controls/NameScopeExtensions.cs
  5. 2
      src/Avalonia.Visuals/Visual.cs
  6. 18
      src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
  7. 1
      src/Markup/Avalonia.Markup.Xaml/AvaloniaTypeConverters.cs
  8. 15
      src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs
  9. 4
      src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaPropertyTypeConverter.cs
  10. 4
      src/Markup/Avalonia.Markup.Xaml/Converters/BitmapTypeConverter.cs
  11. 2
      src/Markup/Avalonia.Markup.Xaml/Converters/FontFamilyTypeConverter.cs
  12. 2
      src/Markup/Avalonia.Markup.Xaml/Converters/IconTypeConverter.cs
  13. 47
      src/Markup/Avalonia.Markup.Xaml/Extensions.cs
  14. 11
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/BindingExtension.cs
  15. 16
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs
  16. 2
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/ResourceInclude.cs
  17. 5
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StyleIncludeExtension.cs
  18. 4
      src/Markup/Avalonia.Markup.Xaml/PortableXaml/TypeDescriptorExtensions.cs
  19. 160
      src/Markup/Avalonia.Markup.Xaml/XamlIl/AvaloniaXamlIlRuntimeCompiler.cs
  20. 21
      src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaPropertyDescriptorEmitter.cs
  21. 61
      src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlCompiler.cs
  22. 153
      src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlLanguage.cs
  23. 4
      src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/README.md
  24. 79
      src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AddNameScopeRegistration.cs
  25. 27
      src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/IgnoredDirectivesTransformer.cs
  26. 26
      src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/KnownPseudoMarkupExtensionsTransformer.cs
  27. 33
      src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/XNameTransformer.cs
  28. 9
      src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/IAvaloniaXamlIlParentStackProvider.cs
  29. 15
      src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/IAvaloniaXamlIlXmlNamespaceInfoProviderV1.cs
  30. 121
      src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs
  31. 1
      src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github

3
.gitmodules

@ -4,3 +4,6 @@
[submodule "nukebuild/Numerge"]
path = nukebuild/Numerge
url = https://github.com/kekekeks/Numerge.git
[submodule "src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github"]
path = src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github
url = https://github.com/kekekeks/XamlIl.git

2
samples/ControlCatalog/Pages/ButtonSpinnerPage.xaml.cs

@ -18,7 +18,7 @@ namespace ControlCatalog.Pages
AvaloniaXamlLoader.Load(this);
}
private void OnSpin(object sender, SpinEventArgs e)
public void OnSpin(object sender, SpinEventArgs e)
{
var spinner = (ButtonSpinner)sender;
var txtBox = (TextBlock)spinner.Content;

10
src/Avalonia.Base/Metadata/UsableDuringInitializationAttribute.cs

@ -0,0 +1,10 @@
using System;
namespace Avalonia.Metadata
{
[AttributeUsage(AttributeTargets.Class)]
public class UsableDuringInitializationAttribute : Attribute
{
}
}

3
src/Avalonia.Styling/Controls/NameScopeExtensions.cs

@ -71,10 +71,11 @@ namespace Avalonia.Controls
{
Contract.Requires<ArgumentNullException>(control != null);
return control.GetSelfAndLogicalAncestors()
var scope = control.GetSelfAndLogicalAncestors()
.OfType<StyledElement>()
.Select(x => (x as INameScope) ?? NameScope.GetNameScope(x))
.FirstOrDefault(x => x != null);
return scope;
}
}
}

2
src/Avalonia.Visuals/Visual.cs

@ -9,6 +9,7 @@ using Avalonia.Collections;
using Avalonia.Data;
using Avalonia.Logging;
using Avalonia.Media;
using Avalonia.Metadata;
using Avalonia.Rendering;
using Avalonia.VisualTree;
@ -23,6 +24,7 @@ namespace Avalonia
/// <see cref="IRenderer"/> to render the control. To traverse the visual tree, use the
/// extension methods defined in <see cref="VisualExtensions"/>.
/// </remarks>
[UsableDuringInitialization]
public class Visual : StyledElement, IVisual
{
/// <summary>

18
src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj

@ -15,6 +15,7 @@
<Compile Include="Converters\ParseTypeConverter.cs" />
<Compile Include="Converters\SetterValueTypeConverter.cs" />
<Compile Include="Converters\TimeSpanTypeConverter.cs" />
<Compile Include="Extensions.cs" />
<Compile Include="MarkupExtensions\DynamicResourceExtension.cs" />
<Compile Include="MarkupExtensions\ResourceInclude.cs" />
<Compile Include="MarkupExtensions\StaticResourceExtension.cs" />
@ -50,8 +51,21 @@
<Compile Include="Templates\TemplateContent.cs" />
<Compile Include="Templates\TemplateLoader.cs" />
<Compile Include="Templates\TreeDataTemplate.cs" />
<Compile Include="XamlIl\AvaloniaXamlIlRuntimeCompiler.cs" />
<Compile Include="XamlIl\CompilerExtensions\AvaloniaPropertyDescriptorEmitter.cs" />
<Compile Include="XamlIl\CompilerExtensions\AvaloniaXamlIlCompiler.cs" />
<Compile Include="XamlIl\CompilerExtensions\AvaloniaXamlIlLanguage.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\AddNameScopeRegistration.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\IgnoredDirectivesTransformer.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\KnownPseudoMarkupExtensionsTransformer.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\XNameTransformer.cs" />
<Compile Include="XamlIl\Runtime\IAvaloniaXamlIlParentStackProvider.cs" />
<Compile Include="XamlIl\Runtime\IAvaloniaXamlIlXmlNamespaceInfoProviderV1.cs" />
<Compile Include="XamlIl\Runtime\XamlIlRuntimeHelpers.cs" />
<Compile Include="XamlLoadException.cs" />
<Compile Include="PortableXaml\portable.xaml.github\src\Portable.Xaml\**\*.cs" Exclude="PortableXaml\portable.xaml.github\src\Portable.Xaml\Assembly\**\*.cs" />
<Compile Include="XamlIl\xamlil.github\src\XamlIl\**\*.cs" />
<Compile Remove="XamlIl\xamlil.github\**\obj\**\*.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Avalonia.Animation\Avalonia.Animation.csproj" />
@ -63,6 +77,10 @@
<ProjectReference Include="..\..\Avalonia.Visuals\Avalonia.Visuals.csproj" />
<ProjectReference Include="..\..\Avalonia.Styling\Avalonia.Styling.csproj" />
<ProjectReference Include="..\Avalonia.Markup\Avalonia.Markup.csproj" />
<PackageReference Include="System.Reflection.Emit" Version="4.3.0" />
</ItemGroup>
<ItemGroup>
<Content Include="XamlIl\CompilerExtensions\README.md" />
</ItemGroup>
<Import Project="..\..\..\build\Rx.props" />
</Project>

1
src/Markup/Avalonia.Markup.Xaml/AvaloniaTypeConverters.cs

@ -29,6 +29,7 @@ namespace Avalonia.Markup.Xaml
/// </remarks>
public static class AvaloniaTypeConverters
{
// When adding item to that list make sure to modify AvaloniaXamlIlLanguage
private static Dictionary<Type, Type> _converters = new Dictionary<Type, Type>()
{
{ typeof(AvaloniaList<>), typeof(AvaloniaListConverter<>) },

15
src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs

@ -13,6 +13,7 @@ using System.Reflection;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Text;
using Avalonia.Markup.Xaml.XamlIl;
namespace Avalonia.Markup.Xaml
{
@ -23,6 +24,8 @@ namespace Avalonia.Markup.Xaml
{
private readonly AvaloniaXamlSchemaContext _context = GetContext();
public bool EnforceCompilerForRuntimeXaml { get; set; } = true;
public bool IsDesignMode
{
get => _context.IsDesignMode;
@ -137,13 +140,13 @@ namespace Avalonia.Markup.Xaml
using (var stream = asset.stream)
{
var absoluteUri = uri.IsAbsoluteUri ? uri : new Uri(baseUri, uri);
try
//try
{
return Load(stream, asset.assembly, rootInstance, absoluteUri);
}
catch (Exception e)
//catch (Exception e)
{
throw new XamlLoadException("Error loading xaml at " + absoluteUri + ": " + e.Message, e);
//throw new XamlLoadException("Error loading xaml at " + absoluteUri + ": " + e.Message, e);
}
}
}
@ -179,6 +182,12 @@ namespace Avalonia.Markup.Xaml
/// <returns>The loaded object.</returns>
public object Load(Stream stream, Assembly localAssembly, object rootInstance = null, Uri uri = null)
{
if (EnforceCompilerForRuntimeXaml)
{
return AvaloniaXamlIlRuntimeCompiler.Load(stream, localAssembly, rootInstance, uri);
}
var readerSettings = new XamlXmlReaderSettings()
{
BaseUri = uri,

4
src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaPropertyTypeConverter.cs

@ -28,8 +28,8 @@ namespace Avalonia.Markup.Xaml.Converters
var parser = new PropertyParser();
var (ns, owner, propertyName) = parser.Parse(new CharacterReader(((string)value).AsSpan()));
var ownerType = TryResolveOwnerByName(context, ns, owner);
var targetType = context.GetFirstAmbientValue<ControlTemplate>()?.TargetType ??
context.GetFirstAmbientValue<Style>()?.Selector?.TargetType ??
var targetType = context.GetFirstParent<ControlTemplate>()?.TargetType ??
context.GetFirstParent<Style>()?.Selector?.TargetType ??
typeof(Control);
var effectiveOwner = ownerType ?? targetType;
var property = registry.FindRegistered(effectiveOwner, propertyName);

4
src/Markup/Avalonia.Markup.Xaml/Converters/BitmapTypeConverter.cs

@ -29,7 +29,7 @@ namespace Avalonia.Markup.Xaml.Converters
return new Bitmap(uri.LocalPath);
var assets = AvaloniaLocator.Current.GetService<IAssetLoader>();
return new Bitmap(assets.Open(uri, context.GetBaseUri()));
return new Bitmap(assets.Open(uri, context.GetContextBaseUri()));
}
}
}
}

2
src/Markup/Avalonia.Markup.Xaml/Converters/FontFamilyTypeConverter.cs

@ -22,7 +22,7 @@ namespace Avalonia.Markup.Xaml.Converters
{
var s = (string)value;
return FontFamily.Parse(s, context.GetBaseUri());
return FontFamily.Parse(s, context.GetContextBaseUri());
}
}
}

2
src/Markup/Avalonia.Markup.Xaml/Converters/IconTypeConverter.cs

@ -45,7 +45,7 @@ namespace Avalonia.Markup.Xaml.Converters
if(uri.IsAbsoluteUri && uri.IsFile)
return new WindowIcon(uri.LocalPath);
var assets = AvaloniaLocator.Current.GetService<IAssetLoader>();
return new WindowIcon(assets.Open(uri, context.GetBaseUri()));
return new WindowIcon(assets.Open(uri, context.GetContextBaseUri()));
}
}
}

47
src/Markup/Avalonia.Markup.Xaml/Extensions.cs

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

11
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/BindingExtension.cs

@ -29,6 +29,11 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return ProvideTypedValue(serviceProvider);
}
public IBinding ProvideTypedValue(IServiceProvider serviceProvider)
{
var descriptorContext = (ITypeDescriptorContext)serviceProvider;
@ -49,18 +54,18 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
};
}
private static object GetDefaultAnchor(ITypeDescriptorContext context)
private static object GetDefaultAnchor(IServiceProvider context)
{
// If the target is not a control, so we need to find an anchor that will let us look
// up named controls and style resources. First look for the closest IControl in
// the context.
object anchor = context.GetFirstAmbientValue<IControl>();
object anchor = context.GetFirstParent<IControl>();
// If a control was not found, then try to find the highest-level style as the XAML
// file could be a XAML file containing only styles.
return anchor ??
context.GetService<IRootObjectProvider>()?.RootObject as IStyle ??
context.GetLastOrDefaultAmbientValue<IStyle>();
context.GetLastParent<IStyle>();
}
public IValueConverter Converter { get; set; }

16
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs

@ -30,12 +30,11 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
public override object ProvideValue(IServiceProvider serviceProvider)
{
var context = (ITypeDescriptorContext)serviceProvider;
var provideTarget = context.GetService<IProvideValueTarget>();
var provideTarget = serviceProvider.GetService<IProvideValueTarget>();
if (!(provideTarget.TargetObject is IResourceNode))
{
_anchor = GetAnchor<IResourceNode>(context);
_anchor = serviceProvider.GetFirstParent<IResourceNode>();
}
return this;
@ -56,16 +55,5 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
return null;
}
private T GetAnchor<T>(ITypeDescriptorContext context) where T : class
{
var schemaContext = context.GetService<IXamlSchemaContextProvider>().SchemaContext;
var ambientProvider = context.GetService<IAmbientProvider>();
var xamlType = schemaContext.GetXamlType(typeof(T));
// We override XamlType.CanAssignTo in BindingXamlType so the results we get back
// from GetAllAmbientValues aren't necessarily of the correct type.
return ambientProvider.GetAllAmbientValues(xamlType).OfType<T>().FirstOrDefault();
}
}
}

2
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/ResourceInclude.cs

@ -56,7 +56,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
public override object ProvideValue(IServiceProvider serviceProvider)
{
var tdc = (ITypeDescriptorContext)serviceProvider;
_baseUri = tdc?.GetBaseUri();
_baseUri = tdc?.GetContextBaseUri();
return this;
}
}

5
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StyleIncludeExtension.cs

@ -20,11 +20,10 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
public override object ProvideValue(IServiceProvider serviceProvider)
{
var tdc = (ITypeDescriptorContext)serviceProvider;
return new StyleInclude(tdc.GetBaseUri()) { Source = Source };
return new StyleInclude(serviceProvider.GetContextBaseUri()) { Source = Source };
}
public Uri Source { get; set; }
}
}
}

4
src/Markup/Avalonia.Markup.Xaml/PortableXaml/TypeDescriptorExtensions.cs

@ -44,7 +44,7 @@ namespace Portable.Xaml.ComponentModel
var amb = ctx.GetService<IAmbientProvider>();
var sc = ctx.GetService<IXamlSchemaContextProvider>().SchemaContext;
// Because GetFirstAmbientValue uses XamlType.CanAssignTo it returns values that
// Because GetFirstParent uses XamlType.CanAssignTo it returns values that
// aren't actually of the correct type. Use GetAllAmbientValues instead.
return amb.GetAllAmbientValues(sc.GetXamlType(typeof(T))).OfType<T>().FirstOrDefault();
}
@ -98,4 +98,4 @@ namespace Portable.Xaml.ComponentModel
public AvaloniaXamlContext Context { get; }
}
}
}
}

160
src/Markup/Avalonia.Markup.Xaml/XamlIl/AvaloniaXamlIlRuntimeCompiler.cs

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

21
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaPropertyDescriptorEmitter.cs

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

61
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlCompiler.cs

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

153
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlLanguage.cs

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

4
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/README.md

@ -0,0 +1,4 @@
### Important
Only use XamlIl and netstandard types here since these sources will be later
used by MSBuild task

79
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AddNameScopeRegistration.cs

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

27
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/IgnoredDirectivesTransformer.cs

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

26
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/KnownPseudoMarkupExtensionsTransformer.cs

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

33
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/XNameTransformer.cs

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

9
src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/IAvaloniaXamlIlParentStackProvider.cs

@ -0,0 +1,9 @@
using System.Collections.Generic;
namespace Avalonia.Markup.Xaml.XamlIl.Runtime
{
public interface IAvaloniaXamlIlParentStackProvider
{
IEnumerable<object> Parents { get; }
}
}

15
src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/IAvaloniaXamlIlXmlNamespaceInfoProviderV1.cs

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

121
src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs

@ -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}`")));
}
}
}
}

1
src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github

@ -0,0 +1 @@
Subproject commit 89bc4d268d7ee48d0907cec24175a99759d0bc4a
Loading…
Cancel
Save