A cross-platform UI framework for .NET
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

116 lines
4.6 KiB

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using XamlX.Emit;
using XamlX.IL;
using XamlX.TypeSystem;
using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers;
using System.Reflection.Emit;
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
{
internal class XamlIlTrampolineBuilder
{
private IXamlTypeBuilder<IXamlILEmitter> _builder;
private Dictionary<string, IXamlMethod> _trampolines = new();
public XamlIlTrampolineBuilder(IXamlTypeBuilder<IXamlILEmitter> builder)
{
_builder = builder;
}
public IXamlMethod EmitCommandExecuteTrampoline(XamlEmitContext<IXamlILEmitter, XamlILNodeEmitResult> context, IXamlMethod executeMethod)
{
Debug.Assert(!executeMethod.IsStatic);
string methodName = $"{executeMethod.DeclaringType.GetFqn()}+{executeMethod.Name}_{executeMethod.Parameters.Count}!CommandExecuteTrampoline";
if (_trampolines.TryGetValue(methodName, out var method))
{
return method;
}
var trampoline = _builder.DefineMethod(
context.Configuration.WellKnownTypes.Void,
new[] { context.Configuration.WellKnownTypes.Object, context.Configuration.WellKnownTypes.Object },
methodName,
XamlVisibility.Public,
true,
false);
var gen = trampoline.Generator;
if (executeMethod.DeclaringType.IsValueType)
{
gen.Ldarg_0()
.Unbox(executeMethod.DeclaringType);
}
else
{
gen.Ldarg_0()
.Castclass(executeMethod.DeclaringType);
}
if (executeMethod.Parameters.Count != 0)
{
Debug.Assert(executeMethod.Parameters.Count == 1);
if (executeMethod.Parameters[0] != context.Configuration.WellKnownTypes.Object)
{
var convertedValue = gen.DefineLocal(context.Configuration.WellKnownTypes.Object);
gen.Ldtype(executeMethod.Parameters[0])
.Ldarg(1)
.EmitCall(context.Configuration.WellKnownTypes.CultureInfo.FindMethod(m => m.Name == "get_CurrentCulture"))
.Ldloca(convertedValue)
.EmitCall(
context.GetAvaloniaTypes().TypeUtilities.FindMethod(m => m.Name == "TryConvert"),
swallowResult: true)
.Ldloc(convertedValue)
.Unbox_Any(executeMethod.Parameters[0]);
}
else
{
gen.Ldarg(1);
}
}
gen.EmitCall(executeMethod, swallowResult: true);
gen.Ret();
_trampolines.Add(methodName, trampoline);
return trampoline;
}
public IXamlMethod EmitCommandCanExecuteTrampoline(XamlEmitContext<IXamlILEmitter, XamlILNodeEmitResult> context, IXamlMethod canExecuteMethod)
{
Debug.Assert(!canExecuteMethod.IsStatic);
Debug.Assert(canExecuteMethod.Parameters.Count == 1);
Debug.Assert(canExecuteMethod.ReturnType == context.Configuration.WellKnownTypes.Boolean);
string methodName = $"{canExecuteMethod.DeclaringType.GetFqn()}+{canExecuteMethod.Name}!CommandCanExecuteTrampoline";
if (_trampolines.TryGetValue(methodName, out var method))
{
return method;
}
var trampoline = _builder.DefineMethod(
context.Configuration.WellKnownTypes.Boolean,
new[] { context.Configuration.WellKnownTypes.Object, context.Configuration.WellKnownTypes.Object },
methodName,
XamlVisibility.Public,
true,
false);
if (canExecuteMethod.DeclaringType.IsValueType)
{
trampoline.Generator
.Ldarg_0()
.Unbox(canExecuteMethod.DeclaringType);
}
else
{
trampoline.Generator
.Ldarg_0()
.Castclass(canExecuteMethod.DeclaringType);
}
trampoline.Generator
.Ldarg(1)
.Emit(OpCodes.Tailcall)
.EmitCall(canExecuteMethod)
.Ret();
_trampolines.Add(methodName, trampoline);
return trampoline;
}
}
}