Skip to content

Commit

Permalink
Allow XAML assembly to access full user assembly
Browse files Browse the repository at this point in the history
Fixes #22
  • Loading branch information
Kir-Antipov committed Dec 16, 2024
1 parent a1aebc6 commit 7a86eb3
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 15 deletions.
4 changes: 2 additions & 2 deletions src/HotAvalonia/Helpers/AvaloniaControlHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ public static object Load(string xaml, Uri uri, object? control, Type? controlTy
// `TextChanged`, etc.). To circumvent this problem, we need to
// patch the dynamic XAML assembly with `IgnoresAccessChecksToAttribute`.
controlType ??= control?.GetType();
if (controlType is not null)
AvaloniaRuntimeXamlScanner.DynamicXamlAssembly?.AllowAccessTo(controlType);
if (controlType?.Assembly is Assembly controlAssembly)
AvaloniaRuntimeXamlScanner.DynamicXamlAssembly?.AllowAccessTo(controlAssembly);

string xamlWithDynamicComponents = MakeStaticComponentsDynamic(xaml);
HashSet<MethodInfo> oldPopulateMethods = new(AvaloniaRuntimeXamlScanner.FindDynamicPopulateMethods(uri));
Expand Down
10 changes: 5 additions & 5 deletions src/HotAvalonia/Reflection/DynamicAssembly.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,14 @@ public DynamicAssembly(Assembly assembly)
public Assembly Assembly => _assembly;

/// <summary>
/// Grants the dynamic assembly access to the specified type.
/// Grants the dynamic assembly access to another assembly.
/// </summary>
/// <param name="type">The <see cref="Type"/> to allow access to.</param>
public virtual void AllowAccessTo(Type type)
/// <param name="assembly">The assembly to allow access to.</param>
public virtual void AllowAccessTo(Assembly assembly)
{
_ = type ?? throw new ArgumentNullException(nameof(type));
_ = assembly ?? throw new ArgumentNullException(nameof(assembly));

if (_assembly is AssemblyBuilder assemblyBuilder)
assemblyBuilder.AllowAccessTo(type);
assemblyBuilder.AllowAccessTo(assembly);
}
}
70 changes: 62 additions & 8 deletions src/HotAvalonia/Reflection/DynamicSreAssembly.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Collections;
using System.Reflection;
using System.Reflection.Emit;
using Avalonia.Markup.Xaml;
Expand Down Expand Up @@ -64,12 +65,16 @@ private static Type CreateDynamicSreAssemblyType()
// private readonly SreTypeSystem _system;
FieldBuilder systemFieldBuilder = typeBuilder.DefineField("_system", sreTypeSystem, FieldAttributes.Private | FieldAttributes.InitOnly);

// private readonly HashSet<Assembly> _assemblies;
FieldBuilder assembliesFieldBuilder = typeBuilder.DefineField("_assemblies", typeof(HashSet<Assembly>), FieldAttributes.Private | FieldAttributes.InitOnly);

// private readonly Dictionary<string, SreType> _types;
FieldBuilder typesFieldBuilder = typeBuilder.DefineField("_types", typeof(Dictionary<,>).MakeGenericType(typeof(string), sreType), FieldAttributes.Private | FieldAttributes.InitOnly);

