AvaloniaXamlIlLanguage.cs 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. using System.Collections.Generic;
  2. using System.Globalization;
  3. using System.Linq;
  4. using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers;
  5. using XamlIl;
  6. using XamlIl.Ast;
  7. using XamlIl.Transform;
  8. using XamlIl.TypeSystem;
  9. namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
  10. {
  11. /*
  12. This file is used in the build task.
  13. ONLY use types from netstandard and XamlIl. NO dependencies on Avalonia are allowed. Only strings.
  14. No, nameof isn't welcome here either
  15. */
  16. class AvaloniaXamlIlLanguage
  17. {
  18. public static XamlIlLanguageTypeMappings Configure(IXamlIlTypeSystem typeSystem)
  19. {
  20. var runtimeHelpers = typeSystem.GetType("Avalonia.Markup.Xaml.XamlIl.Runtime.XamlIlRuntimeHelpers");
  21. var assignBindingAttribute = typeSystem.GetType("Avalonia.Data.AssignBindingAttribute");
  22. var bindingType = typeSystem.GetType("Avalonia.Data.IBinding");
  23. var rv = new XamlIlLanguageTypeMappings(typeSystem)
  24. {
  25. SupportInitialize = typeSystem.GetType("System.ComponentModel.ISupportInitialize"),
  26. XmlnsAttributes =
  27. {
  28. typeSystem.GetType("Avalonia.Metadata.XmlnsDefinitionAttribute"),
  29. },
  30. ContentAttributes =
  31. {
  32. typeSystem.GetType("Avalonia.Metadata.ContentAttribute")
  33. },
  34. ProvideValueTarget = typeSystem.GetType("Avalonia.Markup.Xaml.IProvideValueTarget"),
  35. RootObjectProvider = typeSystem.GetType("Avalonia.Markup.Xaml.IRootObjectProvider"),
  36. RootObjectProviderIntermediateRootPropertyName = "IntermediateRootObject",
  37. UriContextProvider = typeSystem.GetType("Avalonia.Markup.Xaml.IUriContext"),
  38. ParentStackProvider =
  39. typeSystem.GetType("Avalonia.Markup.Xaml.XamlIl.Runtime.IAvaloniaXamlIlParentStackProvider"),
  40. XmlNamespaceInfoProvider =
  41. typeSystem.GetType("Avalonia.Markup.Xaml.XamlIl.Runtime.IAvaloniaXamlIlXmlNamespaceInfoProvider"),
  42. DeferredContentPropertyAttributes = {typeSystem.GetType("Avalonia.Metadata.TemplateContentAttribute")},
  43. DeferredContentExecutorCustomization =
  44. runtimeHelpers.FindMethod(m => m.Name == "DeferredTransformationFactoryV1"),
  45. UsableDuringInitializationAttributes =
  46. {
  47. typeSystem.GetType("Avalonia.Metadata.UsableDuringInitializationAttribute"),
  48. },
  49. InnerServiceProviderFactoryMethod =
  50. runtimeHelpers.FindMethod(m => m.Name == "CreateInnerServiceProviderV1"),
  51. ProvideValueTargetPropertyEmitter = XamlIlAvaloniaPropertyHelper.Emit,
  52. };
  53. rv.CustomAttributeResolver = new AttributeResolver(typeSystem, rv);
  54. rv.ContextTypeBuilderCallback = (b, c) => EmitNameScopeField(rv, typeSystem, b, c);
  55. return rv;
  56. }
  57. public const string ContextNameScopeFieldName = "AvaloniaNameScope";
  58. private static void EmitNameScopeField(XamlIlLanguageTypeMappings mappings,
  59. IXamlIlTypeSystem typeSystem,
  60. IXamlIlTypeBuilder typebuilder, IXamlIlEmitter constructor)
  61. {
  62. var nameScopeType = typeSystem.FindType("Avalonia.Controls.INameScope");
  63. var field = typebuilder.DefineField(nameScopeType,
  64. ContextNameScopeFieldName, true, false);
  65. constructor
  66. .Ldarg_0()
  67. .Ldarg(1)
  68. .Ldtype(nameScopeType)
  69. .EmitCall(mappings.ServiceProvider.GetMethod(new FindMethodMethodSignature("GetService",
  70. typeSystem.FindType("System.Object"), typeSystem.FindType("System.Type"))))
  71. .Stfld(field);
  72. }
  73. class AttributeResolver : IXamlIlCustomAttributeResolver
  74. {
  75. private readonly IXamlIlType _typeConverterAttribute;
  76. private readonly List<KeyValuePair<IXamlIlType, IXamlIlType>> _converters =
  77. new List<KeyValuePair<IXamlIlType, IXamlIlType>>();
  78. private readonly IXamlIlType _avaloniaList;
  79. private readonly IXamlIlType _avaloniaListConverter;
  80. public AttributeResolver(IXamlIlTypeSystem typeSystem, XamlIlLanguageTypeMappings mappings)
  81. {
  82. _typeConverterAttribute = mappings.TypeConverterAttributes.First();
  83. void AddType(IXamlIlType type, IXamlIlType conv)
  84. => _converters.Add(new KeyValuePair<IXamlIlType, IXamlIlType>(type, conv));
  85. void Add(string type, string conv)
  86. => AddType(typeSystem.GetType(type), typeSystem.GetType(conv));
  87. Add("Avalonia.Media.Imaging.IBitmap","Avalonia.Markup.Xaml.Converters.BitmapTypeConverter");
  88. var ilist = typeSystem.GetType("System.Collections.Generic.IList`1");
  89. AddType(ilist.MakeGenericType(typeSystem.GetType("Avalonia.Point")),
  90. typeSystem.GetType("Avalonia.Markup.Xaml.Converters.PointsListTypeConverter"));
  91. Add("Avalonia.Controls.Templates.IMemberSelector",
  92. "Avalonia.Markup.Xaml.Converters.MemberSelectorTypeConverter");
  93. Add("Avalonia.Controls.WindowIcon","Avalonia.Markup.Xaml.Converters.IconTypeConverter");
  94. Add("System.Globalization.CultureInfo", "System.ComponentModel.CultureInfoConverter");
  95. Add("System.Uri", "Avalonia.Markup.Xaml.Converters.AvaloniaUriTypeConverter");
  96. Add("System.TimeSpan", "Avalonia.Markup.Xaml.Converters.TimeSpanTypeConverter");
  97. Add("Avalonia.Media.FontFamily","Avalonia.Markup.Xaml.Converters.FontFamilyTypeConverter");
  98. _avaloniaList = typeSystem.GetType("Avalonia.Collections.AvaloniaList`1");
  99. _avaloniaListConverter = typeSystem.GetType("Avalonia.Collections.AvaloniaListConverter`1");
  100. }
  101. IXamlIlType LookupConverter(IXamlIlType type)
  102. {
  103. foreach(var p in _converters)
  104. if (p.Key.Equals(type))
  105. return p.Value;
  106. if (type.GenericTypeDefinition?.Equals(_avaloniaList) == true)
  107. return _avaloniaListConverter.MakeGenericType(type.GenericArguments[0]);
  108. return null;
  109. }
  110. class ConstructedAttribute : IXamlIlCustomAttribute
  111. {
  112. public bool Equals(IXamlIlCustomAttribute other) => false;
  113. public IXamlIlType Type { get; }
  114. public List<object> Parameters { get; }
  115. public Dictionary<string, object> Properties { get; }
  116. public ConstructedAttribute(IXamlIlType type, List<object> parameters, Dictionary<string, object> properties)
  117. {
  118. Type = type;
  119. Parameters = parameters ?? new List<object>();
  120. Properties = properties ?? new Dictionary<string, object>();
  121. }
  122. }
  123. public IXamlIlCustomAttribute GetCustomAttribute(IXamlIlType type, IXamlIlType attributeType)
  124. {
  125. if (attributeType.Equals(_typeConverterAttribute))
  126. {
  127. var conv = LookupConverter(type);
  128. if (conv != null)
  129. return new ConstructedAttribute(_typeConverterAttribute, new List<object>() {conv}, null);
  130. }
  131. return null;
  132. }
  133. public IXamlIlCustomAttribute GetCustomAttribute(IXamlIlProperty property, IXamlIlType attributeType)
  134. {
  135. return null;
  136. }
  137. }
  138. public static bool CustomValueConverter(XamlIlAstTransformationContext context,
  139. IXamlIlAstValueNode node, IXamlIlType type, out IXamlIlAstValueNode result)
  140. {
  141. if (type.FullName == "System.TimeSpan"
  142. && node is XamlIlAstTextNode tn
  143. && !tn.Text.Contains(":"))
  144. {
  145. var seconds = double.Parse(tn.Text, CultureInfo.InvariantCulture);
  146. result = new XamlIlStaticOrTargetedReturnMethodCallNode(tn,
  147. type.FindMethod("FromSeconds", type, false, context.Configuration.WellKnownTypes.Double),
  148. new[]
  149. {
  150. new XamlIlConstantNode(tn, context.Configuration.WellKnownTypes.Double, seconds)
  151. });
  152. return true;
  153. }
  154. if (type.FullName == "Avalonia.AvaloniaProperty")
  155. {
  156. var scope = context.ParentNodes().OfType<AvaloniaXamlIlTargetTypeMetadataNode>().FirstOrDefault();
  157. if (scope == null)
  158. throw new XamlIlLoadException("Unable to find the parent scope for AvaloniaProperty lookup", node);
  159. if (!(node is XamlIlAstTextNode text))
  160. throw new XamlIlLoadException("Property should be a text node", node);
  161. result = XamlIlAvaloniaPropertyHelper.CreateNode(context, text.Text, scope.TargetType, text);
  162. return true;
  163. }
  164. result = null;
  165. return false;
  166. }
  167. }
  168. }