Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[X] Pass the parent scopes to DataContext #8345

Merged
merged 2 commits into from
Jan 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 32 additions & 5 deletions src/Controls/src/Build.Tasks/NodeILExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,32 @@ static IEnumerable<Instruction> PushTargetProperty(ILContext context, FieldRefer
yield break;
}

static IEnumerable<Instruction> PushNamescopes(INode node, ILContext context, ModuleDefinition module)
{
var scopes = new List<VariableDefinition>();
do
{

if (context.Scopes.TryGetValue(node, out var scope))
scopes.Add(scope.Item1);
node = node.Parent;
} while (node != null);


yield return Instruction.Create(OpCodes.Ldc_I4, scopes.Count);
yield return Instruction.Create(OpCodes.Newarr, module.ImportReference(context.Cache, ("Microsoft.Maui.Controls", "Microsoft.Maui.Controls.Internals", "NameScope")));

var i = 0;
foreach (var scope in scopes)
{
yield return Instruction.Create(OpCodes.Dup);
yield return Instruction.Create(OpCodes.Ldc_I4, i);
yield return Instruction.Create(OpCodes.Ldloc, scope);
yield return Instruction.Create(OpCodes.Stelem_Ref);
i++;
}
}

public static IEnumerable<Instruction> PushServiceProvider(this INode node, ILContext context, FieldReference bpRef = null, PropertyReference propertyRef = null, TypeReference declaringTypeReference = null)
{
var module = context.Body.Method.Module;
Expand Down Expand Up @@ -589,12 +615,13 @@ public static IEnumerable<Instruction> PushServiceProvider(this INode node, ILCo
foreach (var instruction in PushTargetProperty(context, bpRef, propertyRef, declaringTypeReference, module))
yield return instruction;

if (context.Scopes.TryGetValue(node, out var scope))
yield return Create(Ldloc, scope.Item1);
else
yield return Create(Ldnull);
foreach (var instruction in PushNamescopes(node, context, module))
yield return instruction;

yield return Create(Newobj, module.ImportCtorReference(context.Cache, ("Microsoft.Maui.Controls.Xaml", "Microsoft.Maui.Controls.Xaml.Internals", "SimpleValueTargetProvider"), paramCount: 3));
yield return Create(Ldc_I4_0); //don't ask
yield return Create(Newobj, module.ImportCtorReference(context.Cache,
("Microsoft.Maui.Controls.Xaml", "Microsoft.Maui.Controls.Xaml.Internals", "SimpleValueTargetProvider"), paramCount: 4));

//store the provider so we can register it again with a different key
yield return Create(Dup);
var refProvider = new VariableDefinition(module.ImportReference(context.Cache, ("mscorlib", "System", "Object")));
Expand Down
30 changes: 27 additions & 3 deletions src/Controls/src/Build.Tasks/SetPropertiesVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1595,6 +1595,33 @@ static void SetDataTemplate(IElementNode parentNode, ElementNode node, ILContext
Root = root,
XamlFilePath = parentContext.XamlFilePath,
};

//Instanciate nested class
var parentIl = parentContext.IL;
parentIl.Emit(OpCodes.Newobj, ctor);

//Copy the scopes over for x:Reference to work
//the scopes will be copied to fields of the anon type, the templateIL will copy them as VariableDef and populate context.Scopes
var i = 0;
foreach (var kvp in parentContext.Scopes)
{
//On the parentIL, copy the scope to a field
parentIl.Emit(OpCodes.Dup); //Duplicate the nestedclass instance
parentIl.Append(kvp.Value.Item1.LoadAs(parentContext.Cache, module.ImportReference(parentContext.Cache, ("Microsoft.Maui.Controls", "Microsoft.Maui.Controls.Internals", "NameScope")), module));
var fieldDefScope = new FieldDefinition($"_scope{i++}", FieldAttributes.Assembly, module.ImportReference(parentContext.Cache, ("Microsoft.Maui.Controls", "Microsoft.Maui.Controls.Internals", "NameScope")));
anonType.Fields.Add(fieldDefScope);
parentIl.Emit(OpCodes.Stfld, fieldDefScope);

//On the templateIL, copy the field to a var, and populate the Scopes
templateIl.Emit(OpCodes.Ldarg_0);
var varDefScope = new VariableDefinition(module.ImportReference(parentContext.Cache, ("Microsoft.Maui.Controls", "Microsoft.Maui.Controls.Internals", "NameScope")));
loadTemplate.Body.Variables.Add(varDefScope);
templateIl.Emit(OpCodes.Ldfld, fieldDefScope);
templateIl.Emit(OpCodes.Stloc, varDefScope);
templateContext.Scopes[kvp.Key] = new Tuple<VariableDefinition, IList<string>>(varDefScope, kvp.Value.Item2);
}

//inflate the template
node.Accept(new CreateObjectVisitor(templateContext), null);
node.Accept(new SetNamescopesAndRegisterNamesVisitor(templateContext), null);
node.Accept(new SetFieldVisitor(templateContext), null);
Expand All @@ -1604,9 +1631,6 @@ static void SetDataTemplate(IElementNode parentNode, ElementNode node, ILContext
templateIl.Append(templateContext.Variables[node].LoadAs(parentContext.Cache, module.TypeSystem.Object, module));
templateIl.Emit(OpCodes.Ret);

//Instanciate nested class
var parentIl = parentContext.IL;
parentIl.Emit(OpCodes.Newobj, ctor);

//Copy required local vars
parentIl.Emit(OpCodes.Dup); //Duplicate the nestedclass instance
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
#nullable enable
~Microsoft.Maui.Controls.Xaml.Internals.SimpleValueTargetProvider.SimpleValueTargetProvider(object[] objectAndParents, object targetProperty, Microsoft.Maui.Controls.Internals.INameScope[] scopes, bool notused) -> void
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
#nullable enable
~Microsoft.Maui.Controls.Xaml.Internals.SimpleValueTargetProvider.SimpleValueTargetProvider(object[] objectAndParents, object targetProperty, Microsoft.Maui.Controls.Internals.INameScope[] scopes, bool notused) -> void
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
#nullable enable
~Microsoft.Maui.Controls.Xaml.Internals.SimpleValueTargetProvider.SimpleValueTargetProvider(object[] objectAndParents, object targetProperty, Microsoft.Maui.Controls.Internals.INameScope[] scopes, bool notused) -> void
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
#nullable enable
~Microsoft.Maui.Controls.Xaml.Internals.SimpleValueTargetProvider.SimpleValueTargetProvider(object[] objectAndParents, object targetProperty, Microsoft.Maui.Controls.Internals.INameScope[] scopes, bool notused) -> void
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
#nullable enable
~Microsoft.Maui.Controls.Xaml.Internals.SimpleValueTargetProvider.SimpleValueTargetProvider(object[] objectAndParents, object targetProperty, Microsoft.Maui.Controls.Internals.INameScope[] scopes, bool notused) -> void
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
#nullable enable
~Microsoft.Maui.Controls.Xaml.Internals.SimpleValueTargetProvider.SimpleValueTargetProvider(object[] objectAndParents, object targetProperty, Microsoft.Maui.Controls.Internals.INameScope[] scopes, bool notused) -> void
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
#nullable enable
~Microsoft.Maui.Controls.Xaml.Internals.SimpleValueTargetProvider.SimpleValueTargetProvider(object[] objectAndParents, object targetProperty, Microsoft.Maui.Controls.Internals.INameScope[] scopes, bool notused) -> void
16 changes: 12 additions & 4 deletions src/Controls/src/Xaml/XamlServiceProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,15 @@ public class SimpleValueTargetProvider : IProvideParentValues, IProvideValueTarg
{
readonly object[] objectAndParents;
readonly object targetProperty;
readonly INameScope scope;
readonly INameScope[] scopes;

[Obsolete("Use the other ctor")]
public SimpleValueTargetProvider(object[] objectAndParents, object targetProperty, INameScope scope)
: this(objectAndParents, targetProperty, new INameScope[] { scope }, false)
{
}

public SimpleValueTargetProvider(object[] objectAndParents, object targetProperty, INameScope[] scopes, bool notused)
{
if (objectAndParents == null)
throw new ArgumentNullException(nameof(objectAndParents));
Expand All @@ -123,7 +129,7 @@ public SimpleValueTargetProvider(object[] objectAndParents, object targetPropert

this.objectAndParents = objectAndParents;
this.targetProperty = targetProperty;
this.scope = scope;
this.scopes = scopes;
}

IEnumerable<object> IProvideParentValues.ParentObjects => objectAndParents;
Expand All @@ -133,8 +139,10 @@ public SimpleValueTargetProvider(object[] objectAndParents, object targetPropert
public object FindByName(string name)
{
object value;
if ((value = scope?.FindByName(name)) != null)
return value;
if (scopes!=null)
foreach(var scope in scopes)
if ((value = scope?.FindByName(name)) != null)
return value;

for (var i = 0; i < objectAndParents.Length; i++)
{
Expand Down
37 changes: 18 additions & 19 deletions src/Controls/tests/Xaml.UnitTests/Issues/Maui7744.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,28 @@
using Microsoft.Maui.Devices;
using NUnit.Framework;

namespace Microsoft.Maui.Controls.Xaml.UnitTests
namespace Microsoft.Maui.Controls.Xaml.UnitTests;

public partial class Maui7744 : ContentPage
{
public partial class Maui7744 : ContentPage
public Maui7744() => InitializeComponent();
public Maui7744(bool useCompiledXaml)
{
public Maui7744() => InitializeComponent();
public Maui7744(bool useCompiledXaml)
{
//this stub will be replaced at compile time
}
//this stub will be replaced at compile time
}

[TestFixture]
class Test
{
[SetUp] public void Setup() => AppInfo.SetCurrent(new MockAppInfo());
[TearDown] public void TearDown() => AppInfo.SetCurrent(null);
[TestFixture]
class Test
{
[SetUp] public void Setup() => AppInfo.SetCurrent(new MockAppInfo());
[TearDown] public void TearDown() => AppInfo.SetCurrent(null);

[Test]
public void ConvertersAreExecutedWhileApplyingSetter([Values(false, true)] bool useCompiledXaml)
{
var page = new Maui7744(useCompiledXaml);
Assert.That(page.border0.StrokeShape, Is.TypeOf<RoundRectangle>());
Assert.That(page.border1.StrokeShape, Is.TypeOf<RoundRectangle>());
}
[Test]
public void ConvertersAreExecutedWhileApplyingSetter([Values(false, true)] bool useCompiledXaml)
{
var page = new Maui7744(useCompiledXaml);
Assert.That(page.border0.StrokeShape, Is.TypeOf<RoundRectangle>());
Assert.That(page.border1.StrokeShape, Is.TypeOf<RoundRectangle>());
}
}
}
18 changes: 18 additions & 0 deletions src/Controls/tests/Xaml.UnitTests/Issues/Maui8149.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Microsoft.Maui.Controls.Xaml.UnitTests"
x:Class="Microsoft.Maui.Controls.Xaml.UnitTests.Maui8149"
x:Name="Self">

<local:Maui8149View>
<local:Maui8149View.ItemTemplate>
<DataTemplate>
<local:Maui8149Item>
<Label x:Name="label" Text="{Binding Source={x:Reference Self}}"/>
</local:Maui8149Item>
</DataTemplate>
</local:Maui8149View.ItemTemplate>
</local:Maui8149View>

</ContentView>
35 changes: 35 additions & 0 deletions src/Controls/tests/Xaml.UnitTests/Issues/Maui8149.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using Microsoft.Maui.ApplicationModel;
using Microsoft.Maui.Controls.Core.UnitTests;
using Microsoft.Maui.Controls.Shapes;
using Microsoft.Maui.Devices;
using NUnit.Framework;

namespace Microsoft.Maui.Controls.Xaml.UnitTests;

public partial class Maui8149 : ContentView
{

public Maui8149() => InitializeComponent();

public Maui8149(bool useCompiledXaml)
{
//this stub will be replaced at compile time
}

[TestFixture]
class Test
{
[SetUp] public void Setup() => AppInfo.SetCurrent(new MockAppInfo());
[TearDown] public void TearDown() => AppInfo.SetCurrent(null);

[Test]
public void NamescopeWithXamlC([Values(false, true)] bool useCompiledXaml)
{
if (useCompiledXaml)
MockCompiler.Compile(typeof(Maui8149));

var page = new Maui8149(useCompiledXaml);
Assert.That((page.Content as Maui8149View).Text, Is.EqualTo("Microsoft.Maui.Controls.Xaml.UnitTests.Maui8149"));
}
}
}
16 changes: 16 additions & 0 deletions src/Controls/tests/Xaml.UnitTests/Issues/Maui8149Item.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Microsoft.Maui.Controls.Xaml.UnitTests"
x:Class="Microsoft.Maui.Controls.Xaml.UnitTests.Maui8149Item"
x:Name="Self">

<!--
Note the 'x:Name="Self"
In this *minimal* repro, all Content is provided by the DataTemplate
-->

</ContentView>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace Microsoft.Maui.Controls.Xaml.UnitTests;

public partial class Maui8149Item : ContentView
{
public Maui8149Item()
{
InitializeComponent();
}
}
10 changes: 10 additions & 0 deletions src/Controls/tests/Xaml.UnitTests/Issues/Maui8149View.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Microsoft.Maui.Controls.Xaml.UnitTests"
x:Class="Microsoft.Maui.Controls.Xaml.UnitTests.Maui8149View"
x:Name="Self">



</ContentView>
20 changes: 20 additions & 0 deletions src/Controls/tests/Xaml.UnitTests/Issues/Maui8149View.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
namespace Microsoft.Maui.Controls.Xaml.UnitTests;

public partial class Maui8149View : ContentView
{
private Controls.DataTemplate _itemTemplate;
public string Text { get; set; }
public Microsoft.Maui.Controls.DataTemplate ItemTemplate {
get => _itemTemplate;
set {
_itemTemplate = value;
Content = _itemTemplate.CreateContent() as View;
Text = ((Content as Maui8149Item).Content as Label).Text;
}
}

public Maui8149View()
{
InitializeComponent();
}
}