[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
This commit is contained in:
Stephane Delcroix 2019-09-03 22:45:22 +02:00 коммит произвёл GitHub
Родитель a6f2852425
Коммит 395dc293f1
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
13 изменённых файлов: 279 добавлений и 250 удалений

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

@ -113,8 +113,16 @@ namespace Xamarin.Forms.Build.Tasks
var br2 = Instruction.Create(OpCodes.Ldarg_0); var br2 = Instruction.Create(OpCodes.Ldarg_0);
var ret = Instruction.Create(OpCodes.Ret); var ret = Instruction.Create(OpCodes.Ret);
il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldarg_0);
var baseCtor = module.ImportReference(typeDef.BaseType.Resolve().GetConstructors().First(c => c.HasParameters == false)); MethodReference baseCtor;
baseCtor = module.ImportReference(baseCtor.ResolveGenericParameters(typeDef.BaseType, module)); 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.Callvirt, baseCtor);
il.Emit(OpCodes.Nop); il.Emit(OpCodes.Nop);

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

@ -10,10 +10,7 @@ namespace Xamarin.Forms.Build.Tasks
{ {
class SetNamescopesAndRegisterNamesVisitor : IXamlNodeVisitor class SetNamescopesAndRegisterNamesVisitor : IXamlNodeVisitor
{ {
public SetNamescopesAndRegisterNamesVisitor(ILContext context) public SetNamescopesAndRegisterNamesVisitor(ILContext context) => Context = context;
{
Context = context;
}
ILContext Context { get; } ILContext Context { get; }
@ -25,7 +22,7 @@ namespace Xamarin.Forms.Build.Tasks
public bool IsResourceDictionary(ElementNode node) public bool IsResourceDictionary(ElementNode node)
{ {
var parentVar = Context.Variables[(IElementNode)node]; var parentVar = Context.Variables[node];
return parentVar.VariableType.FullName == "Xamarin.Forms.ResourceDictionary" return parentVar.VariableType.FullName == "Xamarin.Forms.ResourceDictionary"
|| parentVar.VariableType.Resolve().BaseType?.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) public void Visit(RootNode node, INode parentNode)
{ {
var namescopeVarDef = CreateNamescope(); var namescopeVarDef = GetOrCreateNameScope(node);
IList<string> namesInNamescope = new List<string>(); IList<string> namesInNamescope = new List<string>();
if (Context.Variables[node].VariableType.InheritsFromOrImplements(Context.Body.Method.Module.ImportReference(("Xamarin.Forms.Core", "Xamarin.Forms", "BindableObject")))) if (Context.Variables[node].VariableType.InheritsFromOrImplements(Context.Body.Method.Module.ImportReference(("Xamarin.Forms.Core", "Xamarin.Forms", "BindableObject"))))
SetNameScope(node, namescopeVarDef); SetNameScope(node, namescopeVarDef);
@ -77,33 +74,38 @@ namespace Xamarin.Forms.Build.Tasks
} }
static bool IsDataTemplate(INode node, INode parentNode) static bool IsDataTemplate(INode node, INode parentNode)
{ => parentNode is IElementNode parentElement && parentElement.Properties.TryGetValue(XmlName._CreateContent, out INode createContent) && createContent == node;
var parentElement = parentNode as IElementNode;
INode createContent;
if (parentElement != null && parentElement.Properties.TryGetValue(XmlName._CreateContent, out createContent) &&
createContent == node)
return true;
return false;
}
static bool IsStyle(INode node, INode parentNode) static bool IsStyle(INode node, INode parentNode) => parentNode is ElementNode pnode && pnode.XmlType.Name == "Style";
{
var pnode = parentNode as ElementNode;
return pnode != null && pnode.XmlType.Name == "Style";
}
static bool IsVisualStateGroupList(ElementNode node) static bool IsVisualStateGroupList(ElementNode node) => node != null && node.XmlType.Name == "VisualStateGroup" && node.Parent is IListNode;
{
return node != null && node.XmlType.Name == "VisualStateGroup" && node.Parent is IListNode;
}
static bool IsXNameProperty(ValueNode node, INode parentNode) 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; var module = Context.Body.Method.Module;
INode xNameNode; var vardef = new VariableDefinition(module.ImportReference(("Xamarin.Forms.Core", "Xamarin.Forms.Internals", "NameScope")));
if (parentElement != null && parentElement.Properties.TryGetValue(XmlName.xName, out xNameNode) && xNameNode == node) Context.Body.Variables.Add(vardef);
return true; var stloc = Instruction.Create(OpCodes.Stloc, vardef);
return false;
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() VariableDefinition CreateNamescope()

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

@ -23,10 +23,7 @@ namespace Xamarin.Forms.Internals
_names[name] = scopedElement; _names[name] = scopedElement;
} }
public static INameScope GetNameScope(BindableObject bindable) public static INameScope GetNameScope(BindableObject bindable) => (INameScope)bindable.GetValue(NameScopeProperty);
{
return (INameScope)bindable.GetValue(NameScopeProperty);
}
public static void SetNameScope(BindableObject bindable, INameScope value) public static void SetNameScope(BindableObject bindable, INameScope value)
{ {

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

@ -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>

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

@ -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();
}
}
}
}

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

@ -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>

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

@ -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
}
}
}

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

