[X] resolve event handlers in base generic types (#6194)

* [XamlC] fix Debug harness generation code

* [X] resolve event handlers in base generic types

- fixes #6176
This commit is contained in:
Stephane Delcroix 2019-06-26 14:35:54 +02:00 коммит произвёл GitHub
Родитель 506d3b92e8
Коммит ea0d55dd0d
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
8 изменённых файлов: 80 добавлений и 25 удалений

Просмотреть файл

@ -96,10 +96,10 @@ namespace Xamarin.Forms.Build.Tasks
MethodDefinition ctorInfo = null;
if (node.Properties.ContainsKey(XmlName.xArguments) && !node.Properties.ContainsKey(XmlName.xFactoryMethod)) {
factoryCtorInfo = typedef.AllMethods().FirstOrDefault(md => md.IsConstructor &&
!md.IsStatic &&
md.HasParameters &&
md.MatchXArguments(node, typeref, Module, Context));
factoryCtorInfo = typedef.AllMethods().FirstOrDefault(md => md.methodDef.IsConstructor &&
!md.methodDef.IsStatic &&
md.methodDef.HasParameters &&
md.methodDef.MatchXArguments(node, typeref, Module, Context)).methodDef;
if (factoryCtorInfo == null) {
throw new XamlParseException(
string.Format("No constructors found for {0} with matching x:Arguments", typedef.FullName), node);
@ -109,10 +109,10 @@ namespace Xamarin.Forms.Build.Tasks
Context.IL.Append(PushCtorXArguments(factoryCtorInfo.ResolveGenericParameters(typeref, Module), node));
} else if (node.Properties.ContainsKey(XmlName.xFactoryMethod)) {
var factoryMethod = (string)(node.Properties [XmlName.xFactoryMethod] as ValueNode).Value;
factoryMethodInfo = typedef.AllMethods().FirstOrDefault(md => !md.IsConstructor &&
md.Name == factoryMethod &&
md.IsStatic &&
md.MatchXArguments(node, typeref, Module, Context));
factoryMethodInfo = typedef.AllMethods().FirstOrDefault(md => !md.methodDef.IsConstructor &&
md.methodDef.Name == factoryMethod &&
md.methodDef.IsStatic &&
md.methodDef.MatchXArguments(node, typeref, Module, Context)).methodDef;
if (factoryMethodInfo == null) {
throw new XamlParseException(
String.Format("No static method found for {0}::{1} ({2})", typedef.FullName, factoryMethod, null), node);

Просмотреть файл

@ -113,8 +113,9 @@ namespace Xamarin.Forms.Build.Tasks
var br2 = Instruction.Create(OpCodes.Ldarg_0);
var ret = Instruction.Create(OpCodes.Ret);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Callvirt,
module.ImportReference(typeDef.BaseType.Resolve().GetConstructors().First(c => c.HasParameters == false)));
var baseCtor = module.ImportReference(typeDef.BaseType.Resolve().GetConstructors().First(c => c.HasParameters == false));
baseCtor = module.ImportReference(baseCtor.ResolveGenericParameters(typeDef.BaseType, module));
il.Emit(OpCodes.Callvirt, baseCtor);
il.Emit(OpCodes.Nop);
il.Emit(OpCodes.Ldarg_1);

Просмотреть файл

@ -15,7 +15,7 @@ namespace Xamarin.Forms.Build.Tasks
var reference = new MethodReference(self.Name, ImportUnresolvedType(self.ReturnType, module))
{
DeclaringType = module.ImportReference(declaringTypeRef),
DeclaringType = declaringTypeRef,
HasThis = self.HasThis,
ExplicitThis = self.ExplicitThis,
CallingConvention = self.CallingConvention

Просмотреть файл

@ -169,7 +169,7 @@ namespace Xamarin.Forms.Build.Tasks
.Methods.FirstOrDefault(md => md.Name == "ConvertFromInvariantString" && md.Parameters.Count == 2)
: typeConverter.ResolveCached()
.AllMethods()
.FirstOrDefault(md => md.Name == "ConvertFromInvariantString" && md.Parameters.Count == 1);
.FirstOrDefault(md => md.methodDef.Name == "ConvertFromInvariantString" && md.methodDef.Parameters.Count == 1).methodDef;
var convertFromInvariantStringReference = module.ImportReference(convertFromInvariantStringDefinition);
yield return Instruction.Create(OpCodes.Newobj, typeConverterCtorRef);

Просмотреть файл

@ -941,31 +941,34 @@ namespace Xamarin.Forms.Build.Tasks
while (declaringType.IsNested)
declaringType = declaringType.DeclaringType;
var handler = declaringType.AllMethods().FirstOrDefault(md => {
if (md.Name != value as string)
if (md.methodDef.Name != value as string)
return false;
//check if the handler signature matches the Invoke signature;
var invoke = module.ImportReference(eventinfo.EventType.ResolveCached().GetMethods().First(eventmd => eventmd.Name == "Invoke"));
invoke = invoke.ResolveGenericParameters(eventinfo.EventType, module);
if (!md.ReturnType.InheritsFromOrImplements(invoke.ReturnType) || invoke.Parameters.Count != md.Parameters.Count)
if (!md.methodDef.ReturnType.InheritsFromOrImplements(invoke.ReturnType) || invoke.Parameters.Count != md.methodDef.Parameters.Count)
return false;
if (!invoke.ContainsGenericParameter)
for (var i = 0; i < invoke.Parameters.Count;i++)
if (!invoke.Parameters[i].ParameterType.InheritsFromOrImplements(md.Parameters[i].ParameterType))
if (!invoke.Parameters[i].ParameterType.InheritsFromOrImplements(md.methodDef.Parameters[i].ParameterType))
return false;
//TODO check generic parameters if any
return true;
});
if (handler == null)
MethodReference handlerRef = null;
if (handler.methodDef != null)
handlerRef = handler.methodDef.ResolveGenericParameters(handler.declTypeRef, module);
if (handler.methodDef == null)
throw new XamlParseException($"EventHandler \"{value}\" with correct signature not found in type \"{declaringType}\"", iXmlLineInfo);
//FIXME: eventually get the right ctor instead fo the First() one, just in case another one could exists (not even sure it's possible).
var ctor = module.ImportReference(eventinfo.EventType.ResolveCached().GetConstructors().First());
ctor = ctor.ResolveGenericParameters(eventinfo.EventType, module);
if (handler.IsStatic) {
if (handler.methodDef.IsStatic) {
yield return Create(Ldnull);
} else {
if (context.Root is VariableDefinition)
@ -978,11 +981,11 @@ namespace Xamarin.Forms.Build.Tasks
throw new InvalidProgramException();
}
if (handler.IsVirtual) {
if (handler.methodDef.IsVirtual) {
yield return Create(Ldarg_0);
yield return Create(Ldvirtftn, handler);
yield return Create(Ldvirtftn, handlerRef);
} else
yield return Create(Ldftn, handler);
yield return Create(Ldftn, handlerRef);
yield return Create(Newobj, module.ImportReference(ctor));
//Check if the handler has the same signature as the ctor (it should)

Просмотреть файл

@ -46,13 +46,14 @@ namespace Xamarin.Forms.Build.Tasks
return ctor;
}
public static IEnumerable<MethodDefinition> AllMethods(this TypeDefinition self)
public static IEnumerable<(MethodDefinition methodDef, TypeReference declTypeRef)> AllMethods(this TypeDefinition self)
{
while (self != null)
{
TypeReference selfTypeRef = self;
while (self != null) {
foreach (var md in self.Methods)
yield return md;
self = self.BaseType == null ? null : self.BaseType.ResolveCached();
yield return (md, selfTypeRef);
selfTypeRef = self.BaseType;
self = self.BaseType?.ResolveCached();
}
}
}

Просмотреть файл

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<local:Gh6176Base
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="using:Xamarin.Forms.Xaml.UnitTests"
x:Class="Xamarin.Forms.Xaml.UnitTests.Gh6176"
x:TypeArguments="local:Gh6176VM">
<ContentPage.ToolbarItems>
<ToolbarItem Clicked="ShowMenu" />
</ContentPage.ToolbarItems>
</local:Gh6176Base>

Просмотреть файл

@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using NUnit.Framework;
using Xamarin.Forms;
using Xamarin.Forms.Core.UnitTests;
namespace Xamarin.Forms.Xaml.UnitTests
{
public class Gh6176VM
{
}
public class Gh6176Base<TVM> : ContentPage where TVM: class
{
public TVM ViewModel => BindingContext as TVM;
protected void ShowMenu(object sender, EventArgs e) { }
}
public partial class Gh6176
{
public Gh6176() => InitializeComponent();
public Gh6176(bool useCompiledXaml)
{
//this stub will be replaced at compile time
}
[TestFixture] class Tests
{
[SetUp] public void Setup() => Device.PlatformServices = new MockPlatformServices();
[TearDown] public void TearDown() => Device.PlatformServices = null;
[Test]
public void XamlCDoesntFail([Values(false, true)]bool useCompiledXaml)
{
var layout = new Gh6176(useCompiledXaml);
}
}
}
}