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] x:Type and x:Static don't expand to Extension #15903

Closed
wants to merge 2 commits into from
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public IEnumerable<Instruction> ProvideValue(IElementNode node, ModuleDefinition
var typename = member.Substring(0, dotIdx);
var membername = member.Substring(dotIdx + 1);

var typeRef = module.ImportReference(XmlTypeExtensions.GetTypeReference(context.Cache, typename, module, node as BaseNode));
var typeRef = module.ImportReference(XmlTypeExtensions.GetTypeReference(context.Cache, typename, false, module, node as BaseNode));
var fieldRef = GetFieldReference(context.Cache, typeRef, membername, module);
var propertyDef = GetPropertyDefinition(context.Cache, typeRef, membername, module);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public IEnumerable<Instruction> ProvideValue(IElementNode node, ModuleDefinition
node.CollectionItems.Clear();
}

var typeref = module.ImportReference(XmlTypeExtensions.GetTypeReference(context.Cache, valueNode.Value as string, module, node as BaseNode));
var typeref = module.ImportReference(XmlTypeExtensions.GetTypeReference(context.Cache, valueNode.Value as string, false, module, node as BaseNode));

context.TypeExtensions[node] = typeref ?? throw new BuildException(BuildExceptionCode.TypeResolution, node as IXmlLineInfo, null, valueNode.Value);

Expand Down
16 changes: 12 additions & 4 deletions src/Controls/src/Build.Tasks/XmlTypeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,10 @@ static IList<XmlnsDefinitionAttribute> GatherXmlnsDefinitionAttributes(ModuleDef

return xmlnsDefinitions;
}

public static TypeReference GetTypeReference(XamlCache cache, string xmlType, ModuleDefinition module, BaseNode node)
=> GetTypeReference(cache, xmlType, true, module, node);

public static TypeReference GetTypeReference(XamlCache cache, string xmlType, bool tryExtensionFirst, ModuleDefinition module, BaseNode node)
{
var split = xmlType.Split(':');
if (split.Length > 2)
Expand All @@ -68,7 +70,7 @@ public static TypeReference GetTypeReference(XamlCache cache, string xmlType, Mo
name = split[0];
}
var namespaceuri = node.NamespaceResolver.LookupNamespace(prefix) ?? "";
return GetTypeReference(new XmlType(namespaceuri, name, null), cache, module, node as IXmlLineInfo);
return GetTypeReference(new XmlType(namespaceuri, name, null), tryExtensionFirst, cache, module, node as IXmlLineInfo);
}

public static TypeReference GetTypeReference(XamlCache cache, string namespaceURI, string typename, ModuleDefinition module, IXmlLineInfo xmlInfo)
Expand All @@ -77,6 +79,9 @@ public static TypeReference GetTypeReference(XamlCache cache, string namespaceUR
}

public static bool TryGetTypeReference(this XmlType xmlType, XamlCache cache, ModuleDefinition module, IXmlLineInfo xmlInfo, out TypeReference typeReference)
=> TryGetTypeReference(xmlType, true, cache, module, xmlInfo, out typeReference);

public static bool TryGetTypeReference(this XmlType xmlType, bool tryExtensionFirst, XamlCache cache, ModuleDefinition module, IXmlLineInfo xmlInfo, out TypeReference typeReference)
{
IList<XmlnsDefinitionAttribute> xmlnsDefinitions = cache.GetXmlsDefinitions(module, GatherXmlnsDefinitionAttributes);

Expand All @@ -89,7 +94,7 @@ public static bool TryGetTypeReference(this XmlType xmlType, XamlCache cache, Mo
if (type is not null && type.IsPublicOrVisibleInternal(module))
return type;
return null;
});
}, tryExtensionFirst);

if (type != null && typeArguments != null && type.HasGenericParameters)
type = module.ImportReference(type).MakeGenericInstanceType(typeArguments.Select(x => x.GetTypeReference(cache, module, xmlInfo)).ToArray());
Expand All @@ -98,8 +103,11 @@ public static bool TryGetTypeReference(this XmlType xmlType, XamlCache cache, Mo
}

public static TypeReference GetTypeReference(this XmlType xmlType, XamlCache cache, ModuleDefinition module, IXmlLineInfo xmlInfo)
=> GetTypeReference(xmlType, true, cache, module, xmlInfo);