@ -36,11 +36,7 @@ namespace Xamarin.Forms.Xaml.UnitTests
public List<XmlName> SkipProperties { get; set; } public List<XmlName> SkipProperties { get; set; }
public Forms.Internals.INameScope Namescope { public NameScopeRef NameScopeRef => throw new NotImplementedException();
get {
throw new NotImplementedException ();
}
}
public XmlType XmlType { public XmlType XmlType {
get; get;

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

@ -141,8 +141,8 @@ namespace Xamarin.Forms.Xaml
Values[node] = value; Values[node] = value;
} }
if (value is BindableObject bindableValue && node.Namescope != (parentNode as IElementNode)?.Namescope) if (value is BindableObject bindableValue && node.NameScopeRef != (parentNode as IElementNode)?.NameScopeRef)
NameScope.SetNameScope(bindableValue, node.Namescope); NameScope.SetNameScope(bindableValue, node.NameScopeRef.NameScope);
if (XamlLoader.ValueCreatedCallback != null) { if (XamlLoader.ValueCreatedCallback != null) {
var name = node.XmlType.Name; var name = node.XmlType.Name;
@ -157,16 +157,18 @@ namespace Xamarin.Forms.Xaml
var rnode = (XamlLoader.RuntimeRootNode)node; var rnode = (XamlLoader.RuntimeRootNode)node;
Values[node] = rnode.Root; Values[node] = rnode.Root;
Context.Types[node] = rnode.Root.GetType(); Context.Types[node] = rnode.Root.GetType();
var bindableRoot = rnode.Root as BindableObject; if (rnode.Root is BindableObject bindable) {
if (bindableRoot != null) if (NameScope.GetNameScope(bindable) is INameScope existingNs)
NameScope.SetNameScope(bindableRoot, node.Namescope); node.NameScopeRef.NameScope = existingNs;
else
NameScope.SetNameScope(bindable, node.NameScopeRef?.NameScope);
}
} }
public void Visit(ListNode node, INode parentNode) public void Visit(ListNode node, INode parentNode)
{ {
//this is a gross hack to keep ListNode alive. ListNode must go in favor of Properties //this is a gross hack to keep ListNode alive. ListNode must go in favor of Properties
XmlName name; if (ApplyPropertiesVisitor.TryGetPropertyName(node, parentNode, out XmlName name))
if (ApplyPropertiesVisitor.TryGetPropertyName(node, parentNode, out name))
node.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)))); ci.GetParameters().All(pi => pi.CustomAttributes.Any(attr => attr.AttributeType == typeof (ParameterAttribute))));
if (ctorInfo == null) if (ctorInfo == null)
return true; return true;
foreach (var parameter in ctorInfo.GetParameters()) foreach (var parameter in ctorInfo.GetParameters()) {
{
var propname = var propname =
parameter.CustomAttributes.First(ca => ca.AttributeType.FullName == "Xamarin.Forms.ParameterAttribute") parameter.CustomAttributes.First(ca => ca.AttributeType.FullName == "Xamarin.Forms.ParameterAttribute")
.ConstructorArguments.First() .ConstructorArguments.First()
.Value as string; .Value as string;
if (!node.Properties.ContainsKey(new XmlName("", propname))) if (!node.Properties.ContainsKey(new XmlName("", propname))) {
{
missingArgName = propname; missingArgName = propname;
return false; return false;
} }
@ -219,8 +219,7 @@ namespace Xamarin.Forms.Xaml
{ {
object[] arguments = CreateArgumentsArray(node); object[] arguments = CreateArgumentsArray(node);
if (!node.Properties.ContainsKey(XmlName.xFactoryMethod)) if (!node.Properties.ContainsKey(XmlName.xFactoryMethod)) {
{
//non-default ctor //non-default ctor
try { try {
return Activator.CreateInstance(nodeType, arguments); return Activator.CreateInstance(nodeType, arguments);
@ -232,7 +231,9 @@ namespace Xamarin.Forms.Xaml
var factoryMethod = ((string)((ValueNode)node.Properties[XmlName.xFactoryMethod]).Value); var factoryMethod = ((string)((ValueNode)node.Properties[XmlName.xFactoryMethod]).Value);
Type[] types = arguments == null ? new Type[0] : arguments.Select(a => a.GetType()).ToArray(); 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) if (m.Name != factoryMethod)
return false; return false;
var p = m.GetParameters(); var p = m.GetParameters();
@ -241,17 +242,18 @@ namespace Xamarin.Forms.Xaml
if (!m.IsStatic) if (!m.IsStatic)
return false; return false;
for (var i = 0; i < p.Length; i++) { for (var i = 0; i < p.Length; i++) {
if ((p [i].ParameterType.IsAssignableFrom(types [i]))) if ((p[i].ParameterType.IsAssignableFrom(types[i])))
continue; 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); ?? types[i].GetImplicitConversionOperator(fromType: types[i], toType: p[i].ParameterType);
if (op_impl == null) if (op_impl == null)
return false; return false;
arguments [i] = op_impl.Invoke(null, new [] { arguments [i]}); arguments[i] = op_impl.Invoke(null, new[] { arguments[i] });
} }
return true; return true;
}; }
try { try {
var mi = nodeType.GetRuntimeMethods().FirstOrDefault(isMatch); var mi = nodeType.GetRuntimeMethods().FirstOrDefault(isMatch);
if (mi == null) if (mi == null)
@ -268,17 +270,13 @@ namespace Xamarin.Forms.Xaml
if (!enode.Properties.ContainsKey(XmlName.xArguments)) if (!enode.Properties.ContainsKey(XmlName.xArguments))
return null; return null;
var node = enode.Properties[XmlName.xArguments]; var node = enode.Properties[XmlName.xArguments];
var elementNode = node as ElementNode; if (node is ElementNode elementNode) {
if (elementNode != null)
{
var array = new object[1]; var array = new object[1];
array[0] = Values[elementNode]; array[0] = Values[elementNode];
return array; return array;
} }
var listnode = node as ListNode; if (node is ListNode listnode) {
if (listnode != null)
{
var array = new object[listnode.CollectionItems.Count]; var array = new object[listnode.CollectionItems.Count];
for (var i = 0; i < listnode.CollectionItems.Count; i++) for (var i = 0; i < listnode.CollectionItems.Count; i++)
array[i] = Values[(ElementNode)listnode.CollectionItems[i]]; array[i] = Values[(ElementNode)listnode.CollectionItems[i]];
@ -291,21 +289,15 @@ namespace Xamarin.Forms.Xaml
{ {
var n = ctorInfo.GetParameters().Length; var n = ctorInfo.GetParameters().Length;
var array = new object[n]; 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 parameter = ctorInfo.GetParameters()[i];
var propname = var propname =
parameter.CustomAttributes.First(attr => attr.AttributeType == typeof (ParameterAttribute)) parameter.CustomAttributes.First(attr => attr.AttributeType == typeof (ParameterAttribute))
.ConstructorArguments.First() .ConstructorArguments.First()
.Value as string; .Value as string;
var name = new XmlName("", propname); var name = new XmlName("", propname);
INode node; if (!enode.Properties.TryGetValue(name, out INode node))
if (!enode.Properties.TryGetValue(name, out node)) throw new XamlParseException($"The Property {propname} is required to create a {ctorInfo.DeclaringType.FullName} object.", enode as IXmlLineInfo);
{
throw new XamlParseException(
String.Format("The Property {0} is required to create a {1} object.", propname, ctorInfo.DeclaringType.FullName),
enode as IXmlLineInfo);
}
if (!enode.SkipProperties.Contains(name)) if (!enode.SkipProperties.Contains(name))
enode.SkipProperties.Add(name); enode.SkipProperties.Add(name);
var value = Context.Values[node]; var value = Context.Values[node];
@ -319,14 +311,11 @@ namespace Xamarin.Forms.Xaml
return array; return array;
} }
static bool IsXaml2009LanguagePrimitive(IElementNode node) static bool IsXaml2009LanguagePrimitive(IElementNode node) => node.NamespaceURI == XamlParser.X2009Uri;
{
return node.NamespaceURI == XamlParser.X2009Uri;
}
static object CreateLanguagePrimitive(Type nodeType, IElementNode node) static object CreateLanguagePrimitive(Type nodeType, IElementNode node)
{ {
object value = null; object value;
if (nodeType == typeof (string)) if (nodeType == typeof (string))
value = String.Empty; value = String.Empty;
else if (nodeType == typeof (Uri)) else if (nodeType == typeof (Uri))
@ -334,95 +323,43 @@ namespace Xamarin.Forms.Xaml
else else
value = Activator.CreateInstance(nodeType); value = Activator.CreateInstance(nodeType);
if (node.CollectionItems.Count == 1 && node.CollectionItems[0] is ValueNode && if ( node.CollectionItems.Count == 1
((ValueNode)node.CollectionItems[0]).Value is string) && node.CollectionItems[0] is ValueNode
{ && ((ValueNode)node.CollectionItems[0]).Value is string valuestring) {
var valuestring = ((ValueNode)node.CollectionItems[0]).Value as string; if (nodeType == typeof(SByte) && sbyte.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out var sbyteval))
return sbyteval;
if (nodeType == typeof(SByte)) { if (nodeType == typeof(Int16) && short.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out var int16val))
sbyte retval; return int16val;
if (sbyte.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval)) if (nodeType == typeof(Int32) && int.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out var int32val))
return retval; return int32val;
} if (nodeType == typeof(Int64) && long.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out var int64val))
if (nodeType == typeof(Int16)) { return int64val;
short retval; if (nodeType == typeof(Byte) && byte.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out var byteval))
if (short.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval)) return byteval;
return retval; if (nodeType == typeof(UInt16) && ushort.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out var uint16val))
} return uint16val;
if (nodeType == typeof(Int32)) { if (nodeType == typeof(UInt32) && uint.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out var uint32val))
int retval; return uint32val;
if (int.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval)) if (nodeType == typeof(UInt64) && ulong.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out var uint64val))
return retval; return uint64val;
} if (nodeType == typeof(Single) && float.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out var singleval))
if (nodeType == typeof(Int64)) { return singleval;
long retval; if (nodeType == typeof(Double) && double.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out var doubleval))
if (long.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval)) return doubleval;
return retval; if (nodeType == typeof(Boolean) && bool.TryParse(valuestring, out var boolval))
} return boolval;
if (nodeType == typeof(Byte)) { if (nodeType == typeof(TimeSpan) && TimeSpan.TryParse(valuestring, CultureInfo.InvariantCulture, out TimeSpan timespanval))
byte retval; return timespanval;
if (byte.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval)) if (nodeType == typeof(char) && char.TryParse(valuestring, out var charval))
return retval; return charval;
}
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 (nodeType == typeof (string)) if (nodeType == typeof (string))
return valuestring; return valuestring;
if (nodeType == typeof (decimal)) if (nodeType == typeof(decimal) && decimal.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out var decimalval))
{ return decimalval;
decimal retval; if (nodeType == typeof(Uri) && Uri.TryCreate(valuestring, UriKind.RelativeOrAbsolute, out Uri urival))
if (decimal.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval)) return urival;
return retval;
}
else if (nodeType == typeof (Uri))
{
Uri retval;
if (Uri.TryCreate(valuestring, UriKind.RelativeOrAbsolute, out retval))
return retval;
}
} }
return value; return value;
} }
} }
} }

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