// public DynamicSreAssembly(Assembly xamlAssembly, SreTypeSystem system) : base(xamlAssembly)
// {
// _system = system;
// _assemblies = new();
// _types = new();
// }
ConstructorBuilder ctorBuilder = typeBuilder.DefineConstructor(
Expand All @@ -85,7 +90,10 @@ private static Type CreateDynamicSreAssemblyType()
ctorIl.Emit(OpCodes.Ldarg_2);
ctorIl.Emit(OpCodes.Stfld, systemFieldBuilder);
ctorIl.Emit(OpCodes.Ldarg_0);
ctorIl.Emit(OpCodes.Newobj, typesFieldBuilder.FieldType.GetConstructor(Type.EmptyTypes));
ctorIl.Emit(OpCodes.Newobj, assembliesFieldBuilder.FieldType.GetInstanceConstructor()!);
ctorIl.Emit(OpCodes.Stfld, assembliesFieldBuilder);
ctorIl.Emit(OpCodes.Ldarg_0);
ctorIl.Emit(OpCodes.Newobj, typesFieldBuilder.FieldType.GetInstanceConstructor()!);
ctorIl.Emit(OpCodes.Stfld, typesFieldBuilder);
ctorIl.Emit(OpCodes.Ret);

Expand All @@ -106,30 +114,76 @@ private static Type CreateDynamicSreAssemblyType()
getCustomAttributesIl.Emit(OpCodes.Call, typeof(Array).GetMethod(nameof(Array.Empty), Type.EmptyTypes).MakeGenericMethod(customAttribute));
getCustomAttributesIl.Emit(OpCodes.Ret);

// public override void AllowAccessTo(Type type)
// public override void AllowAccessTo(Assembly assembly)
// {
// base.AllowAccessTo(type);
// _types[type.FullName] = _system.ResolveType(type);
// if (_assemblies.Contains(assembly))
// return;
//
// base.AllowAccessTo(assembly);
// foreach (Type type in assembly.GetLoadedTypes())
// {
// if (!type.IsPublic)
// _types[type.FullName] = _system.ResolveType(type);
// }
// _assemblies.Add(assembly);
// }
MethodInfo allowDeclaration = typeof(DynamicAssembly).GetMethod(nameof(DynamicAssembly.AllowAccessTo), [typeof(Type)]);
MethodBuilder allowBuilder = typeBuilder.DefineMethod(nameof(DynamicAssembly.AllowAccessTo), VirtualMethod ^ MethodAttributes.NewSlot, typeof(void), [typeof(Type)]);
MethodInfo allowDeclaration = typeof(DynamicAssembly).GetMethod(nameof(DynamicAssembly.AllowAccessTo), [typeof(Assembly)]);
MethodBuilder allowBuilder = typeBuilder.DefineMethod(nameof(DynamicAssembly.AllowAccessTo), VirtualMethod ^ MethodAttributes.NewSlot, typeof(void), [typeof(Assembly)]);
typeBuilder.DefineMethodOverride(allowBuilder, allowDeclaration);
ILGenerator allowIl = allowBuilder.GetILGenerator();
LocalBuilder allowIlEnumerator = allowIl.DeclareLocal(typeof(IEnumerator<Type>));
LocalBuilder allowIlCurrent = allowIl.DeclareLocal(typeof(Type));
Label allowIlEnd = allowIl.DefineLabel();
Label allowIlLoopHead = allowIl.DefineLabel();
Label allowIlLoopNext = allowIl.DefineLabel();
allowIl.Emit(OpCodes.Ldarg_0);
allowIl.Emit(OpCodes.Ldfld, assembliesFieldBuilder);
allowIl.Emit(OpCodes.Ldarg_1);
allowIl.Emit(OpCodes.Call, assembliesFieldBuilder.FieldType.GetMethod(nameof(HashSet<Assembly>.Contains), [typeof(Assembly)])!);
allowIl.Emit(OpCodes.Brtrue_S, allowIlEnd);
allowIl.Emit(OpCodes.Ldarg_0);
allowIl.Emit(OpCodes.Ldarg_1);
allowIl.Emit(OpCodes.Call, allowDeclaration);
if (sreTypeSystem != typeof(object))
{
allowIl.Emit(OpCodes.Ldarg_1);
allowIl.Emit(OpCodes.Call, typeof(AssemblyHelper).GetStaticMethod(nameof(AssemblyHelper.GetLoadedTypes), [typeof(Assembly)])!);
allowIl.Emit(OpCodes.Callvirt, typeof(IEnumerable<Type>).GetMethod(nameof(IEnumerable<Type>.GetEnumerator), Type.EmptyTypes)!);
allowIl.Emit(OpCodes.Stloc_S, allowIlEnumerator);
allowIl.Emit(OpCodes.Br_S, allowIlLoopHead);

allowIl.MarkLabel(allowIlLoopNext);
allowIl.Emit(OpCodes.Ldloc_S, allowIlEnumerator);
allowIl.Emit(OpCodes.Callvirt, typeof(IEnumerator<Type>).GetProperty(nameof(IEnumerator<Type>.Current))!.GetMethod!);
allowIl.Emit(OpCodes.Dup);
allowIl.Emit(OpCodes.Stloc_S, allowIlCurrent);
allowIl.Emit(OpCodes.Callvirt, typeof(Type).GetProperty(nameof(Type.IsPublic))!.GetMethod!);
allowIl.Emit(OpCodes.Brtrue_S, allowIlLoopHead);

allowIl.Emit(OpCodes.Ldarg_0);
allowIl.Emit(OpCodes.Ldfld, typesFieldBuilder);
allowIl.Emit(OpCodes.Ldarg_1);
allowIl.Emit(OpCodes.Ldloc_S, allowIlCurrent);
allowIl.Emit(OpCodes.Callvirt, typeof(Type).GetProperty(nameof(Type.FullName))!.GetMethod!);
allowIl.Emit(OpCodes.Ldarg_0);
allowIl.Emit(OpCodes.Ldfld, systemFieldBuilder);
allowIl.Emit(OpCodes.Ldarg_1);
allowIl.Emit(OpCodes.Ldloc_S, allowIlCurrent);
allowIl.Emit(OpCodes.Callvirt, sreTypeSystem.GetInstanceMethod("ResolveType", [typeof(Type)])!);
allowIl.Emit(OpCodes.Callvirt, typesFieldBuilder.FieldType.GetProperty("Item", sreType, [typeof(string)])!.SetMethod);

allowIl.MarkLabel(allowIlLoopHead);
allowIl.Emit(OpCodes.Ldloc_S, allowIlEnumerator);
allowIl.Emit(OpCodes.Callvirt, typeof(IEnumerator).GetMethod(nameof(IEnumerator.MoveNext), Type.EmptyTypes)!);
allowIl.Emit(OpCodes.Brtrue_S, allowIlLoopNext);

allowIl.Emit(OpCodes.Ldloc_S, allowIlEnumerator);
allowIl.Emit(OpCodes.Callvirt, typeof(IDisposable).GetMethod(nameof(IDisposable.Dispose), Type.EmptyTypes)!);
}
allowIl.Emit(OpCodes.Ldarg_0);
allowIl.Emit(OpCodes.Ldfld, assembliesFieldBuilder);
allowIl.Emit(OpCodes.Ldarg_1);
allowIl.Emit(OpCodes.Call, typeof(HashSet<Assembly>).GetMethod(nameof(HashSet<Assembly>.Add), [typeof(Assembly)])!);
allowIl.Emit(OpCodes.Pop);
allowIl.MarkLabel(allowIlEnd);
allowIl.Emit(OpCodes.Ret);

// public IXamlType? FindType(string fullName)
Expand Down

0 comments on commit 7a86eb3

Please sign in to comment.