[XamlC] check eventhandler signature (#3086)

- fixes #3082
This commit is contained in:
Stephane Delcroix 2018-06-21 10:25:25 +02:00 коммит произвёл GitHub
Родитель 5785fcb29d
Коммит 130dfdf843
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
4 изменённых файлов: 93 добавлений и 9 удалений

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

@ -856,12 +856,12 @@ namespace Xamarin.Forms.Build.Tasks
var value = ((ValueNode)valueNode).Value;
yield return Instruction.Create(OpCodes.Ldloc, parent);
yield return Create(Ldloc, parent);
if (context.Root is VariableDefinition)
yield return Instruction.Create(OpCodes.Ldloc, context.Root as VariableDefinition);
yield return Create(Ldloc, context.Root as VariableDefinition);
else if (context.Root is FieldDefinition) {
yield return Instruction.Create(OpCodes.Ldarg_0);
yield return Instruction.Create(OpCodes.Ldfld, context.Root as FieldDefinition);
yield return Create(Ldarg_0);
yield return Create(Ldfld, context.Root as FieldDefinition);
} else
throw new InvalidProgramException();
var declaringType = context.Body.Method.DeclaringType;
@ -870,19 +870,33 @@ namespace Xamarin.Forms.Build.Tasks
var handler = declaringType.AllMethods().FirstOrDefault(md => md.Name == value as string);
if (handler == null)
throw new XamlParseException($"EventHandler \"{value}\" not found in type \"{context.Body.Method.DeclaringType.FullName}\"", iXmlLineInfo);
//check if the handler signature matches the Invoke signature;
var invoke = module.ImportReference(eventinfo.EventType.ResolveCached().GetMethods().First(md => md.Name == "Invoke"));
invoke = invoke.ResolveGenericParameters(eventinfo.EventType, module);
if (!handler.ReturnType.InheritsFromOrImplements(invoke.ReturnType))
throw new XamlParseException($"Signature (return type) of EventHandler \"{context.Body.Method.DeclaringType.FullName}.{value}\" doesn't match the event type", iXmlLineInfo);
if (invoke.Parameters.Count != handler.Parameters.Count)
throw new XamlParseException($"Signature (number of arguments) of EventHandler \"{context.Body.Method.DeclaringType.FullName}.{value}\" doesn't match the event type", iXmlLineInfo);
if (!invoke.ContainsGenericParameter)
for (var i = 0; i < invoke.Parameters.Count;i++)
if (!handler.Parameters[i].ParameterType.InheritsFromOrImplements(invoke.Parameters[i].ParameterType))
throw new XamlParseException($"Signature (parameter {i}) of EventHandler \"{context.Body.Method.DeclaringType.FullName}.{value}\" doesn't match the event type", iXmlLineInfo);
if (handler.IsVirtual) {
yield return Instruction.Create(OpCodes.Ldarg_0);
yield return Instruction.Create(OpCodes.Ldvirtftn, handler);
yield return Create(Ldarg_0);
yield return Create(Ldvirtftn, handler);
} else
yield return Instruction.Create(OpCodes.Ldftn, handler);
yield return Create(Ldftn, handler);
//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);
yield return Instruction.Create(OpCodes.Newobj, module.ImportReference(ctor));
yield return Create(Newobj, module.ImportReference(ctor));
//Check if the handler has the same signature as the ctor (it should)
var adder = module.ImportReference(eventinfo.AddMethod);
adder = adder.ResolveGenericParameters(eventDeclaringTypeRef, module);
yield return Instruction.Create(OpCodes.Callvirt, module.ImportReference(adder));
yield return Create(Callvirt, module.ImportReference(adder));
}
static bool CanSetDynamicResource(FieldReference bpRef, INode valueNode, ILContext context)

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

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Xamarin.Forms.Xaml.UnitTests.Gh3082">
<Button Clicked="OnClicked" />
</ContentPage>

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

@ -0,0 +1,56 @@
using System;
using System.Threading.Tasks;
using System.Collections.Generic;
using NUnit.Framework;
using Xamarin.Forms.Core.UnitTests;
using Xamarin.Forms;
namespace Xamarin.Forms.Xaml.UnitTests
{
[XamlCompilation(XamlCompilationOptions.Skip)]
public partial class Gh3082 : ContentPage
{
public Gh3082()
{
InitializeComponent();
}
public Gh3082(bool useCompiledXaml)
{
//this stub will be replaced at compile time
}
async Task OnClicked(object sender, EventArgs e)
{
await Task.Delay(1000);
}
[TestFixture]
class Tests
{
[SetUp]
public void Setup()
{
Device.PlatformServices = new MockPlatformServices();
}
[TearDown]
public void TearDown()
{
Device.PlatformServices = null;
}
[TestCase(false), TestCase(true)]
public void ThrowsOnWrongEventHandlerSignature(bool useCompiledXaml)
{
if (useCompiledXaml)
Assert.Throws<XamlParseException>(()=>MockCompiler.Compile(typeof(Gh3082)));
else
Assert.Throws<XamlParseException>(()=>new Gh3082(false));
}
}
}
}

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

@ -625,6 +625,9 @@
<DependentUpon>Gh2574.xaml</DependentUpon>
</Compile>
<Compile Include="XFCorePostProcessorCodeInjected.cs" />
<Compile Include="Issues\Gh3082.xaml.cs">
<DependentUpon>Gh3082.xaml</DependentUpon>
</Compile>
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<Import Project="..\.nuspec\Xamarin.Forms.Debug.targets" Condition="'$(BuildingInsideVisualStudio)' == 'true' AND Exists('..\.nuspec\Xamarin.Forms.Build.Tasks.dll')" />
@ -1131,6 +1134,10 @@
<SubType>Designer</SubType>
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
<EmbeddedResource Include="Issues\Gh3082.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />