From 395dc293f16111123dcdc6217647456bc4fe5694 Mon Sep 17 00:00:00 2001 From: Stephane Delcroix Date: Tue, 3 Sep 2019 22:45:22 +0200 Subject: [PATCH] [X] Reuse baseclass namescope if it exists (#7356) * [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 --- Xamarin.Forms.Build.Tasks/DebugXamlCTask.cs | 12 +- .../SetNamescopesAndRegisterNamesVisitor.cs | 58 +++--- Xamarin.Forms.Core/Internals/NameScope.cs | 5 +- .../Issues/Gh7097.xaml | 34 ++++ .../Issues/Gh7097.xaml.cs | 70 +++++++ .../Issues/Gh7097Base.xaml | 14 ++ .../Issues/Gh7097Base.xaml.cs | 16 ++ .../MarkupExpressionParserTests.cs | 6 +- Xamarin.Forms.Xaml/CreateValuesVisitor.cs | 185 ++++++------------ Xamarin.Forms.Xaml/NamescopingVisitor.cs | 35 ++-- Xamarin.Forms.Xaml/RegisterXNamesVisitor.cs | 15 +- Xamarin.Forms.Xaml/XamlNode.cs | 16 +- Xamarin.Forms.Xaml/XamlServiceProvider.cs | 63 ++---- 13 files changed, 279 insertions(+), 250 deletions(-) create mode 100644 Xamarin.Forms.Xaml.UnitTests/Issues/Gh7097.xaml create mode 100644 Xamarin.Forms.Xaml.UnitTests/Issues/Gh7097.xaml.cs create mode 100644 Xamarin.Forms.Xaml.UnitTests/Issues/Gh7097Base.xaml create mode 100644 Xamarin.Forms.Xaml.UnitTests/Issues/Gh7097Base.xaml.cs diff --git a/Xamarin.Forms.Build.Tasks/DebugXamlCTask.cs b/Xamarin.Forms.Build.Tasks/DebugXamlCTask.cs index af934c52e..25be35bbd 100644 --- a/Xamarin.Forms.Build.Tasks/DebugXamlCTask.cs +++ b/Xamarin.Forms.Build.Tasks/DebugXamlCTask.cs @@ -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); diff --git a/Xamarin.Forms.Build.Tasks/SetNamescopesAndRegisterNamesVisitor.cs b/Xamarin.Forms.Build.Tasks/SetNamescopesAndRegisterNamesVisitor.cs index edd435167..03b68a158 100644 --- a/Xamarin.Forms.Build.Tasks/SetNamescopesAndRegisterNamesVisitor.cs +++ b/Xamarin.Forms.Build.Tasks/SetNamescopesAndRegisterNamesVisitor.cs @@ -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 namesInNamescope = new List(); 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() diff --git a/Xamarin.Forms.Core/Internals/NameScope.cs b/Xamarin.Forms.Core/Internals/NameScope.cs index 9419bf02b..bf20c1111 100644 --- a/Xamarin.Forms.Core/Internals/NameScope.cs +++ b/Xamarin.Forms.Core/Internals/NameScope.cs @@ -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 index 000000000..3a3e14ca9 --- /dev/null +++ b/Xamarin.Forms.Xaml.UnitTests/Issues/Gh7097.xaml @@ -0,0 +1,34 @@ + + + + + + + Vertical + + + + + + +