[X] Reuse baseclass namescope if it exists (#7356)
authorStephane Delcroix <stephane@delcroix.org>
Tue, 3 Sep 2019 20:45:22 +0000 (22:45 +0200)
committerGitHub <noreply@github.com>
Tue, 3 Sep 2019 20:45:22 +0000 (22:45 +0200)
* [X] chain debug ctors

* unit test for 7097

* [X] Reuse baseclass namescope if it exists

If a xaml control derives from another xaml control, thy both should share the same
NameScope. This add a check if the xaml root already has a NameScope (set by the base)
and reuses it.

This fixes 2 regressions introduced by #7040 and some other unreported edge cases.

- fixes #7097
- fixes #7286

* avoid repeting linq queries

13 files changed:
Xamarin.Forms.Build.Tasks/DebugXamlCTask.cs
Xamarin.Forms.Build.Tasks/SetNamescopesAndRegisterNamesVisitor.cs
Xamarin.Forms.Core/Internals/NameScope.cs
Xamarin.Forms.Xaml.UnitTests/Issues/Gh7097.xaml [new file with mode: 0644]
Xamarin.Forms.Xaml.UnitTests/Issues/Gh7097.xaml.cs [new file with mode: 0644]
Xamarin.Forms.Xaml.UnitTests/Issues/Gh7097Base.xaml [new file with mode: 0644]
Xamarin.Forms.Xaml.UnitTests/Issues/Gh7097Base.xaml.cs [new file with mode: 0644]
Xamarin.Forms.Xaml.UnitTests/MarkupExpressionParserTests.cs
Xamarin.Forms.Xaml/CreateValuesVisitor.cs
Xamarin.Forms.Xaml/NamescopingVisitor.cs
Xamarin.Forms.Xaml/RegisterXNamesVisitor.cs
Xamarin.Forms.Xaml/XamlNode.cs
Xamarin.Forms.Xaml/XamlServiceProvider.cs

index af934c5..25be35b 100644 (file)
@@ -113,8 +113,16 @@ namespace Xamarin.Forms.Build.Tasks
                                                var br2 = Instruction.Create(OpCodes.Ldarg_0);
                                                var ret = Instruction.Create(OpCodes.Ret);
                                                il.Emit(OpCodes.Ldarg_0);
-                                               var baseCtor = module.ImportReference(typeDef.BaseType.Resolve().GetConstructors().First(c => c.HasParameters == false));
-                                               baseCtor = module.ImportReference(baseCtor.ResolveGenericParameters(typeDef.BaseType, module));
+                                               MethodReference baseCtor;
+                                               if (typeDef.BaseType.Resolve().GetConstructors().FirstOrDefault(c => c.HasParameters && c.Parameters.Count == 1 && c.Parameters[0].Name == "useCompiledXaml") is MethodDefinition baseCtorDef) {
+                                                       baseCtor = module.ImportReference(baseCtorDef);
+                                                       baseCtor = module.ImportReference(baseCtor.ResolveGenericParameters(typeDef.BaseType, module));
+                                                       il.Emit(OpCodes.Ldarg_1);
+                                               }
+                                               else {
+                                                       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);
index edd4351..03b68a1 100644 (file)
@@ -10,10 +10,7 @@ namespace Xamarin.Forms.Build.Tasks
 {
        class SetNamescopesAndRegisterNamesVisitor : IXamlNodeVisitor
        {
-               public SetNamescopesAndRegisterNamesVisitor(ILContext context)
-               {
-                       Context = context;
-               }
+               public SetNamescopesAndRegisterNamesVisitor(ILContext context) => Context = context;
 
                ILContext Context { get; }
 
@@ -25,7 +22,7 @@ namespace Xamarin.Forms.Build.Tasks
 
                public bool IsResourceDictionary(ElementNode node)
                {
-                       var parentVar = Context.Variables[(IElementNode)node];
+                       var parentVar = Context.Variables[node];
                        return parentVar.VariableType.FullName == "Xamarin.Forms.ResourceDictionary"
                                || parentVar.VariableType.Resolve().BaseType?.FullName == "Xamarin.Forms.ResourceDictionary";
                }
@@ -64,7 +61,7 @@ namespace Xamarin.Forms.Build.Tasks
        
                public void Visit(RootNode node, INode parentNode)
                {
-                       var namescopeVarDef = CreateNamescope();
+                       var namescopeVarDef = GetOrCreateNameScope(node);
                        IList<string> namesInNamescope = new List<string>();
                        if (Context.Variables[node].VariableType.InheritsFromOrImplements(Context.Body.Method.Module.ImportReference(("Xamarin.Forms.Core", "Xamarin.Forms", "BindableObject"))))
                                SetNameScope(node, namescopeVarDef);
@@ -77,33 +74,38 @@ namespace Xamarin.Forms.Build.Tasks
                }
 
                static bool IsDataTemplate(INode node, INode parentNode)
-               {
-                       var parentElement = parentNode as IElementNode;
-                       INode createContent;
-                       if (parentElement != null && parentElement.Properties.TryGetValue(XmlName._CreateContent, out createContent) &&
-                           createContent == node)
-                               return true;
-                       return false;
-               }
+                       => parentNode is IElementNode parentElement && parentElement.Properties.TryGetValue(XmlName._CreateContent, out INode createContent) && createContent == node;
 
-               static bool IsStyle(INode node, INode parentNode)
-               {
-                       var pnode = parentNode as ElementNode;
-                       return pnode != null && pnode.XmlType.Name == "Style";
-               }
+               static bool IsStyle(INode node, INode parentNode) => parentNode is ElementNode pnode && pnode.XmlType.Name == "Style";
 
-               static bool IsVisualStateGroupList(ElementNode node)
-               {
-                       return node != null  && node.XmlType.Name == "VisualStateGroup" && node.Parent is IListNode;
-               }
+               static bool IsVisualStateGroupList(ElementNode node) => node != null && node.XmlType.Name == "VisualStateGroup" && node.Parent is IListNode;
 
                static bool IsXNameProperty(ValueNode node, INode parentNode)
+                       => parentNode is IElementNode parentElement && parentElement.Properties.TryGetValue(XmlName.xName, out INode xNameNode) && xNameNode == node;
+
+               VariableDefinition GetOrCreateNameScope(ElementNode node)
                {
-                       var parentElement = parentNode as IElementNode;
-                       INode xNameNode;
-                       if (parentElement != null && parentElement.Properties.TryGetValue(XmlName.xName, out xNameNode) && xNameNode == node)
-                               return true;
-                       return false;
+                       var module = Context.Body.Method.Module;
+                       var vardef = new VariableDefinition(module.ImportReference(("Xamarin.Forms.Core", "Xamarin.Forms.Internals", "NameScope")));
+                       Context.Body.Variables.Add(vardef);
+                       var stloc = Instruction.Create(OpCodes.Stloc, vardef);
+
+                       if (Context.Variables[node].VariableType.InheritsFromOrImplements(Context.Body.Method.Module.ImportReference(("Xamarin.Forms.Core", "Xamarin.Forms", "BindableObject")))) {
+                               var namescoperef = ("Xamarin.Forms.Core", "Xamarin.Forms", "BindableObject");
+                               Context.IL.Append(Context.Variables[node].LoadAs(module.GetTypeDefinition(namescoperef), module));
+                               Context.IL.Emit(OpCodes.Call, module.ImportMethodReference(("Xamarin.Forms.Core", "Xamarin.Forms.Internals", "NameScope"),
+                                                                                                                                                  methodName: "GetNameScope",
+                                                                                                                                                  parameterTypes: new[] { namescoperef },
+                                                                                                                                                  isStatic: true));
+                               Context.IL.Emit(OpCodes.Dup);
+                               Context.IL.Emit(OpCodes.Brtrue, stloc);
+
+                               Context.IL.Emit(OpCodes.Pop);
+                       }
+                       Context.IL.Emit(OpCodes.Newobj, module.ImportCtorReference(("Xamarin.Forms.Core", "Xamarin.Forms.Internals", "NameScope"), parameterTypes: null));
+
+                       Context.IL.Append(stloc);
+                       return vardef;
                }
 
                VariableDefinition CreateNamescope()
index 9419bf0..bf20c11 100644 (file)
@@ -23,10 +23,7 @@ namespace Xamarin.Forms.Internals
                        _names[name] = scopedElement;
                }
 
-               public static INameScope GetNameScope(BindableObject bindable)
-               {
-                       return (INameScope)bindable.GetValue(NameScopeProperty);
-               }
+               public static INameScope GetNameScope(BindableObject bindable) => (INameScope)bindable.GetValue(NameScopeProperty);
 
                public static void SetNameScope(BindableObject bindable, INameScope value)
                {
diff --git a/Xamarin.Forms.Xaml.UnitTests/Issues/Gh7097.xaml b/Xamarin.Forms.Xaml.UnitTests/Issues/Gh7097.xaml
new file mode 100644 (file)
index 0000000..3a3e14c
--- /dev/null
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<local:Gh7097Base
+        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.Gh7097"
+        Title="Foo"
+        x:Name="self">
+    <CollectionView ItemsSource="{Binding Items}" x:Name="collectionview">
+            <CollectionView.ItemsLayout>
+                <ListItemsLayout ItemSpacing="4">
+                    <x:Arguments>
+                        <ItemsLayoutOrientation>Vertical</ItemsLayoutOrientation>
+                    </x:Arguments>
+                </ListItemsLayout>
+            </CollectionView.ItemsLayout>
+            <CollectionView.ItemTemplate>
+                <DataTemplate>
+                    <StackLayout Orientation="Horizontal" HorizontalOptions="FillAndExpand" BackgroundColor="Green">
+                        <Button 
+                            Text="BTN 1" 
+                            HorizontalOptions="FillAndExpand" 
+                            CommandParameter="{Binding}" 
+                            Command="{Binding BindingContext.Button1Command, Source={x:Reference self}}"/>
+                        <Button 
+                            Text="BTN 2" 
+                            HorizontalOptions="FillAndExpand" 
+                            CommandParameter="{Binding}" 
+                            Command="{Binding BindingContext.Button2Command, Source={x:Reference self}}"/>
+                    </StackLayout>
+                </DataTemplate>
+            </CollectionView.ItemTemplate>
+        </CollectionView>
+</local:Gh7097Base>
diff --git a/Xamarin.Forms.Xaml.UnitTests/Issues/Gh7097.xaml.cs b/Xamarin.Forms.Xaml.UnitTests/Issues/Gh7097.xaml.cs
new file mode 100644 (file)
index 0000000..a92e319
--- /dev/null
@@ -0,0 +1,70 @@
+using System;
+using System.Collections.Generic;
+using System.Windows.Input;
+using NUnit.Framework;
+using Xamarin.Forms;
+using Xamarin.Forms.Core.UnitTests;
+
+namespace Xamarin.Forms.Xaml.UnitTests
+{
+       public partial class Gh7097 : Gh7097Base
+       {
+               public Gh7097() => InitializeComponent();
+               public Gh7097(bool useCompiledXaml) : base(useCompiledXaml)
+               {
+                       //this stub will be replaced at compile time
+               }
+
+               [TestFixture]
+               class Tests
+               {
+                       IReadOnlyList<string> _flags;
+                       [SetUp]
+                       public void Setup()
+                       {
+                               Device.PlatformServices = new MockPlatformServices();
+                               _flags = Device.Flags;
+                               Device.SetFlags(new List<string>(Device.Flags ?? new List<string>()) { "CollectionView_Experimental" }.AsReadOnly());
+                       }
+
+                       [TearDown]
+                       public void TearDown()
+                       {
+                               Device.PlatformServices = null;
+                               Device.SetFlags(_flags);
+                       }
+
+                       [Test]
+                       public void CanXReferenceRoot([Values(false, true)]bool useCompiledXaml)
+                       {
+                               var layout = new Gh7097(useCompiledXaml) { BindingContext = new {
+                                               Button1Command = new MockCommand(),
+                                               Button2Command = new MockCommand(),
+                                       } };
+                               var cv = layout.Content as CollectionView;
+                               var content = cv.ItemTemplate.CreateContent() as StackLayout;
+                               var btn1 = content.Children[0] as Button;
+                               Assert.That(btn1.Command, Is.TypeOf<MockCommand>());
+                       }
+
+                       [Test]
+                       //this was later reported as https://github.com/xamarin/Xamarin.Forms/issues/7286
+                       public void RegisteringXNameOnSubPages([Values(false, true)]bool useCompiledXaml)
+                       {
+                               var layout = new Gh7097(useCompiledXaml);
+                               var s = layout.FindByName("self");
+                               Assert.That(layout.self, Is.Not.Null);
+                               Assert.That(layout.collectionview, Is.Not.Null);
+                       }
+
+                       class MockCommand : ICommand
+                       {
+#pragma warning disable 0067
+                               public event EventHandler CanExecuteChanged;
+#pragma warning restore 0067
+                               public bool CanExecute(object parameter) => true;
+                               public void Execute(object parameter) => throw new NotImplementedException();
+                       }
+               }
+       }
+}
diff --git a/Xamarin.Forms.Xaml.UnitTests/Issues/Gh7097Base.xaml b/Xamarin.Forms.Xaml.UnitTests/Issues/Gh7097Base.xaml
new file mode 100644 (file)
index 0000000..88152d3
--- /dev/null
@@ -0,0 +1,14 @@
+<?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.Gh7097Base">
+    <ContentPage.ControlTemplate>
+        <ControlTemplate>
+            <StackLayout>
+                <Label Text="BASE" FontSize="30" FontAttributes="Bold" HorizontalOptions="CenterAndExpand" TextColor="Red"/>
+                <ContentPresenter HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" />
+            </StackLayout>
+        </ControlTemplate>
+    </ContentPage.ControlTemplate>
+</ContentPage>
\ No newline at end of file
diff --git a/Xamarin.Forms.Xaml.UnitTests/Issues/Gh7097Base.xaml.cs b/Xamarin.Forms.Xaml.UnitTests/Issues/Gh7097Base.xaml.cs
new file mode 100644 (file)
index 0000000..c94415e
--- /dev/null
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+
+using Xamarin.Forms;
+
+namespace Xamarin.Forms.Xaml.UnitTests
+{
+       public partial class Gh7097Base : ContentPage
+       {
+               public Gh7097Base() => InitializeComponent();
+               public Gh7097Base(bool useCompiledXaml)
+               {
+                       //this stub will be replaced at compile time
+               }
+       }
+}
index 37ba170..806b63b 100644 (file)
@@ -36,11 +36,7 @@ namespace Xamarin.Forms.Xaml.UnitTests
 
                        public List<XmlName> SkipProperties { get; set; }
 
-                       public Forms.Internals.INameScope Namescope {
-                               get {
-                                       throw new NotImplementedException ();
-                               }
-                       }
+                       public NameScopeRef NameScopeRef => throw new NotImplementedException();
 
                        public XmlType XmlType {
                                get;
index 88e71ab..d7197f5 100644 (file)
@@ -141,8 +141,8 @@ namespace Xamarin.Forms.Xaml
                                Values[node] = value;
                        }
 
-                       if (value is BindableObject bindableValue && node.Namescope != (parentNode as IElementNode)?.Namescope)
-                               NameScope.SetNameScope(bindableValue, node.Namescope);
+                       if (value is BindableObject bindableValue && node.NameScopeRef != (parentNode as IElementNode)?.NameScopeRef)
+                               NameScope.SetNameScope(bindableValue, node.NameScopeRef.NameScope);
 
                        if (XamlLoader.ValueCreatedCallback != null) {
                                var name = node.XmlType.Name;
@@ -157,16 +157,18 @@ namespace Xamarin.Forms.Xaml
                        var rnode = (XamlLoader.RuntimeRootNode)node;
                        Values[node] = rnode.Root;
                        Context.Types[node] = rnode.Root.GetType();
-                       var bindableRoot = rnode.Root as BindableObject;
-                       if (bindableRoot != null)
-                               NameScope.SetNameScope(bindableRoot, node.Namescope);
+                       if (rnode.Root is BindableObject bindable) {
+                               if (NameScope.GetNameScope(bindable) is INameScope existingNs)
+                                       node.NameScopeRef.NameScope = existingNs;
+                               else
+                                       NameScope.SetNameScope(bindable, node.NameScopeRef?.NameScope);
+                       }
                }
 
                public void Visit(ListNode node, INode parentNode)
                {
                        //this is a gross hack to keep ListNode alive. ListNode must go in favor of Properties
-                       XmlName name;
-                       if (ApplyPropertiesVisitor.TryGetPropertyName(node, parentNode, out name))
+                       if (ApplyPropertiesVisitor.TryGetPropertyName(node, parentNode, out XmlName name))
                                node.XmlName = name;
                }
 
@@ -181,14 +183,12 @@ namespace Xamarin.Forms.Xaml
                                                        ci.GetParameters().All(pi => pi.CustomAttributes.Any(attr => attr.AttributeType == typeof (ParameterAttribute))));
                        if (ctorInfo == null)
                                return true;
-                       foreach (var parameter in ctorInfo.GetParameters())
-                       {
+                       foreach (var parameter in ctorInfo.GetParameters()) {
                                var propname =
                                        parameter.CustomAttributes.First(ca => ca.AttributeType.FullName == "Xamarin.Forms.ParameterAttribute")
                                                .ConstructorArguments.First()
                                                .Value as string;
-                               if (!node.Properties.ContainsKey(new XmlName("", propname)))
-                               {
+                               if (!node.Properties.ContainsKey(new XmlName("", propname))) {
                                        missingArgName = propname;
                                        return false;
                                }
@@ -219,8 +219,7 @@ namespace Xamarin.Forms.Xaml
                {
                        object[] arguments = CreateArgumentsArray(node);
 
-                       if (!node.Properties.ContainsKey(XmlName.xFactoryMethod))
-                       {
+                       if (!node.Properties.ContainsKey(XmlName.xFactoryMethod)) {
                                //non-default ctor
                                try {
                                        return Activator.CreateInstance(nodeType, arguments);
@@ -232,7 +231,9 @@ namespace Xamarin.Forms.Xaml
 
                        var factoryMethod = ((string)((ValueNode)node.Properties[XmlName.xFactoryMethod]).Value);
                        Type[] types = arguments == null ? new Type[0] : arguments.Select(a => a.GetType()).ToArray();
-                       Func<MethodInfo, bool> isMatch = m => {
+
+                       bool isMatch(MethodInfo m)
+                       {
                                if (m.Name != factoryMethod)
                                        return false;
                                var p = m.GetParameters();
@@ -241,17 +242,18 @@ namespace Xamarin.Forms.Xaml
                                if (!m.IsStatic)
                                        return false;
                                for (var i = 0; i < p.Length; i++) {
-                                       if ((p [i].ParameterType.IsAssignableFrom(types [i])))
+                                       if ((p[i].ParameterType.IsAssignableFrom(types[i])))
                                                continue;
-                                       var op_impl =  p[i].ParameterType.GetImplicitConversionOperator(fromType: types[i], toType: p[i].ParameterType)
+                                       var op_impl = p[i].ParameterType.GetImplicitConversionOperator(fromType: types[i], toType: p[i].ParameterType)
                                                                ?? types[i].GetImplicitConversionOperator(fromType: types[i], toType: p[i].ParameterType);
 
                                        if (op_impl == null)
                                                return false;
-                                       arguments [i] = op_impl.Invoke(null, new [] { arguments [i]});
+                                       arguments[i] = op_impl.Invoke(null, new[] { arguments[i] });
                                }
                                return true;
-                       };
+                       }
+
                        try {
                                var mi = nodeType.GetRuntimeMethods().FirstOrDefault(isMatch);
                                if (mi == null)
@@ -268,17 +270,13 @@ namespace Xamarin.Forms.Xaml
                        if (!enode.Properties.ContainsKey(XmlName.xArguments))
                                return null;
                        var node = enode.Properties[XmlName.xArguments];
-                       var elementNode = node as ElementNode;
-                       if (elementNode != null)
-                       {
+                       if (node is ElementNode elementNode) {
                                var array = new object[1];
                                array[0] = Values[elementNode];
                                return array;
                        }
 
-                       var listnode = node as ListNode;
-                       if (listnode != null)
-                       {
+                       if (node is ListNode listnode) {
                                var array = new object[listnode.CollectionItems.Count];
                                for (var i = 0; i < listnode.CollectionItems.Count; i++)
                                        array[i] = Values[(ElementNode)listnode.CollectionItems[i]];
@@ -291,21 +289,15 @@ namespace Xamarin.Forms.Xaml
                {
                        var n = ctorInfo.GetParameters().Length;
                        var array = new object[n];
-                       for (var i = 0; i < n; i++)
-                       {
+                       for (var i = 0; i < n; i++) {
                                var parameter = ctorInfo.GetParameters()[i];
                                var propname =
                                        parameter.CustomAttributes.First(attr => attr.AttributeType == typeof (ParameterAttribute))
                                                .ConstructorArguments.First()
                                                .Value as string;
                                var name = new XmlName("", propname);
-                               INode node;
-                               if (!enode.Properties.TryGetValue(name, out node))
-                               {
-                                       throw new XamlParseException(
-                                               String.Format("The Property {0} is required to create a {1} object.", propname, ctorInfo.DeclaringType.FullName),
-                                               enode as IXmlLineInfo);
-                               }
+                               if (!enode.Properties.TryGetValue(name, out INode node))
+                                       throw new XamlParseException($"The Property {propname} is required to create a {ctorInfo.DeclaringType.FullName} object.", enode as IXmlLineInfo);
                                if (!enode.SkipProperties.Contains(name))
                                        enode.SkipProperties.Add(name);
                                var value = Context.Values[node];
@@ -319,14 +311,11 @@ namespace Xamarin.Forms.Xaml
                        return array;
                }
 
-               static bool IsXaml2009LanguagePrimitive(IElementNode node)
-               {
-                       return node.NamespaceURI == XamlParser.X2009Uri;
-               }
+               static bool IsXaml2009LanguagePrimitive(IElementNode node) => node.NamespaceURI == XamlParser.X2009Uri;
 
                static object CreateLanguagePrimitive(Type nodeType, IElementNode node)
                {
-                       object value = null;
+                       object value;
                        if (nodeType == typeof (string))
                                value = String.Empty;
                        else if (nodeType == typeof (Uri))
@@ -334,95 +323,43 @@ namespace Xamarin.Forms.Xaml
                        else
                                value = Activator.CreateInstance(nodeType);
 
-                       if (node.CollectionItems.Count == 1 && node.CollectionItems[0] is ValueNode &&
-                           ((ValueNode)node.CollectionItems[0]).Value is string)
-                       {
-                               var valuestring = ((ValueNode)node.CollectionItems[0]).Value as string;
-
-                               if (nodeType == typeof(SByte)) {
-                                       sbyte retval;
-                                       if (sbyte.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
-                                               return retval;
-                               }
-                               if (nodeType == typeof(Int16)) {
-                                       short retval;
-                                       if (short.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
-                                               return retval;
-                               }
-                               if (nodeType == typeof(Int32)) {
-                                       int retval;
-                                       if (int.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
-                                               return retval;
-                               }
-                               if (nodeType == typeof(Int64)) {
-                                       long retval;
-                                       if (long.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
-                                               return retval;
-                               }
-                               if (nodeType == typeof(Byte)) {
-                                       byte retval;
-                                       if (byte.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
-                                               return retval;
-                               }
-                               if (nodeType == typeof(UInt16)) {
-                                       ushort retval;
-                                       if (ushort.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
-                                               return retval;
-                               }
-                               if (nodeType == typeof(UInt32)) {
-                                       uint retval;
-                                       if (uint.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
-                                               return retval;
-                               }
-                               if (nodeType == typeof(UInt64)) {
-                                       ulong retval;
-                                       if (ulong.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
-                                               return retval;
-                               }
-                               if (nodeType == typeof(Single)) {
-                                       float retval;
-                                       if (float.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
-                                               return retval;
-                               }
-                               if (nodeType == typeof(Double)) {
-                                       double retval;
-                                       if (double.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
-                                               return retval;
-                               }
-                               if (nodeType == typeof (Boolean))
-                               {
-                                       bool outbool;
-                                       if (bool.TryParse(valuestring, out outbool))
-                                               return outbool;
-                               }
-                               if (nodeType == typeof(TimeSpan)) {
-                                       TimeSpan retval;
-                                       if (TimeSpan.TryParse(valuestring, CultureInfo.InvariantCulture, out retval))
-                                               return retval;
-                               }
-                               if (nodeType == typeof (char))
-                               {
-                                       char retval;
-                                       if (char.TryParse(valuestring, out retval))
-                                               return retval;
-                               }
+                       if (   node.CollectionItems.Count == 1
+                               && node.CollectionItems[0] is ValueNode
+                               && ((ValueNode)node.CollectionItems[0]).Value is string valuestring) {
+                               if (nodeType == typeof(SByte) && sbyte.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out var sbyteval))
+                                       return sbyteval;
+                               if (nodeType == typeof(Int16) && short.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out var int16val))
+                                       return int16val;
+                               if (nodeType == typeof(Int32) && int.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out var int32val))
+                                       return int32val;
+                               if (nodeType == typeof(Int64) && long.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out var int64val))
+                                       return int64val;
+                               if (nodeType == typeof(Byte) && byte.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out var byteval))
+                                       return byteval;
+                               if (nodeType == typeof(UInt16) && ushort.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out var uint16val))
+                                       return uint16val;
+                               if (nodeType == typeof(UInt32) && uint.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out var uint32val))
+                                       return uint32val;
+                               if (nodeType == typeof(UInt64) && ulong.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out var uint64val))
+                                       return uint64val;
+                               if (nodeType == typeof(Single) && float.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out var singleval))
+                                       return singleval;
+                               if (nodeType == typeof(Double) && double.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out var doubleval))
+                                       return doubleval;
+                               if (nodeType == typeof(Boolean) && bool.TryParse(valuestring, out var boolval))
+                                       return boolval;
+                               if (nodeType == typeof(TimeSpan) && TimeSpan.TryParse(valuestring, CultureInfo.InvariantCulture, out TimeSpan timespanval))
+                                       return timespanval;
+                               if (nodeType == typeof(char) && char.TryParse(valuestring, out var charval))
+                                       return charval;
                                if (nodeType == typeof (string))
                                        return valuestring;
-                               if (nodeType == typeof (decimal))
-                               {
-                                       decimal retval;
-                                       if (decimal.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
-                                               return retval;
-                               }
-
-                               else if (nodeType == typeof (Uri))
-                               {
-                                       Uri retval;
-                                       if (Uri.TryCreate(valuestring, UriKind.RelativeOrAbsolute, out retval))
-                                               return retval;
-                               }
+                               if (nodeType == typeof(decimal) && decimal.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out var decimalval))
+                                       return decimalval;
+                               if (nodeType == typeof(Uri) && Uri.TryCreate(valuestring, UriKind.RelativeOrAbsolute, out Uri urival))
+                                       return urival;
                        }
                        return value;
                }
        }
-}
+}
\ No newline at end of file
index 97e9fc7..5cde2cb 100644 (file)
@@ -5,12 +5,11 @@ namespace Xamarin.Forms.Xaml
 {
        class NamescopingVisitor : IXamlNodeVisitor
        {
-               readonly Dictionary<INode, INameScope> _scopes = new Dictionary<INode, INameScope>();
+               readonly Dictionary<INode, NameScopeRef> _scopes = new Dictionary<INode, NameScopeRef>();
 
                public NamescopingVisitor(HydrationContext context)
-                       => Values = context.Values;
-
-               Dictionary<INode, object> Values { get; set; }
+               {
+               }
 
                public TreeVisitingMode VisitingMode => TreeVisitingMode.TopDown;
                public bool StopOnDataTemplate => false;
@@ -19,37 +18,29 @@ namespace Xamarin.Forms.Xaml
                public bool SkipChildren(INode node, INode parentNode) => false;
                public bool IsResourceDictionary(ElementNode node) => false;
 
-               public void Visit(ValueNode node, INode parentNode)
-                       => _scopes[node] = _scopes[parentNode];
-
-               public void Visit(MarkupNode node, INode parentNode)
-                       => _scopes[node] = _scopes[parentNode];
+               public void Visit(ValueNode node, INode parentNode) => _scopes[node] = _scopes[parentNode];
+               public void Visit(MarkupNode node, INode parentNode) => _scopes[node] = _scopes[parentNode];
 
                public void Visit(ElementNode node, INode parentNode)
-                       => _scopes[node] = node.Namescope = (parentNode == null || IsDataTemplate(node, parentNode) || IsStyle(node, parentNode) || IsVisualStateGroupList(node))
-                                                                  ? new NameScope()
+                       => _scopes[node] = node.NameScopeRef = (parentNode == null || IsDataTemplate(node, parentNode) || IsStyle(node, parentNode) || IsVisualStateGroupList(node))
+                                                                  ? new NameScopeRef { NameScope = new NameScope() }
                                                                   : _scopes[parentNode];
 
-               public void Visit(RootNode node, INode parentNode)
-                       => _scopes[node] = node.Namescope = new NameScope();
+               public void Visit(RootNode node, INode parentNode) => _scopes[node] = node.NameScopeRef = new NameScopeRef { NameScope = new NameScope() };
 
                public void Visit(ListNode node, INode parentNode) =>
                        _scopes[node] = _scopes[parentNode];
 
                static bool IsDataTemplate(INode node, INode parentNode)
                {
-                       var parentElement = parentNode as IElementNode;
-                       if (   parentElement != null
-                           && parentElement.Properties.TryGetValue(XmlName._CreateContent, out var createContent)
-                           && createContent == node)
+                       if (   parentNode is IElementNode parentElement
+                               && parentElement.Properties.TryGetValue(XmlName._CreateContent, out var createContent)
+                               && createContent == node)
                                return true;
                        return false;
                }
 
-               static bool IsStyle(INode node, INode parentNode)
-                       => (parentNode as ElementNode)?.XmlType.Name == "Style";
-
-               static bool IsVisualStateGroupList(ElementNode node)
-                       => node?.XmlType.Name == "VisualStateGroup" && node?.Parent is IListNode;
+               static bool IsStyle(INode node, INode parentNode) => (parentNode as ElementNode)?.XmlType.Name == "Style";
+               static bool IsVisualStateGroupList(ElementNode node) => node?.XmlType.Name == "VisualStateGroup" && node?.Parent is IListNode;
        }
 }
\ No newline at end of file
index 7d70da5..881b1ab 100644 (file)
@@ -1,11 +1,6 @@
 using System;
 using System.Collections.Generic;
-using System.Globalization;
-using System.Linq;
-using System.Reflection;
-using System.Xml;
 using Xamarin.Forms.Internals;
-using Xamarin.Forms.Xaml.Internals;
 
 namespace Xamarin.Forms.Xaml
 {
@@ -32,7 +27,7 @@ namespace Xamarin.Forms.Xaml
                                return;
 
                        try {
-                               ((IElementNode)parentNode).Namescope.RegisterName((string)node.Value, Values[parentNode]);
+                               ((IElementNode)parentNode).NameScopeRef.NameScope.RegisterName((string)node.Value, Values[parentNode]);
                        }
                        catch (ArgumentException ae) {
                                if (ae.ParamName != "name")
@@ -73,12 +68,6 @@ namespace Xamarin.Forms.Xaml
                }
 
                static bool IsXNameProperty(ValueNode node, INode parentNode)
-               {
-                       var parentElement = parentNode as IElementNode;
-                       INode xNameNode;
-                       if (parentElement != null && parentElement.Properties.TryGetValue(XmlName.xName, out xNameNode) && xNameNode == node)
-                               return true;
-                       return false;
-               }
+                       => parentNode is IElementNode parentElement && parentElement.Properties.TryGetValue(XmlName.xName, out INode xNameNode) && xNameNode == node;
        }
 }
index e5b536c..a6eea39 100644 (file)
@@ -24,7 +24,7 @@ namespace Xamarin.Forms.Xaml
        {
                Dictionary<XmlName, INode> Properties { get; }
                List<XmlName> SkipProperties { get; }
-               INameScope Namescope { get; }
+               NameScopeRef NameScopeRef { get; }
                XmlType XmlType { get; }
                string NamespaceURI { get; }
        }
@@ -34,6 +34,11 @@ namespace Xamarin.Forms.Xaml
                List<INode> CollectionItems { get; }
        }
 
+       class NameScopeRef
+       {
+               public INameScope NameScope { get; set; }
+       }
+
        [DebuggerDisplay("{NamespaceUri}:{Name}")]
        class XmlType
        {
@@ -112,6 +117,7 @@ namespace Xamarin.Forms.Xaml
                };
        }
 
+
        [DebuggerDisplay("{XmlType.Name}")]
        class ElementNode : BaseNode, IValueNode, IElementNode
        {
@@ -131,7 +137,7 @@ namespace Xamarin.Forms.Xaml
                public List<INode> CollectionItems { get; }
                public XmlType XmlType { get; }
                public string NamespaceURI { get; }
-               public INameScope Namescope { get; set; }
+               public NameScopeRef NameScopeRef { get; set; }
 
                public override void Accept(IXamlNodeVisitor visitor, INode parentNode)
                {
@@ -152,10 +158,8 @@ namespace Xamarin.Forms.Xaml
 
                bool IsDataTemplate(INode parentNode)
                {
-                       var parentElement = parentNode as IElementNode;
-                       INode createContent;
-                       if (parentElement != null &&
-                               parentElement.Properties.TryGetValue(XmlName._CreateContent, out createContent) &&
+                       if (parentNode is IElementNode parentElement &&
+                               parentElement.Properties.TryGetValue(XmlName._CreateContent, out INode createContent) &&
                                createContent == this)
                                return true;
                        return false;
index 5185024..b6d2000 100644 (file)
@@ -74,16 +74,9 @@ namespace Xamarin.Forms.Xaml.Internals
                        set { services[typeof (IValueConverterProvider)] = value; }
                }
 
-               public object GetService(Type serviceType)
-               {
-                       object service;
-                       return services.TryGetValue(serviceType, out service) ? service : null;
-               }
+               public object GetService(Type serviceType) => services.TryGetValue(serviceType, out var service) ? service : null;
 
-               public void Add(Type type, object service)
-               {
-                       services.Add(type, service);
-               }
+               public void Add(Type type, object service) => services.Add(type, service);
        }
 
        class XamlValueTargetProvider : IProvideParentValues, IProvideValueTarget
@@ -104,21 +97,16 @@ namespace Xamarin.Forms.Xaml.Internals
 
                IEnumerable<object> IProvideParentValues.ParentObjects
                {
-                       get
-                       {
+                       get {
                                if (Node == null || Context == null)
                                        yield break;
                                var n = Node;
-                               object obj = null;
                                var context = Context;
-                               while (n.Parent != null && context != null)
-                               {
-                                       if (n.Parent is IElementNode)
-                                       {
-                                               if (context.Values.TryGetValue(n.Parent, out obj))
+                               while (n.Parent != null && context != null) {
+                                       if (n.Parent is IElementNode) {
+                                               if (context.Values.TryGetValue(n.Parent, out var obj))
                                                        yield return obj;
-                                               else
-                                               {
+                                               else {
                                                        context = context.ParentContext;
                                                        continue;
                                                }
@@ -159,14 +147,9 @@ namespace Xamarin.Forms.Xaml.Internals
                        this.scope = scope;
                }
 
-               IEnumerable<object> IProvideParentValues.ParentObjects
-                       => objectAndParents;
-
-               object IProvideValueTarget.TargetObject
-                       => objectAndParents[0];
-
-               object IProvideValueTarget.TargetProperty
-                       => targetProperty;
+               IEnumerable<object> IProvideParentValues.ParentObjects => objectAndParents;
+               object IProvideValueTarget.TargetObject => objectAndParents[0];
+               object IProvideValueTarget.TargetProperty => targetProperty;
 
                public object FindByName(string name)
                {
@@ -277,15 +260,14 @@ namespace Xamarin.Forms.Xaml.Internals
        class ReferenceProvider : IReferenceProvider
        {
                readonly INode _node;
-               internal ReferenceProvider(INode node)
-                       => _node = node;
+               internal ReferenceProvider(INode node) => _node = node;
 
                public object FindByName(string name)
                {
                        var n = _node;
-                       object value = null;
                        while (n != null) {
-                               if ((value = (n as IElementNode)?.Namescope?.FindByName(name)) != null)
+                               object value;
+                               if ((value = (n as IElementNode)?.NameScopeRef.NameScope?.FindByName(name)) != null)
                                        return value;
                                n = n.Parent;
                        }
@@ -304,27 +286,16 @@ namespace Xamarin.Forms.Xaml.Internals
        {
                readonly Dictionary<string, string> namespaces = new Dictionary<string, string>();
 
-               public IDictionary<string, string> GetNamespacesInScope(XmlNamespaceScope scope)
-               {
-                       throw new NotImplementedException();
-               }
+               public IDictionary<string, string> GetNamespacesInScope(XmlNamespaceScope scope) => throw new NotImplementedException();
 
                public string LookupNamespace(string prefix)
                {
-                       string result;
-                       if (namespaces.TryGetValue(prefix, out result))
+                       if (namespaces.TryGetValue(prefix, out var result))
                                return result;
                        return null;
                }
 
-               public string LookupPrefix(string namespaceName)
-               {
-                       throw new NotImplementedException();
-               }
-
-               public void Add(string prefix, string ns)
-               {
-                       namespaces.Add(prefix, ns);
-               }
+               public string LookupPrefix(string namespaceName) => throw new NotImplementedException();
+               public void Add(string prefix, string ns) => namespaces.Add(prefix, ns);
        }
 }
\ No newline at end of file