@ -5,12 +5,11 @@ namespace Xamarin.Forms.Xaml
{ {
class NamescopingVisitor : IXamlNodeVisitor 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) public NamescopingVisitor(HydrationContext context)
=> Values = context.Values; {
}
Dictionary<INode, object> Values { get; set; }
public TreeVisitingMode VisitingMode => TreeVisitingMode.TopDown; public TreeVisitingMode VisitingMode => TreeVisitingMode.TopDown;
public bool StopOnDataTemplate => false; public bool StopOnDataTemplate => false;
@ -19,37 +18,29 @@ namespace Xamarin.Forms.Xaml
public bool SkipChildren(INode node, INode parentNode) => false; public bool SkipChildren(INode node, INode parentNode) => false;
public bool IsResourceDictionary(ElementNode node) => false; public bool IsResourceDictionary(ElementNode node) => false;
public void Visit(ValueNode node, INode parentNode) public void Visit(ValueNode node, INode parentNode) => _scopes[node] = _scopes[parentNode];
=> _scopes[node] = _scopes[parentNode]; public void Visit(MarkupNode 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) public void Visit(ElementNode node, INode parentNode)
=> _scopes[node] = node.Namescope = (parentNode == null || IsDataTemplate(node, parentNode) || IsStyle(node, parentNode) || IsVisualStateGroupList(node)) => _scopes[node] = node.NameScopeRef = (parentNode == null || IsDataTemplate(node, parentNode) || IsStyle(node, parentNode) || IsVisualStateGroupList(node))
? new NameScope() ? new NameScopeRef { NameScope = new NameScope() }
: _scopes[parentNode]; : _scopes[parentNode];
public void Visit(RootNode node, INode parentNode) public void Visit(RootNode node, INode parentNode) => _scopes[node] = node.NameScopeRef = new NameScopeRef { NameScope = new NameScope() };
=> _scopes[node] = node.Namescope = new NameScope();
public void Visit(ListNode node, INode parentNode) => public void Visit(ListNode node, INode parentNode) =>
_scopes[node] = _scopes[parentNode]; _scopes[node] = _scopes[parentNode];
static bool IsDataTemplate(INode node, INode parentNode) static bool IsDataTemplate(INode node, INode parentNode)
{ {
var parentElement = parentNode as IElementNode; if ( parentNode is IElementNode parentElement
if ( parentElement != null && parentElement.Properties.TryGetValue(XmlName._CreateContent, out var createContent)
&& parentElement.Properties.TryGetValue(XmlName._CreateContent, out var createContent) && createContent == node)
&& createContent == node)
return true; return true;
return false; return false;
} }
static bool IsStyle(INode node, INode parentNode) static bool IsStyle(INode node, INode parentNode) => (parentNode as ElementNode)?.XmlType.Name == "Style";
=> (parentNode as ElementNode)?.XmlType.Name == "Style"; static bool IsVisualStateGroupList(ElementNode node) => node?.XmlType.Name == "VisualStateGroup" && node?.Parent is IListNode;
static bool IsVisualStateGroupList(ElementNode node)
=> node?.XmlType.Name == "VisualStateGroup" && node?.Parent is IListNode;
} }
} }

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

