From 1263c3b73d0af78b7be0deaf91cc7ab995771006 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Wed, 5 Jun 2019 13:44:36 +0300 Subject: [PATCH] Added support for F# way of passing `this` to static methods --- .../XamlCompilerTaskExecutor.Helpers.cs | 32 +++++++++++++++++++ .../XamlCompilerTaskExecutor.cs | 10 +++--- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.Helpers.cs b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.Helpers.cs index 05193172be..f94f10f792 100644 --- a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.Helpers.cs +++ b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.Helpers.cs @@ -4,6 +4,7 @@ using System.Linq; using Avalonia.Utilities; using Mono.Cecil; using Mono.Cecil.Cil; +using Mono.Collections.Generic; using XamlIl.TypeSystem; namespace Avalonia.Build.Tasks @@ -144,6 +145,37 @@ namespace Avalonia.Build.Tasks }); } + + + private static bool MatchThisCall(Collection instructions, int idx) + { + var i = instructions[idx]; + // A "normal" way of passing `this` to a static method: + + // ldarg.0 + // call void [Avalonia.Markup.Xaml]Avalonia.Markup.Xaml.AvaloniaXamlLoader::Load(object) + + if (i.OpCode == OpCodes.Ldarg_0 || (i.OpCode == OpCodes.Ldarg && i.Operand?.Equals(0) == true)) + return true; + + /* F# way of using `this` in constructor emits a monstrosity like this: + IL_01c7: ldarg.0 + IL_01c8: ldfld class [FSharp.Core]Microsoft.FSharp.Core.FSharpRef`1 FVim.Cursor::this + IL_01cd: call instance !0 class [FSharp.Core]Microsoft.FSharp.Core.FSharpRef`1::get_contents() + IL_01d2: call !!0 [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/IntrinsicFunctions::CheckThis(!!0) + IL_01d7: call void [Avalonia.Markup.Xaml]Avalonia.Markup.Xaml.AvaloniaXamlLoader::Load(object) + + We check for the previous call to be Microsoft.FSharp.Core.LanguagePrimitives/IntrinsicFunctions::CheckThis + since it actually returns `this` + */ + if (i.OpCode == OpCodes.Call + && i.Operand is GenericInstanceMethod gim + && gim.Name == "CheckThis" + && gim.DeclaringType.FullName == "Microsoft.FSharp.Core.LanguagePrimitives/IntrinsicFunctions") + return true; + + return false; + } } } diff --git a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs index df840464c1..c54d8e19a1 100644 --- a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs +++ b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs @@ -234,8 +234,7 @@ namespace Avalonia.Build.Tasks var i = method.Body.Instructions; for (var c = 1; c < i.Count; c++) { - if (i[c - 1].OpCode == OpCodes.Ldarg_0 - && i[c].OpCode == OpCodes.Call) + if (i[c].OpCode == OpCodes.Call) { var op = i[c].Operand as MethodReference; @@ -254,8 +253,11 @@ namespace Avalonia.Build.Tasks && op.Parameters[0].ParameterType.FullName == "System.Object" && op.DeclaringType.FullName == "Avalonia.Markup.Xaml.AvaloniaXamlLoader") { - i[c].Operand = trampoline; - foundXamlLoader = true; + if (MatchThisCall(i, c - 1)) + { + i[c].Operand = trampoline; + foundXamlLoader = true; + } } } }