XamlIlClrPropertyInfoHelper.cs 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers;
  5. using XamlX.Ast;
  6. using XamlX.Transform;
  7. using XamlX.TypeSystem;
  8. using XamlX.IL;
  9. using XamlX.Emit;
  10. namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
  11. {
  12. class XamlIlClrPropertyInfoEmitter
  13. {
  14. private readonly IXamlTypeBuilder<IXamlILEmitter> _builder;
  15. private Dictionary<string, List<(IXamlProperty prop, IXamlMethod get)>> _fields
  16. = new Dictionary<string, List<(IXamlProperty prop, IXamlMethod get)>>();
  17. public XamlIlClrPropertyInfoEmitter(IXamlTypeBuilder<IXamlILEmitter> builder)
  18. {
  19. _builder = builder;
  20. }
  21. static string GetKey(IXamlProperty property, string indexerArgumentsKey)
  22. {
  23. var baseKey = property.Getter.DeclaringType.GetFullName() + "." + property.Name;
  24. if (indexerArgumentsKey is null)
  25. {
  26. return baseKey;
  27. }
  28. return baseKey + $"[{indexerArgumentsKey}]";
  29. }
  30. public IXamlType Emit(XamlEmitContext<IXamlILEmitter, XamlILNodeEmitResult> context, IXamlILEmitter codeGen, IXamlProperty property, IEnumerable<IXamlAstValueNode> indexerArguments = null, string indexerArgumentsKey = null)
  31. {
  32. indexerArguments = indexerArguments ?? Enumerable.Empty<IXamlAstValueNode>();
  33. var types = context.GetAvaloniaTypes();
  34. IXamlMethod Get()
  35. {
  36. var key = GetKey(property, indexerArgumentsKey);
  37. if (!_fields.TryGetValue(key, out var lst))
  38. _fields[key] = lst = new List<(IXamlProperty prop, IXamlMethod get)>();
  39. foreach (var cached in lst)
  40. {
  41. if (
  42. ((cached.prop.Getter == null && property.Getter == null) ||
  43. cached.prop.Getter?.Equals(property.Getter) == true) &&
  44. ((cached.prop.Setter == null && property.Setter == null) ||
  45. cached.prop.Setter?.Equals(property.Setter) == true)
  46. )
  47. return cached.get;
  48. }
  49. var name = lst.Count == 0 ? key : key + "_" + Guid.NewGuid().ToString("N");
  50. var field = _builder.DefineField(types.IPropertyInfo, name + "!Field", false, true);
  51. void Load(IXamlMethod m, IXamlILEmitter cg)
  52. {
  53. cg
  54. .Ldarg_0();
  55. if (m.DeclaringType.IsValueType)
  56. cg.Unbox(m.DeclaringType);
  57. else
  58. cg.Castclass(m.DeclaringType);
  59. foreach (var indexerArg in indexerArguments)
  60. {
  61. context.Emit(indexerArg, cg, indexerArg.Type.GetClrType());
  62. }
  63. }
  64. var getter = property.Getter == null ?
  65. null :
  66. _builder.DefineMethod(types.XamlIlTypes.Object,
  67. new[] {types.XamlIlTypes.Object}, name + "!Getter", false, true, false);
  68. if (getter != null)
  69. {
  70. Load(property.Getter, getter.Generator);
  71. getter.Generator.EmitCall(property.Getter);
  72. if (property.Getter.ReturnType.IsValueType)
  73. getter.Generator.Box(property.Getter.ReturnType);
  74. getter.Generator.Ret();
  75. }
  76. var setter = property.Setter == null ?
  77. null :
  78. _builder.DefineMethod(types.XamlIlTypes.Void,
  79. new[] {types.XamlIlTypes.Object, types.XamlIlTypes.Object},
  80. name + "!Setter", false, true, false);
  81. if (setter != null)
  82. {
  83. Load(property.Setter, setter.Generator);
  84. setter.Generator.Ldarg(1);
  85. if (property.Setter.Parameters[0].IsValueType)
  86. setter.Generator.Unbox_Any(property.Setter.Parameters[0]);
  87. else
  88. setter.Generator.Castclass(property.Setter.Parameters[0]);
  89. setter.Generator
  90. .EmitCall(property.Setter, true)
  91. .Ret();
  92. }
  93. var get = _builder.DefineMethod(types.IPropertyInfo, Array.Empty<IXamlType>(),
  94. name + "!Property", true, true, false);
  95. var ctor = types.ClrPropertyInfo.Constructors.First(c =>
  96. c.Parameters.Count == 4 && c.IsStatic == false);
  97. var cacheMiss = get.Generator.DefineLabel();
  98. get.Generator
  99. .Ldsfld(field)
  100. .Brfalse(cacheMiss)
  101. .Ldsfld(field)
  102. .Ret()
  103. .MarkLabel(cacheMiss)
  104. .Ldstr(property.Name);
  105. void EmitFunc(IXamlILEmitter emitter, IXamlMethod method, IXamlType del)
  106. {
  107. if (method == null)
  108. emitter.Ldnull();
  109. else
  110. {
  111. emitter
  112. .Ldnull()
  113. .Ldftn(method)
  114. .Newobj(del.Constructors.First(c =>
  115. c.Parameters.Count == 2 &&
  116. c.Parameters[0].Equals(context.Configuration.WellKnownTypes.Object)));
  117. }
  118. }
  119. EmitFunc(get.Generator, getter, ctor.Parameters[1]);
  120. EmitFunc(get.Generator, setter, ctor.Parameters[2]);
  121. get.Generator
  122. .Ldtype(property.PropertyType)
  123. .Newobj(ctor)
  124. .Stsfld(field)
  125. .Ldsfld(field)
  126. .Ret();
  127. lst.Add((property, get));
  128. return get;
  129. }
  130. codeGen.EmitCall(Get());
  131. return types.IPropertyInfo;
  132. }
  133. }
  134. }