diff options
12 files changed, 313 insertions, 159 deletions
diff --git a/Xamarin.Forms.Build.Tasks/CompiledMarkupExtensions/ICompiledMarkupExtension.cs b/Xamarin.Forms.Build.Tasks/CompiledMarkupExtensions/ICompiledMarkupExtension.cs new file mode 100644 index 00000000..b18d9561 --- /dev/null +++ b/Xamarin.Forms.Build.Tasks/CompiledMarkupExtensions/ICompiledMarkupExtension.cs @@ -0,0 +1,12 @@ +using Mono.Cecil; +using Mono.Cecil.Cil; +using System.Collections.Generic; +using Xamarin.Forms.Xaml; + +namespace Xamarin.Forms.Build.Tasks +{ + interface ICompiledMarkupExtension + { + IEnumerable<Instruction> ProvideValue(IElementNode node, ModuleDefinition module, out TypeReference typeRef); + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Build.Tasks/CompiledMarkupExtensions/StaticExtension.cs b/Xamarin.Forms.Build.Tasks/CompiledMarkupExtensions/StaticExtension.cs new file mode 100644 index 00000000..82654969 --- /dev/null +++ b/Xamarin.Forms.Build.Tasks/CompiledMarkupExtensions/StaticExtension.cs @@ -0,0 +1,88 @@ +using System; +using System.Collections.Generic; +using Mono.Cecil; +using Mono.Cecil.Cil; +using Xamarin.Forms.Xaml; +using System.Xml; + +using static System.String; + +namespace Xamarin.Forms.Build.Tasks +{ + class StaticExtension : ICompiledMarkupExtension + { + public IEnumerable<Instruction> ProvideValue(IElementNode node, ModuleDefinition module, out TypeReference memberRef) + { + INode ntype; + if (!node.Properties.TryGetValue(new XmlName("", "Member"), out ntype)) + ntype = node.CollectionItems [0]; + var member = ((ValueNode)ntype).Value as string; + + if (IsNullOrEmpty(member) || !member.Contains(".")) { + var lineInfo = node as IXmlLineInfo; + throw new XamlParseException("Syntax for x:Static is [Member=][prefix:]typeName.staticMemberName", lineInfo); + } + + var dotIdx = member.LastIndexOf('.'); + var typename = member.Substring(0, dotIdx); + var membername = member.Substring(dotIdx + 1); + + var typeRef = GetTypeReference(typename, module, node); + var fieldRef = GetFieldReference(typeRef, membername, module); + var propertyDef = GetPropertyDefinition(typeRef, membername, module); + + if (fieldRef == null && propertyDef == null) + throw new XamlParseException(Format("x:Static: unable to find a public static field or property named {0} in {1}", membername, typename), node as IXmlLineInfo); + + if (fieldRef != null) { + memberRef = fieldRef.FieldType; + return new [] { Instruction.Create(OpCodes.Ldsfld, fieldRef) }; + } + + memberRef = propertyDef.PropertyType; + var getterDef = propertyDef.GetMethod; + return new [] { Instruction.Create(OpCodes.Call, getterDef)}; + } + + + public static TypeReference GetTypeReference(string xmlType, ModuleDefinition module, IElementNode node) + { + var split = xmlType.Split(':'); + if (split.Length > 2) + throw new Xaml.XamlParseException(string.Format("Type \"{0}\" is invalid", xmlType), node as IXmlLineInfo); + + string prefix, name; + if (split.Length == 2) { + prefix = split [0]; + name = split [1]; + } else { + prefix = ""; + name = split [0]; + } + var namespaceuri = node.NamespaceResolver.LookupNamespace(prefix) ?? ""; + return XmlTypeExtensions.GetTypeReference(new XmlType(namespaceuri, name, null), module, node as IXmlLineInfo); + } + + public static FieldReference GetFieldReference(TypeReference typeRef, string fieldName, ModuleDefinition module) + { + TypeReference declaringTypeReference; + FieldReference fRef = typeRef.GetField(fd => fd.Name == fieldName && + fd.IsStatic && + fd.IsPublic, out declaringTypeReference); + if (fRef != null) { + fRef = module.Import(fRef.ResolveGenericParameters(declaringTypeReference)); + fRef.FieldType = module.Import(fRef.FieldType); + } + return fRef; + } + + public static PropertyDefinition GetPropertyDefinition(TypeReference typeRef, string propertyName, ModuleDefinition module) + { + TypeReference declaringTypeReference; + PropertyDefinition pDef = typeRef.GetProperty(pd => pd.Name == propertyName && + pd.GetMethod.IsPublic && + pd.GetMethod.IsStatic, out declaringTypeReference); + return pDef; + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Build.Tasks/CreateObjectVisitor.cs b/Xamarin.Forms.Build.Tasks/CreateObjectVisitor.cs index 5f9adafd..e6453575 100644 --- a/Xamarin.Forms.Build.Tasks/CreateObjectVisitor.cs +++ b/Xamarin.Forms.Build.Tasks/CreateObjectVisitor.cs @@ -5,6 +5,7 @@ using System.Linq; using Mono.Cecil; using Mono.Cecil.Cil; using Xamarin.Forms.Xaml; +using System.Xml; namespace Xamarin.Forms.Build.Tasks { @@ -67,165 +68,161 @@ namespace Xamarin.Forms.Build.Tasks var typeref = node.XmlType.GetTypeReference(Module, node); TypeDefinition typedef = typeref.Resolve(); - if (IsXaml2009LanguagePrimitive(node)) - { + if (IsXaml2009LanguagePrimitive(node)) { var vardef = new VariableDefinition(typeref); - Context.Variables[node] = vardef; + Context.Variables [node] = vardef; Context.Body.Variables.Add(vardef); Context.IL.Append(PushValueFromLanguagePrimitive(typedef, node)); Context.IL.Emit(OpCodes.Stloc, vardef); + return; } - else - { - MethodDefinition factoryCtorInfo = null; - MethodDefinition factoryMethodInfo = null; - MethodDefinition parameterizedCtorInfo = null; - 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, Module)); - if (factoryCtorInfo == null) - { - throw new XamlParseException( - string.Format("No constructors found for {0} with matching x:Arguments", typedef.FullName), node); - } - ctorInfo = factoryCtorInfo; - if (!typedef.IsValueType) //for ctor'ing typedefs, we first have to ldloca before the params - Context.IL.Append(PushCtorXArguments(factoryCtorInfo, 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, Module)); - if (factoryMethodInfo == null) - { - throw new XamlParseException( - String.Format("No static method found for {0}::{1} ({2})", typedef.FullName, factoryMethod, null), node); - } - Context.IL.Append(PushCtorXArguments(factoryMethodInfo, node)); - } - if (ctorInfo == null && factoryMethodInfo == null) - { - parameterizedCtorInfo = typedef.Methods.FirstOrDefault(md => md.IsConstructor && - !md.IsStatic && - md.HasParameters && - md.Parameters.All( - pd => - pd.CustomAttributes.Any( - ca => - ca.AttributeType.FullName == - "Xamarin.Forms.ParameterAttribute"))); - } - if (parameterizedCtorInfo != null && ValidateCtorArguments(parameterizedCtorInfo, node)) - { - ctorInfo = parameterizedCtorInfo; - // IL_0000: ldstr "foo" - Context.IL.Append(PushCtorArguments(parameterizedCtorInfo, node)); - } - ctorInfo = ctorInfo ?? typedef.Methods.FirstOrDefault(md => md.IsConstructor && !md.HasParameters && !md.IsStatic); + if (typeref.FullName == "Xamarin.Forms.Xaml.StaticExtension") { + var markupProvider = new StaticExtension(); - var ctorinforef = ctorInfo?.ResolveGenericParameters(typeref, Module); - var factorymethodinforef = factoryMethodInfo?.ResolveGenericParameters(typeref, Module); - var implicitOperatorref = typedef.Methods.FirstOrDefault(md => - md.IsPublic && - md.IsStatic && - md.IsSpecialName && - md.Name == "op_Implicit" && md.Parameters[0].ParameterType.FullName == "System.String"); + var il = markupProvider.ProvideValue(node, Module, out typeref); - if (ctorinforef != null || factorymethodinforef != null || typedef.IsValueType) - { - VariableDefinition vardef = new VariableDefinition(typeref); - Context.Variables[node] = vardef; - Context.Body.Variables.Add(vardef); + var vardef = new VariableDefinition(typeref); + Context.Variables [node] = vardef; + Context.Body.Variables.Add(vardef); - ValueNode vnode = null; - if (node.CollectionItems.Count == 1 && (vnode = node.CollectionItems.First() as ValueNode) != null && - vardef.VariableType.IsValueType) - { - //<Color>Purple</Color> - Context.IL.Append(vnode.PushConvertedValue(Context, typeref, new ICustomAttributeProvider[] { typedef }, - node.PushServiceProvider(Context), false, true)); - Context.IL.Emit(OpCodes.Stloc, vardef); - } - else if (node.CollectionItems.Count == 1 && (vnode = node.CollectionItems.First() as ValueNode) != null && - implicitOperatorref != null) - { - //<FileImageSource>path.png</FileImageSource> - var implicitOperator = Module.Import(implicitOperatorref); - Context.IL.Emit(OpCodes.Ldstr, ((ValueNode)(node.CollectionItems.First())).Value as string); - Context.IL.Emit(OpCodes.Call, implicitOperator); - Context.IL.Emit(OpCodes.Stloc, vardef); - } - else if (factorymethodinforef != null) - { - var factory = Module.Import(factorymethodinforef); - Context.IL.Emit(OpCodes.Call, factory); - Context.IL.Emit(OpCodes.Stloc, vardef); - } - else if (!typedef.IsValueType) - { - var ctor = Module.Import(ctorinforef); - // IL_0001: newobj instance void class [Xamarin.Forms.Core]Xamarin.Forms.Button::'.ctor'() - // IL_0006: stloc.0 - Context.IL.Emit(OpCodes.Newobj, ctor); - Context.IL.Emit(OpCodes.Stloc, vardef); - } - else if (ctorInfo != null && node.Properties.ContainsKey(XmlName.xArguments) && - !node.Properties.ContainsKey(XmlName.xFactoryMethod) && ctorInfo.MatchXArguments(node, Module)) - { - // IL_0008: ldloca.s 1 - // IL_000a: ldc.i4.1 - // IL_000b: call instance void valuetype Test/Foo::'.ctor'(bool) - - var ctor = Module.Import(ctorinforef); - Context.IL.Emit(OpCodes.Ldloca, vardef); - Context.IL.Append(PushCtorXArguments(factoryCtorInfo, node)); - Context.IL.Emit(OpCodes.Call, ctor); - } - else - { - // IL_0000: ldloca.s 0 - // IL_0002: initobj Test/Foo - Context.IL.Emit(OpCodes.Ldloca, vardef); - Context.IL.Emit(OpCodes.Initobj, Module.Import(typedef)); - } + Context.IL.Append(il); + Context.IL.Emit(OpCodes.Stloc, vardef); - if (typeref.FullName == "Xamarin.Forms.Xaml.TypeExtension") - { - var visitor = new SetPropertiesVisitor(Context); - foreach (var cnode in node.Properties.Values.ToList()) - cnode.Accept(visitor, node); - foreach (var cnode in node.CollectionItems) - cnode.Accept(visitor, node); - - //As we're stripping the TypeExtension bare, keep the type if we need it later (hint: we do need it) - INode ntype; - if (!node.Properties.TryGetValue(new XmlName("", "TypeName"), out ntype)) - ntype = node.CollectionItems[0]; - - var type = ((ValueNode)ntype).Value as string; - var namespaceuri = type.Contains(":") ? node.NamespaceResolver.LookupNamespace(type.Split(':')[0].Trim()) : ""; - type = type.Contains(":") ? type.Split(':')[1].Trim() : type; - Context.TypeExtensions[node] = new XmlType(namespaceuri, type, null).GetTypeReference(Module, node); - - node.Properties.Clear(); - node.CollectionItems.Clear(); - - var vardefref = new VariableDefinitionReference(vardef); - Context.IL.Append(SetPropertiesVisitor.ProvideValue(vardefref, Context, Module, node)); - if (vardef != vardefref.VariableDefinition) - { - Context.Variables[node] = vardefref.VariableDefinition; - Context.Body.Variables.Add(vardefref.VariableDefinition); - } + //clean the node as it has been fully exhausted + node.Properties.Clear(); + node.CollectionItems.Clear(); + return; + } + + MethodDefinition factoryCtorInfo = null; + MethodDefinition factoryMethodInfo = null; + MethodDefinition parameterizedCtorInfo = null; + 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, Module, Context)); + if (factoryCtorInfo == null) { + throw new XamlParseException( + string.Format("No constructors found for {0} with matching x:Arguments", typedef.FullName), node); + } + ctorInfo = factoryCtorInfo; + if (!typedef.IsValueType) //for ctor'ing typedefs, we first have to ldloca before the params + Context.IL.Append(PushCtorXArguments(factoryCtorInfo, 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, Module, Context)); + if (factoryMethodInfo == null) { + throw new XamlParseException( + String.Format("No static method found for {0}::{1} ({2})", typedef.FullName, factoryMethod, null), node); + } + Context.IL.Append(PushCtorXArguments(factoryMethodInfo, node)); + } + if (ctorInfo == null && factoryMethodInfo == null) { + parameterizedCtorInfo = typedef.Methods.FirstOrDefault(md => md.IsConstructor && + !md.IsStatic && + md.HasParameters && + md.Parameters.All( + pd => + pd.CustomAttributes.Any( + ca => + ca.AttributeType.FullName == + "Xamarin.Forms.ParameterAttribute"))); + } + if (parameterizedCtorInfo != null && ValidateCtorArguments(parameterizedCtorInfo, node)) { + ctorInfo = parameterizedCtorInfo; + // IL_0000: ldstr "foo" + Context.IL.Append(PushCtorArguments(parameterizedCtorInfo, node)); + } + ctorInfo = ctorInfo ?? typedef.Methods.FirstOrDefault(md => md.IsConstructor && !md.HasParameters && !md.IsStatic); + + var ctorinforef = ctorInfo?.ResolveGenericParameters(typeref, Module); + var factorymethodinforef = factoryMethodInfo?.ResolveGenericParameters(typeref, Module); + var implicitOperatorref = typedef.Methods.FirstOrDefault(md => + md.IsPublic && + md.IsStatic && + md.IsSpecialName && + md.Name == "op_Implicit" && md.Parameters [0].ParameterType.FullName == "System.String"); + + if (ctorinforef != null || factorymethodinforef != null || typedef.IsValueType) { + VariableDefinition vardef = new VariableDefinition(typeref); + Context.Variables [node] = vardef; + Context.Body.Variables.Add(vardef); + + ValueNode vnode = null; + if (node.CollectionItems.Count == 1 && (vnode = node.CollectionItems.First() as ValueNode) != null && + vardef.VariableType.IsValueType) { + //<Color>Purple</Color> + Context.IL.Append(vnode.PushConvertedValue(Context, typeref, new ICustomAttributeProvider [] { typedef }, + node.PushServiceProvider(Context), false, true)); + Context.IL.Emit(OpCodes.Stloc, vardef); + } else if (node.CollectionItems.Count == 1 && (vnode = node.CollectionItems.First() as ValueNode) != null && + implicitOperatorref != null) { + //<FileImageSource>path.png</FileImageSource> + var implicitOperator = Module.Import(implicitOperatorref); + Context.IL.Emit(OpCodes.Ldstr, ((ValueNode)(node.CollectionItems.First())).Value as string); + Context.IL.Emit(OpCodes.Call, implicitOperator); + Context.IL.Emit(OpCodes.Stloc, vardef); + } else if (factorymethodinforef != null) { + var factory = Module.Import(factorymethodinforef); + Context.IL.Emit(OpCodes.Call, factory); + Context.IL.Emit(OpCodes.Stloc, vardef); + } else if (!typedef.IsValueType) { + var ctor = Module.Import(ctorinforef); +// IL_0001: newobj instance void class [Xamarin.Forms.Core]Xamarin.Forms.Button::'.ctor'() +// IL_0006: stloc.0 + Context.IL.Emit(OpCodes.Newobj, ctor); + Context.IL.Emit(OpCodes.Stloc, vardef); + } else if (ctorInfo != null && node.Properties.ContainsKey(XmlName.xArguments) && + !node.Properties.ContainsKey(XmlName.xFactoryMethod) && ctorInfo.MatchXArguments(node, Module, Context)) { +// IL_0008: ldloca.s 1 +// IL_000a: ldc.i4.1 +// IL_000b: call instance void valuetype Test/Foo::'.ctor'(bool) + + var ctor = Module.Import(ctorinforef); + Context.IL.Emit(OpCodes.Ldloca, vardef); + Context.IL.Append(PushCtorXArguments(factoryCtorInfo, node)); + Context.IL.Emit(OpCodes.Call, ctor); + } else { +// IL_0000: ldloca.s 0 +// IL_0002: initobj Test/Foo + Context.IL.Emit(OpCodes.Ldloca, vardef); + Context.IL.Emit(OpCodes.Initobj, Module.Import(typedef)); + } + + //if/when we land the compiled converters, those 2 blocks could be greatly simplified + if (typeref.FullName == "Xamarin.Forms.Xaml.TypeExtension") { + var visitor = new SetPropertiesVisitor(Context); + foreach (var cnode in node.Properties.Values.ToList()) + cnode.Accept(visitor, node); + foreach (var cnode in node.CollectionItems) + cnode.Accept(visitor, node); + + //As we're stripping the TypeExtension bare, keep the type if we need it later (hint: we do need it) + INode ntype; + if (!node.Properties.TryGetValue(new XmlName("", "TypeName"), out ntype)) + ntype = node.CollectionItems [0]; + + var type = ((ValueNode)ntype).Value as string; + var namespaceuri = type.Contains(":") ? node.NamespaceResolver.LookupNamespace(type.Split(':') [0].Trim()) : ""; + type = type.Contains(":") ? type.Split(':') [1].Trim() : type; + Context.TypeExtensions [node] = new XmlType(namespaceuri, type, null).GetTypeReference(Module, node); + + node.Properties.Clear(); + node.CollectionItems.Clear(); + + var vardefref = new VariableDefinitionReference(vardef); + Context.IL.Append(SetPropertiesVisitor.ProvideValue(vardefref, Context, Module, node)); + if (vardef != vardefref.VariableDefinition) { + Context.Variables [node] = vardefref.VariableDefinition; + Context.Body.Variables.Add(vardefref.VariableDefinition); } } } diff --git a/Xamarin.Forms.Build.Tasks/ExpandMarkupsVisitor.cs b/Xamarin.Forms.Build.Tasks/ExpandMarkupsVisitor.cs index bbdaa6c8..feafe214 100644 --- a/Xamarin.Forms.Build.Tasks/ExpandMarkupsVisitor.cs +++ b/Xamarin.Forms.Build.Tasks/ExpandMarkupsVisitor.cs @@ -12,7 +12,6 @@ namespace Xamarin.Forms.Build.Tasks { XmlName.xKey, XmlName.xTypeArguments, - XmlName.xArguments, XmlName.xFactoryMethod, XmlName.xName }; @@ -175,8 +174,8 @@ namespace Xamarin.Forms.Build.Tasks throw new NotSupportedException(); node = xmlLineInfo == null - ? new ElementNode(type, null, nsResolver) - : new ElementNode(type, null, nsResolver, xmlLineInfo.LineNumber, xmlLineInfo.LinePosition); + ? new ElementNode(type, "", nsResolver) + : new ElementNode(type, "", nsResolver, xmlLineInfo.LineNumber, xmlLineInfo.LinePosition); if (remaining.StartsWith("}", StringComparison.Ordinal)) { diff --git a/Xamarin.Forms.Build.Tasks/MethodDefinitionExtensions.cs b/Xamarin.Forms.Build.Tasks/MethodDefinitionExtensions.cs index 41d7cb20..8c3dba22 100644 --- a/Xamarin.Forms.Build.Tasks/MethodDefinitionExtensions.cs +++ b/Xamarin.Forms.Build.Tasks/MethodDefinitionExtensions.cs @@ -6,7 +6,7 @@ namespace Xamarin.Forms.Build.Tasks { static class MethodDefinitionExtensions { - public static bool MatchXArguments(this MethodDefinition methodDefinition, ElementNode enode, ModuleDefinition module) + public static bool MatchXArguments(this MethodDefinition methodDefinition, ElementNode enode, ModuleDefinition module, ILContext context) { if (!enode.Properties.ContainsKey(XmlName.xArguments)) return !methodDefinition.HasParameters; @@ -28,7 +28,7 @@ namespace Xamarin.Forms.Build.Tasks for (var i = 0; i < methodDefinition.Parameters.Count; i++) { var paramType = methodDefinition.Parameters[i].ParameterType; - var argType = ((IElementNode)arguments[i]).XmlType.GetTypeReference(module, null); + var argType = context.Variables [arguments [i] as IElementNode].VariableType; if (!argType.InheritsFromOrImplements(paramType)) return false; } diff --git a/Xamarin.Forms.Build.Tasks/Xamarin.Forms.Build.Tasks.csproj b/Xamarin.Forms.Build.Tasks/Xamarin.Forms.Build.Tasks.csproj index 1bdd8bf1..0b26e277 100644 --- a/Xamarin.Forms.Build.Tasks/Xamarin.Forms.Build.Tasks.csproj +++ b/Xamarin.Forms.Build.Tasks/Xamarin.Forms.Build.Tasks.csproj @@ -88,6 +88,8 @@ <Compile Include="XamlCAssemblyResolver.cs" /> <Compile Include="FixedCreateCSharpManifestResourceName.cs" /> <Compile Include="MethodDefinitionExtensions.cs" /> + <Compile Include="CompiledMarkupExtensions\StaticExtension.cs" /> + <Compile Include="CompiledMarkupExtensions\ICompiledMarkupExtension.cs" /> </ItemGroup> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <Target Name="AfterBuild"> @@ -113,6 +115,9 @@ <Name>ICSharpCode.Decompiler</Name> </ProjectReference> </ItemGroup> + <ItemGroup> + <Folder Include="CompiledMarkupExtensions\" /> + </ItemGroup> <Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" /> <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> <PropertyGroup> diff --git a/Xamarin.Forms.Xaml.UnitTests/FactoryMethods.xaml b/Xamarin.Forms.Xaml.UnitTests/FactoryMethods.xaml index a90a0378..5225c7d9 100644 --- a/Xamarin.Forms.Xaml.UnitTests/FactoryMethods.xaml +++ b/Xamarin.Forms.Xaml.UnitTests/FactoryMethods.xaml @@ -54,5 +54,19 @@ </local:MockFactory> </local:MockView.Content> </local:MockView> + <local:MockView x:Name="v6"> + <local:MockView.Content> + <local:MockFactory> + <x:Arguments> + <StaticExtension Member="local:MockxStatic.MockStaticProperty"/> + </x:Arguments> + </local:MockFactory> + </local:MockView.Content> + </local:MockView> + <local:MockView x:Name="v7"> + <local:MockView.Content> + <local:MockFactory x:Arguments="{x:Static local:MockxStatic.MockStaticProperty}"/> + </local:MockView.Content> + </local:MockView> </StackLayout> </ContentPage>
\ No newline at end of file diff --git a/Xamarin.Forms.Xaml.UnitTests/FactoryMethods.xaml.cs b/Xamarin.Forms.Xaml.UnitTests/FactoryMethods.xaml.cs index 42f359b7..9fa4e924 100644 --- a/Xamarin.Forms.Xaml.UnitTests/FactoryMethods.xaml.cs +++ b/Xamarin.Forms.Xaml.UnitTests/FactoryMethods.xaml.cs @@ -1,5 +1,6 @@ using Xamarin.Forms; using NUnit.Framework; +using Xamarin.Forms.Core.UnitTests; namespace Xamarin.Forms.Xaml.UnitTests { @@ -16,11 +17,16 @@ namespace Xamarin.Forms.Xaml.UnitTests Content = "default ctor"; } - public MockFactory (string arg0, string arg1) + public MockFactory(string arg0, string arg1) { Content = "alternate ctor " + arg0 + arg1; } + public MockFactory(string arg0) + { + Content = "alternate ctor " + arg0; + } + public MockFactory (int arg) { Content = "int ctor " + arg.ToString (); @@ -63,6 +69,12 @@ namespace Xamarin.Forms.Xaml.UnitTests [TestFixture] public class Tests { + [SetUp] + public void SetUp() + { + Device.PlatformServices = new MockPlatformServices(); + } + [TestCase (false)] [TestCase (true)] public void TestDefaultCtor (bool useCompiledXaml) @@ -110,6 +122,22 @@ namespace Xamarin.Forms.Xaml.UnitTests var layout = new FactoryMethods (useCompiledXaml); Assert.AreEqual ("factory 42foo", layout.v5.Content.Content); } + + [TestCase(false)] + [TestCase(true)] + public void TestCtorWithxStatic(bool useCompiledXaml) + { + var layout = new FactoryMethods(useCompiledXaml); + Assert.AreEqual("alternate ctor Property", layout.v6.Content.Content); + } + + [TestCase(false)] + [TestCase(true)] + public void TestCtorWithxStaticAttribute(bool useCompiledXaml) + { + var layout = new FactoryMethods(useCompiledXaml); + Assert.AreEqual("alternate ctor Property", layout.v7.Content.Content); + } } } }
\ No newline at end of file diff --git a/Xamarin.Forms.Xaml.UnitTests/Validation/StaticExtensionException.xaml.cs b/Xamarin.Forms.Xaml.UnitTests/Validation/StaticExtensionException.xaml.cs index d7476b74..cb8b22b4 100644 --- a/Xamarin.Forms.Xaml.UnitTests/Validation/StaticExtensionException.xaml.cs +++ b/Xamarin.Forms.Xaml.UnitTests/Validation/StaticExtensionException.xaml.cs @@ -7,6 +7,7 @@ using NUnit.Framework; namespace Xamarin.Forms.Xaml.UnitTests { + [XamlCompilation(XamlCompilationOptions.Skip)] public partial class StaticExtensionException : ContentPage { public StaticExtensionException () @@ -23,7 +24,6 @@ namespace Xamarin.Forms.Xaml.UnitTests public class Issue2115 { [TestCase (false)] - [TestCase (true)] public void xStaticThrowsMeaningfullException (bool useCompiledXaml) { Assert.Throws (new XamlParseExceptionConstraint (6, 34), () => new StaticExtensionException (useCompiledXaml)); diff --git a/Xamarin.Forms.Xaml/CreateValuesVisitor.cs b/Xamarin.Forms.Xaml/CreateValuesVisitor.cs index e52ae594..014d82c2 100644 --- a/Xamarin.Forms.Xaml/CreateValuesVisitor.cs +++ b/Xamarin.Forms.Xaml/CreateValuesVisitor.cs @@ -124,8 +124,8 @@ namespace Xamarin.Forms.Xaml Values[node] = value; - var typeExtension = value as TypeExtension; - if (typeExtension != null) + var markup = value as IMarkupExtension; + if (markup != null && (value is TypeExtension || value is StaticExtension)) { var serviceProvider = new XamlServiceProvider(node, Context); @@ -135,7 +135,7 @@ namespace Xamarin.Forms.Xaml foreach (var cnode in node.CollectionItems) cnode.Accept(visitor, node); - value = typeExtension.ProvideValue(serviceProvider); + value = markup.ProvideValue(serviceProvider); node.Properties.Clear(); node.CollectionItems.Clear(); diff --git a/Xamarin.Forms.Xaml/ExpandMarkupsVisitor.cs b/Xamarin.Forms.Xaml/ExpandMarkupsVisitor.cs index 0d5e9ee6..36e8fc17 100644 --- a/Xamarin.Forms.Xaml/ExpandMarkupsVisitor.cs +++ b/Xamarin.Forms.Xaml/ExpandMarkupsVisitor.cs @@ -12,6 +12,14 @@ namespace Xamarin.Forms.Xaml Context = context; } + public static readonly IList<XmlName> Skips = new List<XmlName> + { + XmlName.xKey, + XmlName.xTypeArguments, + XmlName.xFactoryMethod, + XmlName.xName + }; + Dictionary<INode, object> Values { get { return Context.Values; } @@ -44,7 +52,7 @@ namespace Xamarin.Forms.Xaml XmlName propertyName; if (!ApplyPropertiesVisitor.TryGetPropertyName(markupnode, parentNode, out propertyName)) return; - if (ApplyPropertiesVisitor.Skips.Contains(propertyName)) + if (Skips.Contains(propertyName)) return; if (parentElement.SkipProperties.Contains(propertyName)) return; diff --git a/Xamarin.Forms.Xaml/XamlParser.cs b/Xamarin.Forms.Xaml/XamlParser.cs index 730c1624..7dd79d77 100644 --- a/Xamarin.Forms.Xaml/XamlParser.cs +++ b/Xamarin.Forms.Xaml/XamlParser.cs @@ -223,6 +223,9 @@ namespace Xamarin.Forms.Xaml case "x:FactoryMethod": propertyName = XmlName.xFactoryMethod; break; + case "x:Arguments": + propertyName = XmlName.xArguments; + break; default: Debug.WriteLine("Unhandled {0}", reader.Name); continue; |