@ -1,11 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Xml;
using Xamarin.Forms.Internals; using Xamarin.Forms.Internals;
using Xamarin.Forms.Xaml.Internals;
namespace Xamarin.Forms.Xaml namespace Xamarin.Forms.Xaml
{ {
@ -32,7 +27,7 @@ namespace Xamarin.Forms.Xaml
return; return;
try { try {
((IElementNode)parentNode).Namescope.RegisterName((string)node.Value, Values[parentNode]); ((IElementNode)parentNode).NameScopeRef.NameScope.RegisterName((string)node.Value, Values[parentNode]);
} }
catch (ArgumentException ae) { catch (ArgumentException ae) {
if (ae.ParamName != "name") if (ae.ParamName != "name")
@ -73,12 +68,6 @@ namespace Xamarin.Forms.Xaml
} }
static bool IsXNameProperty(ValueNode node, INode parentNode) static bool IsXNameProperty(ValueNode node, INode parentNode)
{ => parentNode is IElementNode parentElement && parentElement.Properties.TryGetValue(XmlName.xName, out INode xNameNode) && xNameNode == node;
var parentElement = parentNode as IElementNode;
INode xNameNode;
if (parentElement != null && parentElement.Properties.TryGetValue(XmlName.xName, out xNameNode) && xNameNode == node)
return true;
return false;
}
} }
} }

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