public static TypeReference GetTypeReference(this XmlType xmlType, bool tryExtensionFirst, XamlCache cache, ModuleDefinition module, IXmlLineInfo xmlInfo)
{
if (TryGetTypeReference(xmlType, cache, module, xmlInfo, out TypeReference typeReference))
if (TryGetTypeReference(xmlType, tryExtensionFirst, cache, module, xmlInfo, out TypeReference typeReference))
return typeReference;

throw new BuildException(BuildExceptionCode.TypeResolution, xmlInfo, null, $"{xmlType.NamespaceUri}:{xmlType.Name}");
Expand Down
1 change: 1 addition & 0 deletions src/Controls/src/Core/IXamlTypeResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ namespace Microsoft.Maui.Controls.Xaml
public interface IXamlTypeResolver
{
Type Resolve(string qualifiedTypeName, IServiceProvider serviceProvider = null);
Type Resolve(string qualifiedTypeName, bool tryExtensionFirst, IServiceProvider serviceProvider = null);
bool TryResolve(string qualifiedTypeName, out Type type);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ static readonly Microsoft.Maui.Controls.PointerGestureRecognizer.PointerReleased
static readonly Microsoft.Maui.Controls.PointerGestureRecognizer.PointerReleasedCommandProperty -> Microsoft.Maui.Controls.BindableProperty!
~Microsoft.Maui.Controls.Accelerator.Key.get -> string
*REMOVED*override Microsoft.Maui.Controls.RefreshView.MeasureOverride(double widthConstraint, double heightConstraint) -> Microsoft.Maui.Graphics.Size
~Microsoft.Maui.Controls.Xaml.IXamlTypeResolver.Resolve(string qualifiedTypeName, bool tryExtensionFirst, System.IServiceProvider serviceProvider = null) -> System.Type
Microsoft.Maui.Controls.Border.~Border() -> void
Microsoft.Maui.Controls.IWindowCreator
Microsoft.Maui.Controls.IWindowCreator.CreateWindow(Microsoft.Maui.Controls.Application! app, Microsoft.Maui.IActivationState? activationState) -> Microsoft.Maui.Controls.Window!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ static Microsoft.Maui.Controls.Shapes.Matrix.operator ==(Microsoft.Maui.Controls
override Microsoft.Maui.Controls.View.ChangeVisualState() -> void
Microsoft.Maui.Controls.Handlers.BoxViewHandler
Microsoft.Maui.Controls.Handlers.BoxViewHandler.BoxViewHandler() -> void
~Microsoft.Maui.Controls.Xaml.IXamlTypeResolver.Resolve(string qualifiedTypeName, bool tryExtensionFirst, System.IServiceProvider serviceProvider = null) -> System.Type
~override Microsoft.Maui.Controls.Handlers.Items.CarouselViewController.DetermineCellReuseId(Foundation.NSIndexPath indexPath) -> string
~override Microsoft.Maui.Controls.ImageButton.OnPropertyChanged(string propertyName = null) -> void
~override Microsoft.Maui.Controls.Handlers.Compatibility.PhoneFlyoutPageRenderer.ViewWillTransitionToSize(CoreGraphics.CGSize toSize, UIKit.IUIViewControllerTransitionCoordinator coordinator) -> void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ static readonly Microsoft.Maui.Controls.PointerGestureRecognizer.PointerReleased
static readonly Microsoft.Maui.Controls.PointerGestureRecognizer.PointerReleasedCommandProperty -> Microsoft.Maui.Controls.BindableProperty!
~Microsoft.Maui.Controls.Accelerator.Key.get -> string
*REMOVED*override Microsoft.Maui.Controls.RefreshView.MeasureOverride(double widthConstraint, double heightConstraint) -> Microsoft.Maui.Graphics.Size
~Microsoft.Maui.Controls.Xaml.IXamlTypeResolver.Resolve(string qualifiedTypeName, bool tryExtensionFirst, System.IServiceProvider serviceProvider = null) -> System.Type
Microsoft.Maui.Controls.Border.~Border() -> void
Microsoft.Maui.Controls.IWindowCreator
Microsoft.Maui.Controls.IWindowCreator.CreateWindow(Microsoft.Maui.Controls.Application! app, Microsoft.Maui.IActivationState? activationState) -> Microsoft.Maui.Controls.Window!
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#nullable enable
#nullable enable
~Microsoft.Maui.Controls.Xaml.IXamlTypeResolver.Resolve(string qualifiedTypeName, bool tryExtensionFirst, System.IServiceProvider serviceProvider = null) -> System.Type
*REMOVED*~Microsoft.Maui.Controls.Accelerator.Modifiers.set -> void
*REMOVED*~Microsoft.Maui.Controls.Accelerator.Keys.set -> void
Microsoft.Maui.Controls.PointerGestureRecognizer.PointerPressed -> System.EventHandler<Microsoft.Maui.Controls.PointerEventArgs!>?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ static readonly Microsoft.Maui.Controls.PointerGestureRecognizer.PointerReleased
static readonly Microsoft.Maui.Controls.PointerGestureRecognizer.PointerReleasedCommandProperty -> Microsoft.Maui.Controls.BindableProperty!
~Microsoft.Maui.Controls.Accelerator.Key.get -> string
*REMOVED*override Microsoft.Maui.Controls.RefreshView.MeasureOverride(double widthConstraint, double heightConstraint) -> Microsoft.Maui.Graphics.Size
~Microsoft.Maui.Controls.Xaml.IXamlTypeResolver.Resolve(string qualifiedTypeName, bool tryExtensionFirst, System.IServiceProvider serviceProvider = null) -> System.Type
Microsoft.Maui.Controls.Border.~Border() -> void
Microsoft.Maui.Controls.InputView.IsTextPredictionEnabled.get -> bool
Microsoft.Maui.Controls.InputView.IsTextPredictionEnabled.set -> void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ static readonly Microsoft.Maui.Controls.PointerGestureRecognizer.PointerReleased
static readonly Microsoft.Maui.Controls.PointerGestureRecognizer.PointerReleasedCommandProperty -> Microsoft.Maui.Controls.BindableProperty!
~Microsoft.Maui.Controls.Accelerator.Key.get -> string
*REMOVED*override Microsoft.Maui.Controls.RefreshView.MeasureOverride(double widthConstraint, double heightConstraint) -> Microsoft.Maui.Graphics.Size
~Microsoft.Maui.Controls.Xaml.IXamlTypeResolver.Resolve(string qualifiedTypeName, bool tryExtensionFirst, System.IServiceProvider serviceProvider = null) -> System.Type
Microsoft.Maui.Controls.Border.~Border() -> void
Microsoft.Maui.Controls.InputView.IsTextPredictionEnabled.get -> bool
Microsoft.Maui.Controls.InputView.IsTextPredictionEnabled.set -> void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ static readonly Microsoft.Maui.Controls.PointerGestureRecognizer.PointerReleased
Microsoft.Maui.Controls.Element.ClearLogicalChildren() -> void
~Microsoft.Maui.Controls.Accelerator.Key.get -> string
*REMOVED*override Microsoft.Maui.Controls.RefreshView.MeasureOverride(double widthConstraint, double heightConstraint) -> Microsoft.Maui.Graphics.Size
~Microsoft.Maui.Controls.Xaml.IXamlTypeResolver.Resolve(string qualifiedTypeName, bool tryExtensionFirst, System.IServiceProvider serviceProvider = null) -> System.Type
Microsoft.Maui.Controls.Border.~Border() -> void
Microsoft.Maui.Controls.InputView.IsTextPredictionEnabled.get -> bool
Microsoft.Maui.Controls.InputView.IsTextPredictionEnabled.set -> void
Expand Down
2 changes: 1 addition & 1 deletion src/Controls/src/Xaml/ApplyPropertiesVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ static bool GetRealNameAndType(ref Type elementType, string namespaceURI, ref st
var typename = localname.Substring(0, dotIdx);
localname = localname.Substring(dotIdx + 1);
XamlParseException xpe;
elementType = XamlParser.GetElementType(new XmlType(namespaceURI, typename, null), lineInfo,
elementType = XamlParser.GetElementType(new XmlType(namespaceURI, typename, null), true, lineInfo,
rootElement.GetType().Assembly, out xpe);

if (xpe != null)
Expand Down
2 changes: 1 addition & 1 deletion src/Controls/src/Xaml/CreateValuesVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public void Visit(ElementNode node, INode parentNode)
{
object value = null;

var type = XamlParser.GetElementType(node.XmlType, node, Context.RootElement?.GetType().Assembly,
var type = XamlParser.GetElementType(node.XmlType, true, node, Context.RootElement?.GetType().Assembly,
out XamlParseException xpe);
if (xpe != null)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public object ProvideValue(IServiceProvider serviceProvider)
var typename = Member.Substring(0, dotIdx);
var membername = Member.Substring(dotIdx + 1);

var type = typeResolver.Resolve(typename, serviceProvider);
var type = typeResolver.Resolve(typename, tryExtensionFirst: false, serviceProvider: serviceProvider);

var pinfo = type.GetRuntimeProperties().FirstOrDefault(pi => pi.Name == membername && pi.GetMethod.IsStatic);
if (pinfo != null)
Expand Down
2 changes: 1 addition & 1 deletion src/Controls/src/Xaml/MarkupExtensions/TypeExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public Type ProvideValue(IServiceProvider serviceProvider)
throw new XamlParseException("TypeName isn't set.", li);
}

return typeResolver.Resolve(TypeName, serviceProvider);
return typeResolver.Resolve(TypeName, tryExtensionFirst: false, serviceProvider: serviceProvider);
}

object IMarkupExtension.ProvideValue(IServiceProvider serviceProvider)
Expand Down
6 changes: 3 additions & 3 deletions src/Controls/src/Xaml/XamlParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,7 @@ static void GatherXmlnsDefinitionAttributes()
}
}

public static Type GetElementType(XmlType xmlType, IXmlLineInfo xmlInfo, Assembly currentAssembly,
public static Type GetElementType(XmlType xmlType, bool tryExtensionFirst, IXmlLineInfo xmlInfo, Assembly currentAssembly,
out XamlParseException exception)
{
bool hasRetriedNsSearch = false;
Expand All @@ -374,7 +374,7 @@ public static Type GetElementType(XmlType xmlType, IXmlLineInfo xmlInfo, Assembl
if (t is not null && t.IsPublicOrVisibleInternal(currentAssembly))
return t;
return null;
});
}, tryExtensionFirst);

var typeArguments = xmlType.TypeArguments;
exception = null;
Expand All @@ -398,7 +398,7 @@ public static Type GetElementType(XmlType xmlType, IXmlLineInfo xmlInfo, Assembl
XamlParseException innerexception = null;
var args = typeArguments.Select(delegate (XmlType xmltype)
{
var t = GetElementType(xmltype, xmlInfo, currentAssembly, out XamlParseException xpe);
var t = GetElementType(xmltype, true, xmlInfo, currentAssembly, out XamlParseException xpe);
if (xpe != null)
{
innerexception = xpe;
Expand Down
15 changes: 9 additions & 6 deletions src/Controls/src/Xaml/XamlServiceProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -177,27 +177,30 @@ internal XamlTypeResolver(IXmlNamespaceResolver namespaceResolver, GetTypeFromXm
}

Type IXamlTypeResolver.Resolve(string qualifiedTypeName, IServiceProvider serviceProvider)
=> ((IXamlTypeResolver)this).Resolve(qualifiedTypeName, true, serviceProvider);

Type IXamlTypeResolver.Resolve(string qualifiedTypeName, bool tryExtensionFirst, IServiceProvider serviceProvider)
{
var type = Resolve(qualifiedTypeName, serviceProvider, out XamlParseException e);
var type = Resolve(qualifiedTypeName, tryExtensionFirst, serviceProvider, out XamlParseException e);
if (e != null)
throw e;
return type;
}

bool IXamlTypeResolver.TryResolve(string qualifiedTypeName, out Type type)
{
type = Resolve(qualifiedTypeName, null, out XamlParseException exception);
type = Resolve(qualifiedTypeName, true, null, out XamlParseException exception);
return exception == null;
}

internal bool TryResolve(XmlType xmlType, out Type type)
{
XamlParseException exception;
type = getTypeFromXmlName(xmlType, null, currentAssembly, out exception);
type = getTypeFromXmlName(xmlType, true, null, currentAssembly, out exception);
return exception == null;
}

Type Resolve(string qualifiedTypeName, IServiceProvider serviceProvider, out XamlParseException exception)
Type Resolve(string qualifiedTypeName, bool tryExtensionFirst, IServiceProvider serviceProvider, out XamlParseException exception)
{
exception = null;
var split = qualifiedTypeName.Split(':');
Expand Down Expand Up @@ -230,10 +233,10 @@ Type Resolve(string qualifiedTypeName, IServiceProvider serviceProvider, out Xam
return null;
}

return getTypeFromXmlName(new XmlType(namespaceuri, name, null), xmlLineInfo, currentAssembly, out exception);
return getTypeFromXmlName(new XmlType(namespaceuri, name, null), tryExtensionFirst, xmlLineInfo, currentAssembly, out exception);
}

internal delegate Type GetTypeFromXmlName(XmlType xmlType, IXmlLineInfo xmlInfo, Assembly currentAssembly, out XamlParseException exception);
internal delegate Type GetTypeFromXmlName(XmlType xmlType, bool tryExtensionFirst, IXmlLineInfo xmlInfo, Assembly currentAssembly, out XamlParseException exception);
}

class XamlRootObjectProvider : IRootObjectProvider
Expand Down
5 changes: 3 additions & 2 deletions src/Controls/src/Xaml/XmlTypeXamlExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ static class XmlTypeXamlExtensions
this XmlType xmlType,
IEnumerable<XmlnsDefinitionAttribute> xmlnsDefinitions,
string defaultAssemblyName,
Func<(string typeName, string clrNamespace, string assemblyName), T> refFromTypeInfo)
Func<(string typeName, string clrNamespace, string assemblyName), T> refFromTypeInfo,
bool tryExtensionFirst = true)
where T : class
{
var lookupAssemblies = new List<XmlnsDefinitionAttribute>();
Expand All @@ -61,7 +62,7 @@ static class XmlTypeXamlExtensions
}

var lookupNames = new List<string>();
if (elementName != "DataTemplate" && !elementName.EndsWith("Extension", StringComparison.Ordinal))
if (elementName != "DataTemplate" && !elementName.EndsWith("Extension", StringComparison.Ordinal) && tryExtensionFirst)
lookupNames.Add(elementName + "Extension");
lookupNames.Add(elementName);

Expand Down
11 changes: 11 additions & 0 deletions src/Controls/tests/Xaml.UnitTests/Issues/Maui11833.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage 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.Maui11833">
<StackLayout>
<Image AnchorX="{Binding Path=Foo, TargetNullValue={x:Static Application.AccentColor}}"/>
<Image AnchorX="{Binding Path=Foo, TargetNullValue={x:Static Binding.DoNothing}}"/>

</StackLayout>
</ContentPage>
25 changes: 25 additions & 0 deletions src/Controls/tests/Xaml.UnitTests/Issues/Maui11833.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using Microsoft.Maui.Controls.Core.UnitTests;
using NUnit.Framework;

namespace Microsoft.Maui.Controls.Xaml.UnitTests
{
public partial class Maui11833 : ContentPage
{
public Maui11833() => InitializeComponent();
public Maui11833(bool useCompiledXaml)
{
//this stub will be replaced at compile time
}

[TestFixture]
class Tests
{
[Test]
public void xStaticBinding([Values(false, true)] bool useCompiledXaml)
{
var page = new Maui11833(useCompiledXaml);
}
}
}

}
2 changes: 1 addition & 1 deletion src/Controls/tests/Xaml.UnitTests/LoaderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -792,7 +792,7 @@ public void StyleWithoutTargetTypeThrows()
public void BindingIsResolvedAsBindingExtension()
// https://github.com/xamarin/Microsoft.Maui.Controls/issues/3606#issuecomment-422377338
{
var bindingType = XamlParser.GetElementType(new XmlType("http://schemas.microsoft.com/dotnet/2021/maui", "Binding", null), null, null, out var ex);
var bindingType = XamlParser.GetElementType(new XmlType("http://schemas.microsoft.com/dotnet/2021/maui", "Binding", null), true, null, null, out var ex);
Assert.That(ex, Is.Null);
Assert.That(bindingType, Is.EqualTo(typeof(BindingExtension)));
var module = ModuleDefinition.CreateModule("foo", new ModuleParameters()
Expand Down
7 changes: 7 additions & 0 deletions src/Controls/tests/Xaml.UnitTests/TypeExtensionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,5 +51,12 @@ public void TestWithExplicitTypeName()
var markupString = @"{x:Type TypeName=sys:String}";
Assert.AreEqual(typeof(string), (new MarkupExtensionParser()).ParseExpression(ref markupString, serviceProvider));
}

[Test]
public void DontExpandToExtension()
{
var markupString = @"{x:Type Binding}";
Assert.AreEqual(typeof(Binding), (new MarkupExtensionParser()).ParseExpression(ref markupString, serviceProvider));
}
}
}