From 7f904f6c892e91138b531791452fd8c189f1924c Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Sun, 1 Nov 2020 18:03:56 +0300 Subject: [PATCH 1/3] Emit IgnoresAccessChecksToAttribute for runtime-generated XAML --- build.sh | 2 +- .../AvaloniaXamlIlRuntimeCompiler.cs | 53 +++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/build.sh b/build.sh index a40e00f815..bd162fab9b 100755 --- a/build.sh +++ b/build.sh @@ -47,7 +47,7 @@ if [ -f "$DOTNET_GLOBAL_FILE" ]; then fi # If dotnet is installed locally, and expected version is not set or installation matches the expected version -if [[ -x "$(command -v dotnet)" && (-z ${DOTNET_VERSION+x} || $(dotnet --version) == "$DOTNET_VERSION") ]]; then +if [[ -x "$(command -v dotnet)" && (-z ${DOTNET_VERSION+x} || $(dotnet --version) == "$DOTNET_VERSION") || "$SKIP_DOTNET_DOWNLOAD" == "1" ]]; then export DOTNET_EXE="$(command -v dotnet)" else DOTNET_DIRECTORY="$TEMP_DIRECTORY/dotnet-unix" diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs index d9b4cd70da..c69a651af2 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs @@ -26,6 +26,7 @@ namespace Avalonia.Markup.Xaml.XamlIl { #if !RUNTIME_XAML_CECIL private static SreTypeSystem _sreTypeSystem; + private static Type _ignoresAccessChecksFromAttribute; private static ModuleBuilder _sreBuilder; private static IXamlType _sreContextType; private static XamlLanguageTypeMappings _sreMappings; @@ -94,8 +95,58 @@ namespace Avalonia.Markup.Xaml.XamlIl _sreTypeSystem.CreateTypeBuilder( _sreBuilder.DefineType("XamlIlContext")), _sreTypeSystem, _sreMappings, _sreEmitMappings); + if (_ignoresAccessChecksFromAttribute == null) + _ignoresAccessChecksFromAttribute = EmitIgnoresAccessCheckAttributeDefinition(_sreBuilder); } + static Type EmitIgnoresAccessCheckAttributeDefinition(ModuleBuilder builder) + { + var tb = builder.DefineType("System.Runtime.CompilerServices.IgnoresAccessChecksToAttribute", + TypeAttributes.Class | TypeAttributes.Public, typeof(Attribute)); + var field = tb.DefineField("_name", typeof(string), FieldAttributes.Private); + var propGet = tb.DefineMethod("get_AssemblyName", MethodAttributes.Public, typeof(string), + Array.Empty()); + var propGetIl = propGet.GetILGenerator(); + propGetIl.Emit(OpCodes.Ldarg_0); + propGetIl.Emit(OpCodes.Ldfld, field); + propGetIl.Emit(OpCodes.Ret); + var prop = tb.DefineProperty("AssemblyName", PropertyAttributes.None, typeof(string), Array.Empty()); + prop.SetGetMethod(propGet); + + + var ctor = tb.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, + new[] { typeof(string) }); + var ctorIl = ctor.GetILGenerator(); + ctorIl.Emit(OpCodes.Ldarg_0); + ctorIl.Emit(OpCodes.Ldarg_1); + ctorIl.Emit(OpCodes.Stfld, field); + ctorIl.Emit(OpCodes.Ldarg_0); + ctorIl.Emit(OpCodes.Call, typeof(Attribute) + .GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) + .First(x => x.GetParameters().Length == 0)); + + ctorIl.Emit(OpCodes.Ret); + + tb.SetCustomAttribute(new CustomAttributeBuilder( + typeof(AttributeUsageAttribute).GetConstructor(new[] { typeof(AttributeTargets) }), + new object[] { AttributeTargets.Assembly }, + new[] { typeof(AttributeUsageAttribute).GetProperty("AllowMultiple") }, + new object[] { true })); + + return tb.CreateTypeInfo(); + } + + static void EmitIgnoresAccessCheckToAttribute(AssemblyName assemblyName) + { + var name = assemblyName.Name; + var key = assemblyName.GetPublicKey(); + if (key != null) + name += ", PublicKey=" + BitConverter.ToString(key).Replace("-", "").ToUpperInvariant(); + _sreAsm.SetCustomAttribute(new CustomAttributeBuilder( + _ignoresAccessChecksFromAttribute.GetConstructors()[0], + new object[] { name })); + } + static object LoadSre(string xaml, Assembly localAssembly, object rootInstance, Uri uri, bool isDesignMode) { @@ -118,6 +169,8 @@ namespace Avalonia.Markup.Xaml.XamlIl { InitializeSre(); + if (localAssembly?.FullName != null) + EmitIgnoresAccessCheckToAttribute(localAssembly.GetName()); var asm = localAssembly == null ? null : _sreTypeSystem.GetAssembly(localAssembly); var tb = _sreBuilder.DefineType("Builder_" + Guid.NewGuid().ToString("N") + "_" + uri); var clrPropertyBuilder = tb.DefineNestedType("ClrProperties_" + Guid.NewGuid().ToString("N")); From 8f6107969cdbe487c58b28d4f6ba6620a9fbd542 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Sun, 1 Nov 2020 19:16:56 +0300 Subject: [PATCH 2/3] Extra checks --- .../AvaloniaXamlIlRuntimeCompiler.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs index c69a651af2..75de1f55ce 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs @@ -139,6 +139,8 @@ namespace Avalonia.Markup.Xaml.XamlIl static void EmitIgnoresAccessCheckToAttribute(AssemblyName assemblyName) { var name = assemblyName.Name; + if(string.IsNullOrWhiteSpace(name)) + return; var key = assemblyName.GetPublicKey(); if (key != null) name += ", PublicKey=" + BitConverter.ToString(key).Replace("-", "").ToUpperInvariant(); @@ -169,7 +171,7 @@ namespace Avalonia.Markup.Xaml.XamlIl { InitializeSre(); - if (localAssembly?.FullName != null) + if (localAssembly?.GetName() != null) EmitIgnoresAccessCheckToAttribute(localAssembly.GetName()); var asm = localAssembly == null ? null : _sreTypeSystem.GetAssembly(localAssembly); var tb = _sreBuilder.DefineType("Builder_" + Guid.NewGuid().ToString("N") + "_" + uri); From 6b662026ef1aa4d303c83d087b74733d2d951e79 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Sun, 1 Nov 2020 20:35:56 +0300 Subject: [PATCH 3/3] Fix support for packages without strong name --- .../AvaloniaXamlIlRuntimeCompiler.cs | 2 +- .../DesignerSupportTests.cs | 61 ++++++++++++++++--- 2 files changed, 54 insertions(+), 9 deletions(-) diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs index 75de1f55ce..ece90762cb 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs @@ -142,7 +142,7 @@ namespace Avalonia.Markup.Xaml.XamlIl if(string.IsNullOrWhiteSpace(name)) return; var key = assemblyName.GetPublicKey(); - if (key != null) + if (key != null && key.Length != 0) name += ", PublicKey=" + BitConverter.ToString(key).Replace("-", "").ToUpperInvariant(); _sreAsm.SetCustomAttribute(new CustomAttributeBuilder( _ignoresAccessChecksFromAttribute.GetConstructors()[0], diff --git a/tests/Avalonia.DesignerSupport.Tests/DesignerSupportTests.cs b/tests/Avalonia.DesignerSupport.Tests/DesignerSupportTests.cs index dd704bc775..d76ed9042f 100644 --- a/tests/Avalonia.DesignerSupport.Tests/DesignerSupportTests.cs +++ b/tests/Avalonia.DesignerSupport.Tests/DesignerSupportTests.cs @@ -11,6 +11,7 @@ using System.Threading; using System.Threading.Tasks; using Avalonia.Remote.Protocol; using Avalonia.Remote.Protocol.Designer; +using Avalonia.Remote.Protocol.Viewport; using Xunit; using Xunit.Extensions; @@ -31,19 +32,38 @@ namespace Avalonia.DesignerSupport.Tests @"..\..\..\..\..\tests/Avalonia.DesignerSupport.TestApp/bin/$BUILD/netcoreapp3.1/", "Avalonia.DesignerSupport.TestApp", "Avalonia.DesignerSupport.TestApp.dll", - @"..\..\..\..\..\tests\Avalonia.DesignerSupport.TestApp\MainWindow.xaml"), + @"..\..\..\..\..\tests\Avalonia.DesignerSupport.TestApp\MainWindow.xaml", + "win32"), InlineData( @"..\..\..\..\..\samples\ControlCatalog.NetCore\bin\$BUILD\netcoreapp3.1\", "ControlCatalog.NetCore", "ControlCatalog.dll", - @"..\..\..\..\..\samples\ControlCatalog\MainWindow.xaml")] + @"..\..\..\..\..\samples\ControlCatalog\MainWindow.xaml", + "win32"), + InlineData( + @"..\..\..\..\..\tests/Avalonia.DesignerSupport.TestApp/bin/$BUILD/netcoreapp3.1/", + "Avalonia.DesignerSupport.TestApp", + "Avalonia.DesignerSupport.TestApp.dll", + @"..\..\..\..\..\tests\Avalonia.DesignerSupport.TestApp\MainWindow.xaml", + "avalonia-remote"), + InlineData( + @"..\..\..\..\..\samples\ControlCatalog.NetCore\bin\$BUILD\netcoreapp3.1\", + "ControlCatalog.NetCore", + "ControlCatalog.dll", + @"..\..\..\..\..\samples\ControlCatalog\MainWindow.xaml", + "avalonia-remote")] public async Task Designer_In_Win32_Mode_Should_Provide_Valid_Hwnd( string outputDir, string executableName, string assemblyName, - string xamlFile) + string xamlFile, + string method) { - Skip.IfNot(RuntimeInformation.IsOSPlatform(OSPlatform.Windows)); + outputDir = Path.GetFullPath(outputDir.Replace('\\', Path.DirectorySeparatorChar)); + xamlFile = Path.GetFullPath(xamlFile.Replace('\\', Path.DirectorySeparatorChar)); + + if (method == "win32") + Skip.IfNot(RuntimeInformation.IsOSPlatform(OSPlatform.Windows)); var xaml = File.ReadAllText(xamlFile); string buildType; @@ -56,6 +76,8 @@ namespace Avalonia.DesignerSupport.Tests var sessionId = Guid.NewGuid(); long handle = 0; + bool success = false; + string error = null; var resultMessageReceivedToken = new CancellationTokenSource(); @@ -71,6 +93,18 @@ namespace Avalonia.DesignerSupport.Tests if (msg is StartDesignerSessionMessage start) { Assert.Equal(sessionId, Guid.Parse(start.SessionId)); + if (method == "avalonia-remote") + { + await conn.Send(new ClientSupportedPixelFormatsMessage + { + Formats = new[] { PixelFormat.Rgba8888 } + }); + await conn.Send(new ClientViewportAllocatedMessage + { + DpiX = 96, DpiY = 96, Width = 1024, Height = 768 + }); + } + await conn.Send(new UpdateXamlMessage { AssemblyPath = Path.Combine(outputDir, assemblyName), @@ -80,8 +114,14 @@ namespace Avalonia.DesignerSupport.Tests else if (msg is UpdateXamlResultMessage result) { if (result.Error != null) + { + error = result.Error; outputHelper.WriteLine(result.Error); - handle = result.Handle != null ? long.Parse(result.Handle) : 0; + } + else + success = true; + if (method == "win32") + handle = result.Handle != null ? long.Parse(result.Handle) : 0; resultMessageReceivedToken.Cancel(); conn.Dispose(); } @@ -91,7 +131,7 @@ namespace Avalonia.DesignerSupport.Tests var cmdline = $"exec --runtimeconfig \"{outputDir}{executableName}.runtimeconfig.json\" --depsfile \"{outputDir}{executableName}.deps.json\" " + $" \"{DesignerAppPath.Replace("$BUILD", buildType)}\" " - + $"--transport tcp-bson://127.0.0.1:{port}/ --session-id {sessionId} --method win32 \"{outputDir}{executableName}.dll\""; + + $"--transport tcp-bson://127.0.0.1:{port}/ --session-id {sessionId} --method {method} \"{outputDir}{executableName}.dll\""; using (var proc = new Process { @@ -128,10 +168,15 @@ namespace Avalonia.DesignerSupport.Tests } proc.WaitForExit(); + var stdout = proc.StandardOutput.ReadToEnd(); + var stderr = proc.StandardError.ReadToEnd(); Assert.True(cancelled, $"Message Not Received.\n" + proc.StandardOutput.ReadToEnd() + "\n" + - proc.StandardError.ReadToEnd()); - Assert.NotEqual(0, handle); + stderr + "\n" + stdout); + Assert.True(success, error); + if (method == "win32") + Assert.NotEqual(0, handle); + } }