@ -24,7 +24,7 @@ namespace Xamarin.Forms.Xaml
{ {
Dictionary<XmlName, INode> Properties { get; } Dictionary<XmlName, INode> Properties { get; }
List<XmlName> SkipProperties { get; } List<XmlName> SkipProperties { get; }
INameScope Namescope { get; } NameScopeRef NameScopeRef { get; }
XmlType XmlType { get; } XmlType XmlType { get; }
string NamespaceURI { get; } string NamespaceURI { get; }
} }
@ -34,6 +34,11 @@ namespace Xamarin.Forms.Xaml
List<INode> CollectionItems { get; } List<INode> CollectionItems { get; }
} }
class NameScopeRef
{
public INameScope NameScope { get; set; }
}
[DebuggerDisplay("{NamespaceUri}:{Name}")] [DebuggerDisplay("{NamespaceUri}:{Name}")]
class XmlType class XmlType
{ {
@ -112,6 +117,7 @@ namespace Xamarin.Forms.Xaml
}; };
} }
[DebuggerDisplay("{XmlType.Name}")] [DebuggerDisplay("{XmlType.Name}")]
class ElementNode : BaseNode, IValueNode, IElementNode class ElementNode : BaseNode, IValueNode, IElementNode
{ {
@ -131,7 +137,7 @@ namespace Xamarin.Forms.Xaml
public List<INode> CollectionItems { get; } public List<INode> CollectionItems { get; }
public XmlType XmlType { get; } public XmlType XmlType { get; }
public string NamespaceURI { get; } public string NamespaceURI { get; }
public INameScope Namescope { get; set; } public NameScopeRef NameScopeRef { get; set; }
public override void Accept(IXamlNodeVisitor visitor, INode parentNode) public override void Accept(IXamlNodeVisitor visitor, INode parentNode)
{ {
@ -152,10 +158,8 @@ namespace Xamarin.Forms.Xaml
bool IsDataTemplate(INode parentNode) bool IsDataTemplate(INode parentNode)
{ {
var parentElement = parentNode as IElementNode; if (parentNode is IElementNode parentElement &&
INode createContent; parentElement.Properties.TryGetValue(XmlName._CreateContent, out INode createContent) &&
if (parentElement != null &&
parentElement.Properties.TryGetValue(XmlName._CreateContent, out createContent) &&
createContent == this) createContent == this)
return true; return true;
return false; return false;

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

@ -74,16 +74,9 @@ namespace Xamarin.Forms.Xaml.Internals
set { services[typeof (IValueConverterProvider)] = value; } set { services[typeof (IValueConverterProvider)] = value; }
} }
public object GetService(Type serviceType) public object GetService(Type serviceType) => services.TryGetValue(serviceType, out var service) ? service : null;
{
object service;
return services.TryGetValue(serviceType, out service) ? service : null;
}
public void Add(Type type, object service) public void Add(Type type, object service) => services.Add(type, service);
{
services.Add(type, service);
}
} }
class XamlValueTargetProvider : IProvideParentValues, IProvideValueTarget class XamlValueTargetProvider : IProvideParentValues, IProvideValueTarget
@ -104,21 +97,16 @@ namespace Xamarin.Forms.Xaml.Internals
IEnumerable<object> IProvideParentValues.ParentObjects IEnumerable<object> IProvideParentValues.ParentObjects
{ {
get get {
{
if (Node == null || Context == null) if (Node == null || Context == null)
yield break; yield break;
var n = Node; var n = Node;
object obj = null;
var context = Context; var context = Context;
while (n.Parent != null && context != null) while (n.Parent != null && context != null) {
{ if (n.Parent is IElementNode) {
if (n.Parent is IElementNode) if (context.Values.TryGetValue(n.Parent, out var obj))
{
if (context.Values.TryGetValue(n.Parent, out obj))
yield return obj; yield return obj;
else else {
{
context = context.ParentContext; context = context.ParentContext;
continue; continue;
} }
@ -159,14 +147,9 @@ namespace Xamarin.Forms.Xaml.Internals
this.scope = scope; this.scope = scope;
} }
IEnumerable<object> IProvideParentValues.ParentObjects IEnumerable<object> IProvideParentValues.ParentObjects => objectAndParents;
=> objectAndParents; object IProvideValueTarget.TargetObject => objectAndParents[0];
object IProvideValueTarget.TargetProperty => targetProperty;
object IProvideValueTarget.TargetObject
=> objectAndParents[0];
object IProvideValueTarget.TargetProperty
=> targetProperty;
public object FindByName(string name) public object FindByName(string name)
{ {
@ -277,15 +260,14 @@ namespace Xamarin.Forms.Xaml.Internals
class ReferenceProvider : IReferenceProvider class ReferenceProvider : IReferenceProvider
{ {
readonly INode _node; readonly INode _node;
internal ReferenceProvider(INode node) internal ReferenceProvider(INode node) => _node = node;
=> _node = node;
public object FindByName(string name) public object FindByName(string name)
{ {
var n = _node; var n = _node;
object value = null;
while (n != 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; return value;
n = n.Parent; n = n.Parent;
} }
@ -304,27 +286,16 @@ namespace Xamarin.Forms.Xaml.Internals
{ {
readonly Dictionary<string, string> namespaces = new Dictionary<string, string>(); readonly Dictionary<string, string> namespaces = new Dictionary<string, string>();
public IDictionary<string, string> GetNamespacesInScope(XmlNamespaceScope scope) public IDictionary<string, string> GetNamespacesInScope(XmlNamespaceScope scope) => throw new NotImplementedException();
{
throw new NotImplementedException();
}
public string LookupNamespace(string prefix) public string LookupNamespace(string prefix)
{ {
string result; if (namespaces.TryGetValue(prefix, out var result))
if (namespaces.TryGetValue(prefix, out result))
return result; return result;
return null; return null;
} }
public string LookupPrefix(string namespaceName) public string LookupPrefix(string namespaceName) => throw new NotImplementedException();
{ public void Add(string prefix, string ns) => namespaces.Add(prefix, ns);
throw new NotImplementedException();
}
public void Add(string prefix, string ns)
{
namespaces.Add(prefix, ns);
}
} }
} }