diff options
-rw-r--r-- | Xamarin.Forms.Build.Tasks/SetPropertiesVisitor.cs | 4 | ||||
-rw-r--r-- | Xamarin.Forms.Build.Tasks/XamlCTask.cs | 177 | ||||
-rw-r--r-- | Xamarin.Forms.Xaml.UnitTests/Issues/Bz43450.xaml | 15 | ||||
-rw-r--r-- | Xamarin.Forms.Xaml.UnitTests/Issues/Bz43450.xaml.cs | 32 | ||||
-rw-r--r-- | Xamarin.Forms.Xaml.UnitTests/MockCompiler.cs | 30 | ||||
-rw-r--r-- | Xamarin.Forms.Xaml.UnitTests/Xamarin.Forms.Xaml.UnitTests.csproj | 11 |
6 files changed, 184 insertions, 85 deletions
diff --git a/Xamarin.Forms.Build.Tasks/SetPropertiesVisitor.cs b/Xamarin.Forms.Build.Tasks/SetPropertiesVisitor.cs index 462a8962..9bc09ce9 100644 --- a/Xamarin.Forms.Build.Tasks/SetPropertiesVisitor.cs +++ b/Xamarin.Forms.Build.Tasks/SetPropertiesVisitor.cs @@ -169,7 +169,9 @@ namespace Xamarin.Forms.Build.Tasks Context.IL.Emit(OpCodes.Callvirt, adderRef); if (adderRef.ReturnType.FullName != "System.Void") Context.IL.Emit(OpCodes.Pop); - } + } else + throw new XamlParseException(string.Format("Property {0} not found", localname), node); + } } diff --git a/Xamarin.Forms.Build.Tasks/XamlCTask.cs b/Xamarin.Forms.Build.Tasks/XamlCTask.cs index b119b4e9..206fc452 100644 --- a/Xamarin.Forms.Build.Tasks/XamlCTask.cs +++ b/Xamarin.Forms.Build.Tasks/XamlCTask.cs @@ -40,12 +40,26 @@ namespace Xamarin.Forms.Build.Tasks protected bool InMsBuild { get; set; } + internal string Type { get; set; } + public override bool Execute() { InMsBuild = true; return Compile(); } + protected void LogException(string subcategory, string errorCode, string helpKeyword, string file, Exception e) + { + var xpe = e as XamlParseException; + var xe = e as XmlException; + if (xpe != null) + LogError(subcategory, errorCode, helpKeyword, file, xpe.XmlInfo.LineNumber, xpe.XmlInfo.LinePosition, 0, 0, xpe.Message, xpe.HelpLink, xpe.Source); + else if (xe != null) + LogError(subcategory, errorCode, helpKeyword, file, xe.LineNumber, xe.LinePosition, 0, 0, xe.Message, xe.HelpLink, xe.Source); + else + LogError(subcategory, errorCode, helpKeyword, file, 0, 0, 0, 0, e.Message, e.HelpLink, e.Source); + } + protected void LogError(string subcategory, string errorCode, string helpKeyword, string file, int lineNumber, int columnNumber, int endLineNumber, int endColumnNumber, string message, params object[] messageArgs) { @@ -109,12 +123,12 @@ namespace Xamarin.Forms.Build.Tasks InMsBuild = false, DependencyPaths = dependencyPaths, ReferencePath = referencePath, - OutputGeneratedILAsCode = outputCSharp + OutputGeneratedILAsCode = outputCSharp, }; xamlc.Compile(); } - public bool Compile() + public bool Compile(IList<Exception> thrownExceptions = null) { LogLine(1, "Compiling Xaml"); LogLine(1, "\nAssembly: {0}", Assembly); @@ -150,8 +164,6 @@ namespace Xamarin.Forms.Build.Tasks var searchpath = Path.GetDirectoryName(p); LogLine(3, "Adding searchpath {0}", searchpath); resolver.AddSearchDirectory(searchpath); - // LogLine (3, "Referencing {0}", p); - // resolver.AddAssembly (p); } } @@ -218,6 +230,10 @@ namespace Xamarin.Forms.Build.Tasks if ((options & XamlCompilationOptions.Compile) == XamlCompilationOptions.Compile) skiptype = false; } + + if (Type != null) + skiptype = !(Type == classname); + if (skiptype) { LogLine(2, "Has XamlCompilationAttribute set to Skip and not Compile... skipped"); @@ -252,87 +268,14 @@ namespace Xamarin.Forms.Build.Tasks hasCompiledXamlResources = true; - try - { - LogString(2, " Replacing {0}.InitializeComponent ()... ", typeDef.Name); - var body = new MethodBody(initComp); - var il = body.GetILProcessor(); - il.Emit(OpCodes.Nop); - - // Generating branching code for the Previewer - // IL_0007: call class [mscorlib]System.Func`2<class [mscorlib]System.Type,string> class [Xamarin.Forms.Xaml.Internals]Xamarin.Forms.Xaml.XamlLoader::get_XamlFileProvider() - // IL_000c: brfalse IL_0031 - // IL_0011: call class [mscorlib]System.Func`2<class [mscorlib]System.Type,string> class [Xamarin.Forms.Xaml.Internals]Xamarin.Forms.Xaml.XamlLoader::get_XamlFileProvider() - // IL_0016: ldarg.0 - // IL_0017: call instance class [mscorlib]System.Type object::GetType() - // IL_001c: callvirt instance !1 class [mscorlib]System.Func`2<class [mscorlib]System.Type, string>::Invoke(!0) - // IL_0021: brfalse IL_0031 - // IL_0026: ldarg.0 - // IL_0027: call instance void class Xamarin.Forms.Xaml.UnitTests.XamlLoaderGetXamlForTypeTests::__InitComponentRuntime() - // IL_002c: ret - // IL_0031: nop - - var nop = Instruction.Create(OpCodes.Nop); - var getXamlFileProvider = body.Method.Module.Import(body.Method.Module.Import(typeof(Xamarin.Forms.Xaml.Internals.XamlLoader)) - .Resolve() - .Properties.FirstOrDefault(pd => pd.Name == "XamlFileProvider") - .GetMethod); - il.Emit(OpCodes.Call, getXamlFileProvider); - il.Emit(OpCodes.Brfalse, nop); - il.Emit(OpCodes.Call, getXamlFileProvider); - il.Emit(OpCodes.Ldarg_0); - var getType = body.Method.Module.Import(body.Method.Module.Import(typeof(object)) - .Resolve() - .Methods.FirstOrDefault(md => md.Name == "GetType")); - il.Emit(OpCodes.Call, getType); - var func = body.Method.Module.Import(body.Method.Module.Import(typeof(Func<Type, string>)) - .Resolve() - .Methods.FirstOrDefault(md => md.Name == "Invoke")); - func = func.ResolveGenericParameters(body.Method.Module.Import(typeof(Func<Type, string>)), body.Method.Module); - il.Emit(OpCodes.Callvirt, func); - il.Emit(OpCodes.Brfalse, nop); - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Call, initCompRuntime); - il.Emit(OpCodes.Ret); - il.Append(nop); - - var visitorContext = new ILContext(il, body); - - rootnode.Accept(new XamlNodeVisitor((node, parent) => node.Parent = parent), null); - rootnode.Accept(new ExpandMarkupsVisitor(visitorContext), null); - rootnode.Accept(new PruneIgnoredNodesVisitor(), null); - rootnode.Accept(new CreateObjectVisitor(visitorContext), null); - rootnode.Accept(new SetNamescopesAndRegisterNamesVisitor(visitorContext), null); - rootnode.Accept(new SetFieldVisitor(visitorContext), null); - rootnode.Accept(new SetResourcesVisitor(visitorContext), null); - rootnode.Accept(new SetPropertiesVisitor(visitorContext, true), null); - - il.Emit(OpCodes.Ret); - initComp.Body = body; - } - catch (XamlParseException xpe) - { - LogLine(2, "failed."); - LogError(null, null, null, resource.Name, xpe.XmlInfo.LineNumber, xpe.XmlInfo.LinePosition, 0, 0, xpe.Message, - xpe.HelpLink, xpe.Source); - LogLine(4, xpe.StackTrace); - success = false; - continue; - } - catch (XmlException xe) - { - LogLine(2, "failed."); - LogError(null, null, null, resource.Name, xe.LineNumber, xe.LinePosition, 0, 0, xe.Message, xe.HelpLink, xe.Source); - LogLine(4, xe.StackTrace); + LogString(2, " Replacing {0}.InitializeComponent ()... ", typeDef.Name); + Exception e; + if (!TryCoreCompile(initComp, initCompRuntime, rootnode, out e)) { success = false; - continue; - } - catch (Exception e) - { LogLine(2, "failed."); - LogError(null, null, null, resource.Name, 0, 0, 0, 0, e.Message, e.HelpLink, e.Source); + thrownExceptions?.Add(e); + LogException(null, null, null, resource.Name, e); LogLine(4, e.StackTrace); - success = false; continue; } LogLine(2, "done."); @@ -395,7 +338,8 @@ namespace Xamarin.Forms.Build.Tasks catch (Exception e) { LogLine(1, "failed."); - LogError(null, null, null, null, 0, 0, 0, 0, e.Message, e.HelpLink, e.Source); + LogException(null, null, null, null, e); + thrownExceptions?.Add(e); LogLine(4, e.StackTrace); success = false; } @@ -403,6 +347,73 @@ namespace Xamarin.Forms.Build.Tasks return success; } + bool TryCoreCompile(MethodDefinition initComp, MethodDefinition initCompRuntime, ILRootNode rootnode, out Exception exception) + { + try { + var body = new MethodBody(initComp); + var il = body.GetILProcessor(); + il.Emit(OpCodes.Nop); + + if (initCompRuntime != null) { + // Generating branching code for the Previewer + // IL_0007: call class [mscorlib]System.Func`2<class [mscorlib]System.Type,string> class [Xamarin.Forms.Xaml.Internals]Xamarin.Forms.Xaml.XamlLoader::get_XamlFileProvider() + // IL_000c: brfalse IL_0031 + // IL_0011: call class [mscorlib]System.Func`2<class [mscorlib]System.Type,string> class [Xamarin.Forms.Xaml.Internals]Xamarin.Forms.Xaml.XamlLoader::get_XamlFileProvider() + // IL_0016: ldarg.0 + // IL_0017: call instance class [mscorlib]System.Type object::GetType() + // IL_001c: callvirt instance !1 class [mscorlib]System.Func`2<class [mscorlib]System.Type, string>::Invoke(!0) + // IL_0021: brfalse IL_0031 + // IL_0026: ldarg.0 + // IL_0027: call instance void class Xamarin.Forms.Xaml.UnitTests.XamlLoaderGetXamlForTypeTests::__InitComponentRuntime() + // IL_002c: ret + // IL_0031: nop + + var nop = Instruction.Create(OpCodes.Nop); + var getXamlFileProvider = body.Method.Module.Import(body.Method.Module.Import(typeof(Xamarin.Forms.Xaml.Internals.XamlLoader)) + .Resolve() + .Properties.FirstOrDefault(pd => pd.Name == "XamlFileProvider") + .GetMethod); + il.Emit(OpCodes.Call, getXamlFileProvider); + il.Emit(OpCodes.Brfalse, nop); + il.Emit(OpCodes.Call, getXamlFileProvider); + il.Emit(OpCodes.Ldarg_0); + var getType = body.Method.Module.Import(body.Method.Module.Import(typeof(object)) + .Resolve() + .Methods.FirstOrDefault(md => md.Name == "GetType")); + il.Emit(OpCodes.Call, getType); + var func = body.Method.Module.Import(body.Method.Module.Import(typeof(Func<Type, string>)) + .Resolve() + .Methods.FirstOrDefault(md => md.Name == "Invoke")); + func = func.ResolveGenericParameters(body.Method.Module.Import(typeof(Func<Type, string>)), body.Method.Module); + il.Emit(OpCodes.Callvirt, func); + il.Emit(OpCodes.Brfalse, nop); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Call, initCompRuntime); + il.Emit(OpCodes.Ret); + il.Append(nop); + } + + var visitorContext = new ILContext(il, body); + + rootnode.Accept(new XamlNodeVisitor((node, parent) => node.Parent = parent), null); + rootnode.Accept(new ExpandMarkupsVisitor(visitorContext), null); + rootnode.Accept(new PruneIgnoredNodesVisitor(), null); + rootnode.Accept(new CreateObjectVisitor(visitorContext), null); + rootnode.Accept(new SetNamescopesAndRegisterNamesVisitor(visitorContext), null); + rootnode.Accept(new SetFieldVisitor(visitorContext), null); + rootnode.Accept(new SetResourcesVisitor(visitorContext), null); + rootnode.Accept(new SetPropertiesVisitor(visitorContext, true), null); + + il.Emit(OpCodes.Ret); + initComp.Body = body; + exception = null; + return true; + } catch (Exception e) { + exception = e; + return false; + } + } + protected static MethodDefinition DuplicateMethodDef(TypeDefinition typeDef, MethodDefinition methodDef, string newName) { var dup = new MethodDefinition(newName, methodDef.Attributes, methodDef.ReturnType); diff --git a/Xamarin.Forms.Xaml.UnitTests/Issues/Bz43450.xaml b/Xamarin.Forms.Xaml.UnitTests/Issues/Bz43450.xaml new file mode 100644 index 00000000..cc234879 --- /dev/null +++ b/Xamarin.Forms.Xaml.UnitTests/Issues/Bz43450.xaml @@ -0,0 +1,15 @@ +<?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.Bz43450"> + <Grid> + <Grid.RowDefinition> + <RowDefinition Height="20"/> + <RowDefinition Height="*"/> + <RowDefinition Height="20"/> + </Grid.RowDefinition> + <BoxView BackgroundColor="Red"/> + <BoxView Grid.Row="1" BackgroundColor="Yellow"/> + <BoxView Grid.Row="2" BackgroundColor="Green"/> + </Grid> +</ContentPage>
\ No newline at end of file diff --git a/Xamarin.Forms.Xaml.UnitTests/Issues/Bz43450.xaml.cs b/Xamarin.Forms.Xaml.UnitTests/Issues/Bz43450.xaml.cs new file mode 100644 index 00000000..b3346b37 --- /dev/null +++ b/Xamarin.Forms.Xaml.UnitTests/Issues/Bz43450.xaml.cs @@ -0,0 +1,32 @@ +using NUnit.Framework; + +namespace Xamarin.Forms.Xaml.UnitTests +{ + [XamlCompilation(XamlCompilationOptions.Skip)] + public partial class Bz43450 : ContentPage + { + public Bz43450() + { + InitializeComponent(); + } + + public Bz43450(bool useCompiledXaml) + { + //this stub will be replaced at compile time + } + + [TestFixture] + class Tests + { + [TestCase(true)] + [TestCase(false)] + public void DoesNotAllowGridRowDefinition(bool useCompiledXaml) + { + if (!useCompiledXaml) + Assert.Throws<XamlParseException>(() => new Bz43450(useCompiledXaml)); + else + Assert.Throws<XamlParseException>(() => MockCompiler.Compile(typeof(Bz43450))); + } + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Xaml.UnitTests/MockCompiler.cs b/Xamarin.Forms.Xaml.UnitTests/MockCompiler.cs new file mode 100644 index 00000000..404169b3 --- /dev/null +++ b/Xamarin.Forms.Xaml.UnitTests/MockCompiler.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using Xamarin.Forms.Build.Tasks; + +namespace Xamarin.Forms.Xaml.UnitTests +{ + public static class MockCompiler + { + public static void Compile(Type type) + { + var assembly = type.Assembly.Location; + var refs = from an in type.Assembly.GetReferencedAssemblies() + let a = System.Reflection.Assembly.Load(an) + select a.Location; + + var xamlc = new XamlCTask { + Assembly = assembly, + ReferencePath = string.Join(";", refs), + KeepXamlResources = true, + Type = type.FullName + }; + + var exceptions = new List<Exception>(); + if (!xamlc.Compile(exceptions) && exceptions.Any()) + throw exceptions [0]; + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Xaml.UnitTests/Xamarin.Forms.Xaml.UnitTests.csproj b/Xamarin.Forms.Xaml.UnitTests/Xamarin.Forms.Xaml.UnitTests.csproj index 26bf6d41..16f54e88 100644 --- a/Xamarin.Forms.Xaml.UnitTests/Xamarin.Forms.Xaml.UnitTests.csproj +++ b/Xamarin.Forms.Xaml.UnitTests/Xamarin.Forms.Xaml.UnitTests.csproj @@ -81,6 +81,9 @@ <Link>MockPlatformServices.cs</Link> </Compile> <Compile Include="FontConverterTests.cs" /> + <Compile Include="Issues\Bz43450.xaml.cs"> + <DependentUpon>Bz43450.xaml</DependentUpon> + </Compile> <Compile Include="Issues\Bz41296.xaml.cs"> <DependentUpon>Bz41296.xaml</DependentUpon> </Compile> @@ -362,6 +365,7 @@ <Compile Include="NativeViewsAndBindings.xaml.cs"> <DependentUpon>NativeViewsAndBindings.xaml</DependentUpon> </Compile> + <Compile Include="MockCompiler.cs" /> </ItemGroup> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <Import Project="..\.nuspec\Xamarin.Forms.Debug.targets" /> @@ -669,4 +673,9 @@ <Generator>MSBuild:UpdateDesignTimeXaml</Generator> </EmbeddedResource> </ItemGroup> -</Project>
\ No newline at end of file + <ItemGroup> + <EmbeddedResource Include="Issues\Bz43450.xaml"> + <Generator>MSBuild:UpdateDesignTimeXaml</Generator> + </EmbeddedResource> + </ItemGroup> +</Project> |