diff --git a/src/HotAvalonia/Helpers/AvaloniaControlHelper.cs b/src/HotAvalonia/Helpers/AvaloniaControlHelper.cs index 8a4d804..714772e 100644 --- a/src/HotAvalonia/Helpers/AvaloniaControlHelper.cs +++ b/src/HotAvalonia/Helpers/AvaloniaControlHelper.cs @@ -1,5 +1,6 @@ using System.Diagnostics.CodeAnalysis; using System.Reflection; +using System.Reflection.Emit; using Avalonia; using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml.XamlIl.Runtime; @@ -18,6 +19,19 @@ internal static class AvaloniaControlHelper /// private static readonly FieldInfo? s_stylesAppliedField; + /// + /// The `Type` property of the XamlX.IL.SreTypeSystem.SreType class. + /// + private static readonly PropertyInfo? s_xamlTypeProperty; + + /// + /// The instance responsible for injecting the + /// + /// callback. + /// + [SuppressMessage("CodeQuality", "IDE0052", Justification = "Injections must be kept alive.")] + private static readonly IInjection? s_sreMethodBuilderInjection; + /// /// Initializes static members of the class. /// @@ -25,6 +39,51 @@ static AvaloniaControlHelper() { FieldInfo? stylesAppliedField = typeof(StyledElement).GetField("_stylesApplied", BindingFlags.NonPublic | BindingFlags.Instance); s_stylesAppliedField = stylesAppliedField?.FieldType == typeof(bool) ? stylesAppliedField : null; + + Assembly xamlLoaderAssembly = typeof(AvaloniaRuntimeXamlLoader).Assembly; + Type? sreType = xamlLoaderAssembly.GetType("XamlX.IL.SreTypeSystem+SreType"); + s_xamlTypeProperty = sreType?.GetProperty("Type"); + + BindingFlags ctorFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; + Type? sreMethodBuilder = xamlLoaderAssembly.GetType("XamlX.IL.SreTypeSystem+SreTypeBuilder+SreMethodBuilder"); + ConstructorInfo? sreMethodBuilderCtor = sreMethodBuilder?.GetConstructors(ctorFlags).FirstOrDefault(x => x.GetParameters().Length > 1); + if (sreMethodBuilderCtor is not null) + s_sreMethodBuilderInjection = CallbackInjector.Inject(sreMethodBuilderCtor, OnNewSreMethodBuilder); + } + + /// + /// Fixes newly created XamlX.IL.SreTypeSystem.SreTypeBuilder.SreMethodBuilder + /// instances. + /// + /// + /// The used to create the + /// SreMethodBuilder instance. + /// + /// + /// The parameters used to create the SreMethodBuilder + /// instance represented by a collection of SreType objects. + /// + private static void OnNewSreMethodBuilder(MethodBuilder methodBuilder, IEnumerable parameters) + { + // `Avalonia.Markup.Xaml.Loader` does not handle scenarios where + // the control population logic needs to reference private members, + // which are commonly used for subscribing to events (e.g., `Click`, + // `TextChanged`, etc.). To circumvent this problem, we need this + // callback to track the creation of dynamic `Populate` methods and + // patch their containing assembly with `IgnoresAccessChecksToAttribute`. + if (!AvaloniaRuntimeXamlScanner.IsDynamicPopulateMethod(methodBuilder)) + return; + + if (methodBuilder.DeclaringType?.Assembly is not AssemblyBuilder assembly) + return; + + IEnumerable parameterTypes = parameters + .Where(static x => x is not null && x.GetType() == s_xamlTypeProperty?.DeclaringType) + .Select(static x => (Type?)s_xamlTypeProperty?.GetValue(x)) + .Where(static x => x is not null)!; + + foreach (Type parameterType in parameterTypes) + assembly.AllowAccessTo(parameterType); } ///