diff --git a/.gitignore b/.gitignore index fad28c60f7e3..5a048174d6c3 100644 --- a/.gitignore +++ b/.gitignore @@ -44,6 +44,7 @@ bld/ # Visual Studio 2017 auto generated files Generated\ Files/ +Generated/ # MSTest test Results [Tt]est[Rr]esult*/ diff --git a/Microsoft.Maui-vscode.sln b/Microsoft.Maui-vscode.sln index d74ad80a1185..d61f2e1e3fd5 100644 --- a/Microsoft.Maui-vscode.sln +++ b/Microsoft.Maui-vscode.sln @@ -212,6 +212,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UITest.Analyzers", "src\Tes EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Maui.Controls.Sample.Embedding", "src\Controls\samples\Controls.Sample.Embedding\Maui.Controls.Sample.Embedding.csproj", "{4ADCBA87-30DB-44F5-85E9-94A4F4132FD9}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SourceGen.UnitTests", "src\Controls\tests\SourceGen.UnitTests\SourceGen.UnitTests.csproj", "{A426B2FC-F012-436B-BDD9-BEC0025DB96B}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -539,6 +541,10 @@ Global {4ADCBA87-30DB-44F5-85E9-94A4F4132FD9}.Release|Any CPU.ActiveCfg = Release|Any CPU {4ADCBA87-30DB-44F5-85E9-94A4F4132FD9}.Release|Any CPU.Build.0 = Release|Any CPU {4ADCBA87-30DB-44F5-85E9-94A4F4132FD9}.Release|Any CPU.Deploy.0 = Release|Any CPU + {A426B2FC-F012-436B-BDD9-BEC0025DB96B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A426B2FC-F012-436B-BDD9-BEC0025DB96B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A426B2FC-F012-436B-BDD9-BEC0025DB96B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A426B2FC-F012-436B-BDD9-BEC0025DB96B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -636,6 +642,7 @@ Global {0048EA9A-D751-4576-A2BB-2A37BFB385A5} = {25D0D27A-C5FE-443D-8B65-D6C987F4A80E} {DA001142-4777-4EDE-97D5-B1AC08162F99} = {7AC28763-9C68-4BF9-A1BA-25CBFFD2D15C} {4ADCBA87-30DB-44F5-85E9-94A4F4132FD9} = {E1082E26-D700-4127-9329-66D673FD2D55} + {A426B2FC-F012-436B-BDD9-BEC0025DB96B} = {25D0D27A-C5FE-443D-8B65-D6C987F4A80E} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {0B8ABEAD-D2B5-4370-A187-62B5ABE4EE50} diff --git a/src/Controls/docs/Microsoft.Maui.Controls/XmlnsDefinitionAttribute.xml b/src/Controls/docs/Microsoft.Maui.Controls/XmlnsDefinitionAttribute.xml index fa5fd8ab4c07..2ccabffcd8f8 100644 --- a/src/Controls/docs/Microsoft.Maui.Controls/XmlnsDefinitionAttribute.xml +++ b/src/Controls/docs/Microsoft.Maui.Controls/XmlnsDefinitionAttribute.xml @@ -24,8 +24,8 @@ - - + + Constructor @@ -35,11 +35,11 @@ - + To be added. - To be added. + To be added. @@ -74,6 +74,22 @@ + + + + + + Property + + Microsoft.Maui.Controls.Core + 2.0.0.0 + + + System.String + + + + diff --git a/src/Controls/src/Build.Tasks/CompiledConverters/BindablePropertyConverter.cs b/src/Controls/src/Build.Tasks/CompiledConverters/BindablePropertyConverter.cs index 261d61e08789..d7488a10fea9 100644 --- a/src/Controls/src/Build.Tasks/CompiledConverters/BindablePropertyConverter.cs +++ b/src/Controls/src/Build.Tasks/CompiledConverters/BindablePropertyConverter.cs @@ -25,6 +25,18 @@ public IEnumerable ConvertFromString(string value, ILContext contex yield return Instruction.Create(OpCodes.Ldsfld, bpRef); } + static bool IsOfAnyType(XmlType xmlType, params string[] types) + { + if (types == null || types.Length == 0) + return false; + if (xmlType == null) + return false; + if (xmlType.NamespaceUri != XamlParser.MauiUri && xmlType.NamespaceUri != XamlParser.MauiGlobalUri) + return false; + if (types.Contains(xmlType.Name)) + return true; + return false; + } public FieldReference GetBindablePropertyFieldReference(string value, ILContext context, ModuleDefinition module, BaseNode node) { FieldReference bpRef = null; @@ -34,24 +46,18 @@ public FieldReference GetBindablePropertyFieldReference(string value, ILContext if (parts.Length == 1) { var parent = node.Parent?.Parent as IElementNode ?? (node.Parent?.Parent as IListNode)?.Parent as IElementNode; - if ((node.Parent as ElementNode)?.XmlType.NamespaceUri == XamlParser.MauiUri - && ((node.Parent as ElementNode)?.XmlType.Name == nameof(Setter) - || (node.Parent as ElementNode)?.XmlType.Name == nameof(PropertyCondition))) + if (IsOfAnyType((node.Parent as ElementNode)?.XmlType, nameof(Setter), nameof(PropertyCondition))) { - if (parent.XmlType.NamespaceUri == XamlParser.MauiUri && - (parent.XmlType.Name == nameof(Trigger) - || parent.XmlType.Name == nameof(DataTrigger) - || parent.XmlType.Name == nameof(MultiTrigger) - || parent.XmlType.Name == nameof(Style))) + if (IsOfAnyType(parent.XmlType, nameof(Trigger), nameof(DataTrigger), nameof(MultiTrigger), nameof(Style))) { typeName = GetTargetTypeName(parent); } - else if (parent.XmlType.NamespaceUri == XamlParser.MauiUri && parent.XmlType.Name == nameof(VisualState)) + else if (IsOfAnyType(parent.XmlType, nameof(VisualState))) { typeName = FindTypeNameForVisualState(parent, node); } } - else if ((node.Parent as ElementNode)?.XmlType.NamespaceUri == XamlParser.MauiUri && (node.Parent as ElementNode)?.XmlType.Name == nameof(Trigger)) + else if (IsOfAnyType((node.Parent as ElementNode)?.XmlType, nameof(Trigger))) { typeName = GetTargetTypeName(node.Parent); } @@ -86,19 +92,19 @@ static string FindTypeNameForVisualState(IElementNode parent, IXmlLineInfo lineI //1. parent is VisualState, don't check that //2. check that the VS is in a VSG - if (!(parent.Parent is IElementNode target) || target.XmlType.NamespaceUri != XamlParser.MauiUri || target.XmlType.Name != nameof(VisualStateGroup)) + // if (!(parent.Parent is IElementNode target) || target.XmlType.NamespaceUri != XamlParser.MauiUri || target.XmlType.Name != nameof(VisualStateGroup)) + if (!(parent.Parent is IElementNode target) || !IsOfAnyType(target.XmlType, nameof(VisualStateGroup))) throw new XamlParseException($"Expected {nameof(VisualStateGroup)} but found {parent.Parent}", lineInfo); //3. if the VSG is in a VSGL, skip that as it could be implicit if (target.Parent is ListNode - || ((target.Parent as IElementNode)?.XmlType.NamespaceUri == XamlParser.MauiUri - && (target.Parent as IElementNode)?.XmlType.Name == nameof(VisualStateGroupList))) + || IsOfAnyType((target.Parent as IElementNode)?.XmlType, nameof(VisualStateGroupList))) target = target.Parent.Parent as IElementNode; else target = target.Parent as IElementNode; //4. target is now a Setter in a Style, or a VE - if (target.XmlType.NamespaceUri == XamlParser.MauiUri && target.XmlType.Name == nameof(Setter)) + if (IsOfAnyType(target.XmlType, nameof(Setter))) return ((target?.Parent as IElementNode)?.Properties[new XmlName("", "TargetType")] as ValueNode)?.Value as string; else return target.XmlType.Name; diff --git a/src/Controls/src/Build.Tasks/ModuleDefinitionExtensions.cs b/src/Controls/src/Build.Tasks/ModuleDefinitionExtensions.cs index 41fe0c965666..34c33621cbdd 100644 --- a/src/Controls/src/Build.Tasks/ModuleDefinitionExtensions.cs +++ b/src/Controls/src/Build.Tasks/ModuleDefinitionExtensions.cs @@ -254,7 +254,8 @@ public static TypeDefinition GetTypeDefinition(this ModuleDefinition module, Xam { return cache.GetOrAddTypeDefinition(module, type, x => { - var asm = module.Assembly.Name.Name == type.assemblyName + var assemblyName = module.Assembly.Name; + var asm = (assemblyName.Name == type.assemblyName || assemblyName.FullName == type.assemblyName) ? module.Assembly : module.AssemblyResolver.Resolve(AssemblyNameReference.Parse(type.assemblyName)); var typeDef = asm.MainModule.GetType($"{type.clrNamespace}.{type.typeName}"); diff --git a/src/Controls/src/Build.Tasks/SetPropertiesVisitor.cs b/src/Controls/src/Build.Tasks/SetPropertiesVisitor.cs index c2ed777870a0..bdc757bdfb3a 100644 --- a/src/Controls/src/Build.Tasks/SetPropertiesVisitor.cs +++ b/src/Controls/src/Build.Tasks/SetPropertiesVisitor.cs @@ -533,7 +533,7 @@ static bool TryCompileBindingPath(ElementNode node, ILContext context, VariableD } if (n.XmlType.Name == nameof(Microsoft.Maui.Controls.DataTemplate) - && n.XmlType.NamespaceUri == XamlParser.MauiUri) + && (n.XmlType.NamespaceUri == XamlParser.MauiUri) || n.XmlType.NamespaceUri == XamlParser.MauiGlobalUri) { xDataTypeIsInOuterScope = true; } diff --git a/src/Controls/src/Build.Tasks/TypeReferenceExtensions.cs b/src/Controls/src/Build.Tasks/TypeReferenceExtensions.cs index 5c20c1dcd64d..933126178885 100644 --- a/src/Controls/src/Build.Tasks/TypeReferenceExtensions.cs +++ b/src/Controls/src/Build.Tasks/TypeReferenceExtensions.cs @@ -9,23 +9,14 @@ namespace Microsoft.Maui.Controls.Build.Tasks { class TypeRefComparer : IEqualityComparer { - static string GetAssemblyName(TypeReference typeRef) - { - if (typeRef.Scope is ModuleDefinition md) - return md.Assembly.Name.Name; - if (typeRef.Scope is AssemblyNameReference anr) - return anr.Name; - throw new ArgumentOutOfRangeException(nameof(typeRef)); - } - public bool Equals(TypeReference x, TypeReference y) { - if (x == null) - return y == null; - if (y == null) - return x == null; + if (x == null && y == null) + return true; + if (x == null || y == null) + return false; - //strip the leading `&` as byref typered fullnames have a `&` + //strip the leading `&` as byref typeref fullnames have a `&` var xname = x.FullName.EndsWith("&", StringComparison.InvariantCulture) ? x.FullName.Substring(0, x.FullName.Length - 1) : x.FullName; var yname = y.FullName.EndsWith("&", StringComparison.InvariantCulture) ? y.FullName.Substring(0, y.FullName.Length - 1) : y.FullName; if (xname != yname) @@ -34,27 +25,40 @@ public bool Equals(TypeReference x, TypeReference y) var yasm = GetAssemblyName(y); //standard types comes from either mscorlib. System.Runtime or netstandard. Assume they are equivalent - if ((xasm.StartsWith("System.Runtime", StringComparison.Ordinal) - || xasm.StartsWith("System", StringComparison.Ordinal) - || xasm.StartsWith("mscorlib", StringComparison.Ordinal) - || xasm.StartsWith("netstandard", StringComparison.Ordinal) - || xasm.StartsWith("System.Xml", StringComparison.Ordinal)) - && (yasm.StartsWith("System.Runtime", StringComparison.Ordinal) - || yasm.StartsWith("System", StringComparison.Ordinal) - || yasm.StartsWith("mscorlib", StringComparison.Ordinal) - || yasm.StartsWith("netstandard", StringComparison.Ordinal) - || yasm.StartsWith("System.Xml", StringComparison.Ordinal))) + if (IsSystemAssembly(xasm) && IsSystemAssembly(yasm)) return true; return xasm == yasm; } public int GetHashCode(TypeReference obj) { - return $"{GetAssemblyName(obj)}//{obj.FullName}".GetHashCode(); + var assemblyName = GetAssemblyName(obj); + if (IsSystemAssembly(assemblyName)) + return obj.FullName.GetHashCode(); + return assemblyName.GetHashCode() ^ obj.FullName.GetHashCode(); + } + + static string GetAssemblyName(TypeReference typeRef) + { + if (typeRef.Scope is ModuleDefinition md) + return md.Assembly.Name.Name; + if (typeRef.Scope is AssemblyNameReference anr) + return anr.Name; + throw new ArgumentOutOfRangeException(nameof(typeRef)); + } + + bool IsSystemAssembly(string assemblyName) + { + return assemblyName.StartsWith("System", StringComparison.Ordinal) + || assemblyName.StartsWith("mscorlib", StringComparison.Ordinal) + || assemblyName.StartsWith("netstandard", StringComparison.Ordinal) + || assemblyName.StartsWith("System.Runtime", StringComparison.Ordinal) + || assemblyName.StartsWith("System.Xml", StringComparison.Ordinal) + || assemblyName.StartsWith("System.Private.CoreLib", StringComparison.Ordinal); } static TypeRefComparer s_default; - public static TypeRefComparer Default => s_default ?? (s_default = new TypeRefComparer()); + public static TypeRefComparer Default => s_default ??= new TypeRefComparer(); } static class TypeReferenceExtensions diff --git a/src/Controls/src/Build.Tasks/XamlCTask.cs b/src/Controls/src/Build.Tasks/XamlCTask.cs index 9592b9cc21e5..f910a08643fa 100644 --- a/src/Controls/src/Build.Tasks/XamlCTask.cs +++ b/src/Controls/src/Build.Tasks/XamlCTask.cs @@ -285,7 +285,7 @@ public override bool Execute(out IList thrownExceptions) ILRootNode rootnode = null; try { - rootnode = ParseXaml(resource.GetResourceStream(), typeDef); + rootnode = ParseXaml(resource.GetResourceStream(), module, typeDef); if (rootnode == null) { LoggingHelper.LogMessage(Low, $"{new string(' ', 8)}failed."); diff --git a/src/Controls/src/Build.Tasks/XamlTask.cs b/src/Controls/src/Build.Tasks/XamlTask.cs index 1b0150150026..5d04f2f7a930 100644 --- a/src/Controls/src/Build.Tasks/XamlTask.cs +++ b/src/Controls/src/Build.Tasks/XamlTask.cs @@ -3,6 +3,7 @@ using System.ComponentModel; using System.Diagnostics; using System.IO; +using System.Linq; using System.Xml; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; @@ -40,9 +41,23 @@ public bool Execute() public abstract bool Execute(out IList thrownExceptions); - internal static ILRootNode ParseXaml(Stream stream, TypeReference typeReference) + internal static ILRootNode ParseXaml(Stream stream, ModuleDefinition module, TypeReference typeReference) { - using (var reader = XmlReader.Create(stream)) + var allowImplicitXmlns = module.Assembly.CustomAttributes.Any(a => + a.AttributeType.FullName == typeof(Microsoft.Maui.Controls.Xaml.Internals.AllowImplicitXmlnsDeclarationAttribute).FullName + && (a.ConstructorArguments.Count == 0 || a.ConstructorArguments[0].Value is bool b && b)); + + var nsmgr = new XmlNamespaceManager(new NameTable()); + if (allowImplicitXmlns) + { + nsmgr.AddNamespace("", XamlParser.DefaultImplicitUri); + foreach (var xmlnsPrefix in XmlTypeExtensions.GetXmlnsPrefixAttributes(module)) + nsmgr.AddNamespace(xmlnsPrefix.Prefix, xmlnsPrefix.XmlNamespace); + } + + using (var reader = XmlReader.Create(stream, + new XmlReaderSettings { ConformanceLevel = allowImplicitXmlns ? ConformanceLevel.Fragment : ConformanceLevel.Document }, + new XmlParserContext(nsmgr.NameTable, nsmgr, null, XmlSpace.None))) { while (reader.Read()) { @@ -73,8 +88,22 @@ public static bool IsXaml(this EmbeddedResource resource, XamlCache cache, Modul if (!resource.Name.EndsWith(".xaml", StringComparison.InvariantCulture)) return false; + var allowImplicitXmlns = module.Assembly.CustomAttributes.Any(a => + a.AttributeType.FullName == typeof(Microsoft.Maui.Controls.Xaml.Internals.AllowImplicitXmlnsDeclarationAttribute).FullName + && (a.ConstructorArguments.Count == 0 || a.ConstructorArguments[0].Value is bool b && b)); + + var nsmgr = new XmlNamespaceManager(new NameTable()); + nsmgr.AddNamespace("__f__", XamlParser.MauiUri); + if (allowImplicitXmlns) + { + nsmgr.AddNamespace("", XamlParser.DefaultImplicitUri); + foreach (var xmlnsPrefix in XmlTypeExtensions.GetXmlnsPrefixAttributes(module)) + nsmgr.AddNamespace(xmlnsPrefix.Prefix, xmlnsPrefix.XmlNamespace); + } using (var resourceStream = resource.GetResourceStream()) - using (var reader = XmlReader.Create(resourceStream)) + using (var reader = XmlReader.Create(resourceStream, + new XmlReaderSettings { ConformanceLevel = allowImplicitXmlns ? ConformanceLevel.Fragment : ConformanceLevel.Document }, + new XmlParserContext(nsmgr.NameTable, nsmgr, null, XmlSpace.None))) { // Read to the first Element while (reader.Read() && reader.NodeType != XmlNodeType.Element) diff --git a/src/Controls/src/Build.Tasks/XmlTypeExtensions.cs b/src/Controls/src/Build.Tasks/XmlTypeExtensions.cs index f11ac9eb3b88..1884ec126b54 100644 --- a/src/Controls/src/Build.Tasks/XmlTypeExtensions.cs +++ b/src/Controls/src/Build.Tasks/XmlTypeExtensions.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Linq; using System.Xml; @@ -10,6 +11,8 @@ namespace Microsoft.Maui.Controls.Build.Tasks { static class XmlTypeExtensions { + static readonly string _xmlnsDefinitionName = typeof(XmlnsDefinitionAttribute).FullName; + static IList GatherXmlnsDefinitionAttributes(ModuleDefinition module) { var xmlnsDefinitions = new List(); @@ -18,22 +21,17 @@ static IList GatherXmlnsDefinitionAttributes(ModuleDef { // Search for the attribute in the assemblies being // referenced. + GatherXmlnsDefinitionAttributes(xmlnsDefinitions, module.Assembly, module.Assembly); + foreach (var asmRef in module.AssemblyReferences) { var asmDef = module.AssemblyResolver.Resolve(asmRef); - foreach (var ca in asmDef.CustomAttributes) - { - if (ca.AttributeType.FullName == typeof(XmlnsDefinitionAttribute).FullName) - { - var attr = GetXmlnsDefinition(ca, asmDef); - xmlnsDefinitions.Add(attr); - } - } + GatherXmlnsDefinitionAttributes(xmlnsDefinitions, asmDef, module.Assembly); } } else { - // Use standard XF assemblies + // Use standard MAUI assemblies // (Should only happen in unit tests) var requiredAssemblies = new[] { typeof(XamlLoader).Assembly, @@ -42,13 +40,46 @@ static IList GatherXmlnsDefinitionAttributes(ModuleDef foreach (var assembly in requiredAssemblies) foreach (XmlnsDefinitionAttribute attribute in assembly.GetCustomAttributes(typeof(XmlnsDefinitionAttribute), false)) { - attribute.AssemblyName = attribute.AssemblyName ?? assembly.FullName; + attribute.AssemblyName ??= assembly.FullName; + ValidateProtectedXmlns(attribute.XmlNamespace, attribute.AssemblyName); xmlnsDefinitions.Add(attribute); } } return xmlnsDefinitions; } + static void ValidateProtectedXmlns(string xmlNamespace, string assemblyName) + { + //maui, and x: xmlns are protected + if (xmlNamespace != XamlParser.MauiUri && xmlNamespace != XamlParser.X2009Uri) + return; + + //we know thos assemblies, they are fine in maui or x xmlns + if (assemblyName.StartsWith("Microsoft", StringComparison.OrdinalIgnoreCase) + || assemblyName.StartsWith("System", StringComparison.OrdinalIgnoreCase) + || assemblyName.StartsWith("mscorlib", StringComparison.OrdinalIgnoreCase)) + return; + + throw new BuildException(BuildExceptionCode.InvalidXaml, null, null, + $"Protected Xmlns {xmlNamespace}. Can't add assembly {assemblyName}."); + + } + static void GatherXmlnsDefinitionAttributes(List xmlnsDefinitions, AssemblyDefinition asmDef, AssemblyDefinition currentAssembly) + { + foreach (var ca in asmDef.CustomAttributes) + { + if (ca.AttributeType.FullName == _xmlnsDefinitionName) + { + var attr = GetXmlnsDefinition(ca, asmDef); + //only add globalxmlns definition from the current assembly + if (attr.XmlNamespace == XamlParser.MauiGlobalUri + && asmDef != currentAssembly) + continue; + ValidateProtectedXmlns(attr.XmlNamespace, attr.AssemblyName); + xmlnsDefinitions.Add(attr); + } + } + } public static TypeReference GetTypeReference(XamlCache cache, string typeName, ModuleDefinition module, BaseNode node, bool expandToExtension = true) { @@ -74,8 +105,10 @@ public static bool TryGetTypeReference(this XmlType xmlType, XamlCache cache, Mo var typeArguments = xmlType.TypeArguments; - TypeReference type = xmlType.GetTypeReference(xmlnsDefinitions, module.Assembly.Name.Name, (typeInfo) => + IEnumerable types = xmlType.GetTypeReferences(xmlnsDefinitions, module.Assembly.Name.Name, (typeInfo) => { + if (typeInfo.clrNamespace.StartsWith("http")) //aggregated xmlns, might result in a typeload exception + return null; string typeName = typeInfo.typeName.Replace('+', '/'); //Nested types var type = module.GetTypeDefinition(cache, (typeInfo.assemblyName, typeInfo.clrNamespace, typeName)); if (type is not null && type.IsPublicOrVisibleInternal(module)) @@ -83,6 +116,13 @@ public static bool TryGetTypeReference(this XmlType xmlType, XamlCache cache, Mo return null; }, expandToExtension: expandToExtension); + if (types.Distinct(TypeRefComparer.Default).Skip(1).Any()) + { + typeReference = null; + return false; + } + + var type = types.Distinct(TypeRefComparer.Default).FirstOrDefault(); if (type != null && typeArguments != null && type.HasGenericParameters) type = module.ImportReference(type).MakeGenericInstanceType(typeArguments.Select(x => x.GetTypeReference(cache, module, xmlInfo)).ToArray()); @@ -97,7 +137,7 @@ public static TypeReference GetTypeReference(this XmlType xmlType, XamlCache cac throw new BuildException(BuildExceptionCode.TypeResolution, xmlInfo, null, $"{xmlType.NamespaceUri}:{xmlType.Name}"); } - public static XmlnsDefinitionAttribute GetXmlnsDefinition(this CustomAttribute ca, AssemblyDefinition asmDef) + static XmlnsDefinitionAttribute GetXmlnsDefinition(this CustomAttribute ca, AssemblyDefinition asmDef) { var attr = new XmlnsDefinitionAttribute( ca.ConstructorArguments[0].Value as string, @@ -109,5 +149,48 @@ public static XmlnsDefinitionAttribute GetXmlnsDefinition(this CustomAttribute c attr.AssemblyName = assemblyName ?? asmDef.Name.FullName; return attr; } + + public static IList GetXmlnsPrefixAttributes(ModuleDefinition module) + { + var xmlnsPrefixes = new List(); + foreach (var ca in module.Assembly.CustomAttributes) + { + if (ca.AttributeType.FullName == typeof(XmlnsPrefixAttribute).FullName) + { + var attr = new XmlnsPrefixAttribute( + ca.ConstructorArguments[0].Value as string, + ca.ConstructorArguments[1].Value as string); + xmlnsPrefixes.Add(attr); + } + } + + if (module.AssemblyReferences?.Count > 0) + { + // Search for the attribute in the assemblies being + // referenced. + foreach (var asmRef in module.AssemblyReferences) + { + try + { + var asmDef = module.AssemblyResolver.Resolve(asmRef); + foreach (var ca in asmDef.CustomAttributes) + { + if (ca.AttributeType.FullName == typeof(XmlnsPrefixAttribute).FullName) + { + var attr = new XmlnsPrefixAttribute( + ca.ConstructorArguments[0].Value as string, + ca.ConstructorArguments[1].Value as string); + xmlnsPrefixes.Add(attr); + } + } + } + catch (System.Exception) + { + // Ignore assembly resolution errors + } + } + } + return xmlnsPrefixes; + } } } diff --git a/src/Controls/src/Core/AllowImplicitXmlnsDeclarationAttribute.cs b/src/Controls/src/Core/AllowImplicitXmlnsDeclarationAttribute.cs new file mode 100644 index 000000000000..a57728973470 --- /dev/null +++ b/src/Controls/src/Core/AllowImplicitXmlnsDeclarationAttribute.cs @@ -0,0 +1,14 @@ +#nullable enable +using System; +using System.Runtime.Versioning; + +namespace Microsoft.Maui.Controls.Xaml.Internals; + +[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)] +#if !NETSTANDARD +[RequiresPreviewFeatures] +#endif +public sealed class AllowImplicitXmlnsDeclarationAttribute(bool allow = true) : Attribute +{ + public bool Allow { get; } = allow; +} \ No newline at end of file diff --git a/src/Controls/src/Core/Properties/AssemblyInfo.cs b/src/Controls/src/Core/Properties/AssemblyInfo.cs index 5510be121462..564f9e0339f5 100644 --- a/src/Controls/src/Core/Properties/AssemblyInfo.cs +++ b/src/Controls/src/Core/Properties/AssemblyInfo.cs @@ -79,6 +79,7 @@ [assembly: XmlnsPrefix("http://schemas.microsoft.com/dotnet/2021/maui", "maui")] [assembly: XmlnsPrefix("http://schemas.microsoft.com/dotnet/2021/maui/design", "d")] +[assembly: XmlnsPrefix("http://schemas.microsoft.com/winfx/2009/xaml", "x")] [assembly: StyleProperty("background-color", typeof(VisualElement), nameof(VisualElement.BackgroundColorProperty))] [assembly: StyleProperty("background", typeof(VisualElement), nameof(VisualElement.BackgroundProperty))] diff --git a/src/Controls/src/Core/PublicAPI/net-android/PublicAPI.Shipped.txt b/src/Controls/src/Core/PublicAPI/net-android/PublicAPI.Shipped.txt index ce68bc5c9d50..ae9a487a3c9f 100644 --- a/src/Controls/src/Core/PublicAPI/net-android/PublicAPI.Shipped.txt +++ b/src/Controls/src/Core/PublicAPI/net-android/PublicAPI.Shipped.txt @@ -1907,7 +1907,7 @@ ~Microsoft.Maui.Controls.XmlnsDefinitionAttribute.AssemblyName.set -> void ~Microsoft.Maui.Controls.XmlnsDefinitionAttribute.ClrNamespace.get -> string ~Microsoft.Maui.Controls.XmlnsDefinitionAttribute.XmlNamespace.get -> string -~Microsoft.Maui.Controls.XmlnsDefinitionAttribute.XmlnsDefinitionAttribute(string xmlNamespace, string clrNamespace) -> void +~Microsoft.Maui.Controls.XmlnsDefinitionAttribute.XmlnsDefinitionAttribute(string xmlNamespace, string target) -> void ~Microsoft.Maui.Controls.XmlnsPrefixAttribute.Prefix.get -> string ~Microsoft.Maui.Controls.XmlnsPrefixAttribute.XmlNamespace.get -> string ~Microsoft.Maui.Controls.XmlnsPrefixAttribute.XmlnsPrefixAttribute(string xmlNamespace, string prefix) -> void diff --git a/src/Controls/src/Core/PublicAPI/net-android/PublicAPI.Unshipped.txt b/src/Controls/src/Core/PublicAPI/net-android/PublicAPI.Unshipped.txt index 59ed8f054f61..5f6b5b5c1d42 100644 --- a/src/Controls/src/Core/PublicAPI/net-android/PublicAPI.Unshipped.txt +++ b/src/Controls/src/Core/PublicAPI/net-android/PublicAPI.Unshipped.txt @@ -383,3 +383,7 @@ const Microsoft.Maui.Controls.BrushTypeConverter.Rgba = "rgba" -> string! Microsoft.Maui.Controls.BrushTypeConverter.GradientBrushParser.GradientBrushParser(Microsoft.Maui.Graphics.Converters.ColorTypeConverter? colorConverter = null) -> void Microsoft.Maui.Controls.BrushTypeConverter.GradientBrushParser.Parse(string? css) -> Microsoft.Maui.Controls.GradientBrush? static Microsoft.Maui.Controls.Shapes.PathFigureCollectionConverter.ParseStringToPathFigureCollection(Microsoft.Maui.Controls.Shapes.PathFigureCollection! pathFigureCollection, string? pathString) -> void +Microsoft.Maui.Controls.Xaml.Internals.AllowImplicitXmlnsDeclarationAttribute +Microsoft.Maui.Controls.Xaml.Internals.AllowImplicitXmlnsDeclarationAttribute.Allow.get -> bool +Microsoft.Maui.Controls.Xaml.Internals.AllowImplicitXmlnsDeclarationAttribute.AllowImplicitXmlnsDeclarationAttribute(bool allow = true) -> void +~Microsoft.Maui.Controls.XmlnsDefinitionAttribute.Target.get -> string diff --git a/src/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Shipped.txt b/src/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Shipped.txt index 92ab1bbb6309..9b2241faac8c 100644 --- a/src/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Shipped.txt +++ b/src/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Shipped.txt @@ -1866,7 +1866,7 @@ ~Microsoft.Maui.Controls.XmlnsDefinitionAttribute.AssemblyName.set -> void ~Microsoft.Maui.Controls.XmlnsDefinitionAttribute.ClrNamespace.get -> string ~Microsoft.Maui.Controls.XmlnsDefinitionAttribute.XmlNamespace.get -> string -~Microsoft.Maui.Controls.XmlnsDefinitionAttribute.XmlnsDefinitionAttribute(string xmlNamespace, string clrNamespace) -> void +~Microsoft.Maui.Controls.XmlnsDefinitionAttribute.XmlnsDefinitionAttribute(string xmlNamespace, string target) -> void ~Microsoft.Maui.Controls.XmlnsPrefixAttribute.Prefix.get -> string ~Microsoft.Maui.Controls.XmlnsPrefixAttribute.XmlNamespace.get -> string ~Microsoft.Maui.Controls.XmlnsPrefixAttribute.XmlnsPrefixAttribute(string xmlNamespace, string prefix) -> void diff --git a/src/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Unshipped.txt b/src/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Unshipped.txt index 06d6197cb329..2d00b072a8aa 100644 --- a/src/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Unshipped.txt +++ b/src/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Unshipped.txt @@ -585,3 +585,7 @@ const Microsoft.Maui.Controls.BrushTypeConverter.Rgba = "rgba" -> string! Microsoft.Maui.Controls.BrushTypeConverter.GradientBrushParser.GradientBrushParser(Microsoft.Maui.Graphics.Converters.ColorTypeConverter? colorConverter = null) -> void Microsoft.Maui.Controls.BrushTypeConverter.GradientBrushParser.Parse(string? css) -> Microsoft.Maui.Controls.GradientBrush? static Microsoft.Maui.Controls.Shapes.PathFigureCollectionConverter.ParseStringToPathFigureCollection(Microsoft.Maui.Controls.Shapes.PathFigureCollection! pathFigureCollection, string? pathString) -> void +Microsoft.Maui.Controls.Xaml.Internals.AllowImplicitXmlnsDeclarationAttribute +Microsoft.Maui.Controls.Xaml.Internals.AllowImplicitXmlnsDeclarationAttribute.Allow.get -> bool +Microsoft.Maui.Controls.Xaml.Internals.AllowImplicitXmlnsDeclarationAttribute.AllowImplicitXmlnsDeclarationAttribute(bool allow = true) -> void +~Microsoft.Maui.Controls.XmlnsDefinitionAttribute.Target.get -> string diff --git a/src/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Shipped.txt b/src/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Shipped.txt index 92ab1bbb6309..9b2241faac8c 100644 --- a/src/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Shipped.txt +++ b/src/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Shipped.txt @@ -1866,7 +1866,7 @@ ~Microsoft.Maui.Controls.XmlnsDefinitionAttribute.AssemblyName.set -> void ~Microsoft.Maui.Controls.XmlnsDefinitionAttribute.ClrNamespace.get -> string ~Microsoft.Maui.Controls.XmlnsDefinitionAttribute.XmlNamespace.get -> string -~Microsoft.Maui.Controls.XmlnsDefinitionAttribute.XmlnsDefinitionAttribute(string xmlNamespace, string clrNamespace) -> void +~Microsoft.Maui.Controls.XmlnsDefinitionAttribute.XmlnsDefinitionAttribute(string xmlNamespace, string target) -> void ~Microsoft.Maui.Controls.XmlnsPrefixAttribute.Prefix.get -> string ~Microsoft.Maui.Controls.XmlnsPrefixAttribute.XmlNamespace.get -> string ~Microsoft.Maui.Controls.XmlnsPrefixAttribute.XmlnsPrefixAttribute(string xmlNamespace, string prefix) -> void diff --git a/src/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt b/src/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt index 9535b0873802..7d7075472fc2 100644 --- a/src/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt +++ b/src/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt @@ -585,3 +585,7 @@ const Microsoft.Maui.Controls.BrushTypeConverter.Rgba = "rgba" -> string! Microsoft.Maui.Controls.BrushTypeConverter.GradientBrushParser.GradientBrushParser(Microsoft.Maui.Graphics.Converters.ColorTypeConverter? colorConverter = null) -> void Microsoft.Maui.Controls.BrushTypeConverter.GradientBrushParser.Parse(string? css) -> Microsoft.Maui.Controls.GradientBrush? static Microsoft.Maui.Controls.Shapes.PathFigureCollectionConverter.ParseStringToPathFigureCollection(Microsoft.Maui.Controls.Shapes.PathFigureCollection! pathFigureCollection, string? pathString) -> void +Microsoft.Maui.Controls.Xaml.Internals.AllowImplicitXmlnsDeclarationAttribute +Microsoft.Maui.Controls.Xaml.Internals.AllowImplicitXmlnsDeclarationAttribute.Allow.get -> bool +Microsoft.Maui.Controls.Xaml.Internals.AllowImplicitXmlnsDeclarationAttribute.AllowImplicitXmlnsDeclarationAttribute(bool allow = true) -> void +~Microsoft.Maui.Controls.XmlnsDefinitionAttribute.Target.get -> string diff --git a/src/Controls/src/Core/PublicAPI/net-tizen/PublicAPI.Shipped.txt b/src/Controls/src/Core/PublicAPI/net-tizen/PublicAPI.Shipped.txt index ba7dcea67a25..94d4cededcd8 100644 --- a/src/Controls/src/Core/PublicAPI/net-tizen/PublicAPI.Shipped.txt +++ b/src/Controls/src/Core/PublicAPI/net-tizen/PublicAPI.Shipped.txt @@ -1685,7 +1685,7 @@ ~Microsoft.Maui.Controls.XmlnsDefinitionAttribute.AssemblyName.set -> void ~Microsoft.Maui.Controls.XmlnsDefinitionAttribute.ClrNamespace.get -> string ~Microsoft.Maui.Controls.XmlnsDefinitionAttribute.XmlNamespace.get -> string -~Microsoft.Maui.Controls.XmlnsDefinitionAttribute.XmlnsDefinitionAttribute(string xmlNamespace, string clrNamespace) -> void +~Microsoft.Maui.Controls.XmlnsDefinitionAttribute.XmlnsDefinitionAttribute(string xmlNamespace, string target) -> void ~Microsoft.Maui.Controls.XmlnsPrefixAttribute.Prefix.get -> string ~Microsoft.Maui.Controls.XmlnsPrefixAttribute.XmlNamespace.get -> string ~Microsoft.Maui.Controls.XmlnsPrefixAttribute.XmlnsPrefixAttribute(string xmlNamespace, string prefix) -> void diff --git a/src/Controls/src/Core/PublicAPI/net-tizen/PublicAPI.Unshipped.txt b/src/Controls/src/Core/PublicAPI/net-tizen/PublicAPI.Unshipped.txt index 5d7c9911fc37..87dceab020ba 100644 --- a/src/Controls/src/Core/PublicAPI/net-tizen/PublicAPI.Unshipped.txt +++ b/src/Controls/src/Core/PublicAPI/net-tizen/PublicAPI.Unshipped.txt @@ -339,4 +339,8 @@ const Microsoft.Maui.Controls.BrushTypeConverter.Rgb = "rgb" -> string! const Microsoft.Maui.Controls.BrushTypeConverter.Rgba = "rgba" -> string! Microsoft.Maui.Controls.BrushTypeConverter.GradientBrushParser.GradientBrushParser(Microsoft.Maui.Graphics.Converters.ColorTypeConverter? colorConverter = null) -> void Microsoft.Maui.Controls.BrushTypeConverter.GradientBrushParser.Parse(string? css) -> Microsoft.Maui.Controls.GradientBrush? -static Microsoft.Maui.Controls.Shapes.PathFigureCollectionConverter.ParseStringToPathFigureCollection(Microsoft.Maui.Controls.Shapes.PathFigureCollection! pathFigureCollection, string? pathString) -> void \ No newline at end of file +static Microsoft.Maui.Controls.Shapes.PathFigureCollectionConverter.ParseStringToPathFigureCollection(Microsoft.Maui.Controls.Shapes.PathFigureCollection! pathFigureCollection, string? pathString) -> void +Microsoft.Maui.Controls.Xaml.Internals.AllowImplicitXmlnsDeclarationAttribute +Microsoft.Maui.Controls.Xaml.Internals.AllowImplicitXmlnsDeclarationAttribute.Allow.get -> bool +Microsoft.Maui.Controls.Xaml.Internals.AllowImplicitXmlnsDeclarationAttribute.AllowImplicitXmlnsDeclarationAttribute(bool allow = true) -> void +~Microsoft.Maui.Controls.XmlnsDefinitionAttribute.Target.get -> string diff --git a/src/Controls/src/Core/PublicAPI/net-windows/PublicAPI.Shipped.txt b/src/Controls/src/Core/PublicAPI/net-windows/PublicAPI.Shipped.txt index bc3a72d2f293..e5b38dca7a4f 100644 --- a/src/Controls/src/Core/PublicAPI/net-windows/PublicAPI.Shipped.txt +++ b/src/Controls/src/Core/PublicAPI/net-windows/PublicAPI.Shipped.txt @@ -1755,7 +1755,7 @@ ~Microsoft.Maui.Controls.XmlnsDefinitionAttribute.AssemblyName.set -> void ~Microsoft.Maui.Controls.XmlnsDefinitionAttribute.ClrNamespace.get -> string ~Microsoft.Maui.Controls.XmlnsDefinitionAttribute.XmlNamespace.get -> string -~Microsoft.Maui.Controls.XmlnsDefinitionAttribute.XmlnsDefinitionAttribute(string xmlNamespace, string clrNamespace) -> void +~Microsoft.Maui.Controls.XmlnsDefinitionAttribute.XmlnsDefinitionAttribute(string xmlNamespace, string target) -> void ~Microsoft.Maui.Controls.XmlnsPrefixAttribute.Prefix.get -> string ~Microsoft.Maui.Controls.XmlnsPrefixAttribute.XmlNamespace.get -> string ~Microsoft.Maui.Controls.XmlnsPrefixAttribute.XmlnsPrefixAttribute(string xmlNamespace, string prefix) -> void diff --git a/src/Controls/src/Core/PublicAPI/net-windows/PublicAPI.Unshipped.txt b/src/Controls/src/Core/PublicAPI/net-windows/PublicAPI.Unshipped.txt index 632347663727..d7830043034a 100644 --- a/src/Controls/src/Core/PublicAPI/net-windows/PublicAPI.Unshipped.txt +++ b/src/Controls/src/Core/PublicAPI/net-windows/PublicAPI.Unshipped.txt @@ -381,3 +381,7 @@ const Microsoft.Maui.Controls.BrushTypeConverter.Rgba = "rgba" -> string! Microsoft.Maui.Controls.BrushTypeConverter.GradientBrushParser.GradientBrushParser(Microsoft.Maui.Graphics.Converters.ColorTypeConverter? colorConverter = null) -> void Microsoft.Maui.Controls.BrushTypeConverter.GradientBrushParser.Parse(string? css) -> Microsoft.Maui.Controls.GradientBrush? static Microsoft.Maui.Controls.Shapes.PathFigureCollectionConverter.ParseStringToPathFigureCollection(Microsoft.Maui.Controls.Shapes.PathFigureCollection! pathFigureCollection, string? pathString) -> void +Microsoft.Maui.Controls.Xaml.Internals.AllowImplicitXmlnsDeclarationAttribute +Microsoft.Maui.Controls.Xaml.Internals.AllowImplicitXmlnsDeclarationAttribute.Allow.get -> bool +Microsoft.Maui.Controls.Xaml.Internals.AllowImplicitXmlnsDeclarationAttribute.AllowImplicitXmlnsDeclarationAttribute(bool allow = true) -> void +~Microsoft.Maui.Controls.XmlnsDefinitionAttribute.Target.get -> string diff --git a/src/Controls/src/Core/PublicAPI/net/PublicAPI.Shipped.txt b/src/Controls/src/Core/PublicAPI/net/PublicAPI.Shipped.txt index 3792b5f9c96a..e2c9056a24a6 100644 --- a/src/Controls/src/Core/PublicAPI/net/PublicAPI.Shipped.txt +++ b/src/Controls/src/Core/PublicAPI/net/PublicAPI.Shipped.txt @@ -1681,7 +1681,7 @@ ~Microsoft.Maui.Controls.XmlnsDefinitionAttribute.AssemblyName.set -> void ~Microsoft.Maui.Controls.XmlnsDefinitionAttribute.ClrNamespace.get -> string ~Microsoft.Maui.Controls.XmlnsDefinitionAttribute.XmlNamespace.get -> string -~Microsoft.Maui.Controls.XmlnsDefinitionAttribute.XmlnsDefinitionAttribute(string xmlNamespace, string clrNamespace) -> void +~Microsoft.Maui.Controls.XmlnsDefinitionAttribute.XmlnsDefinitionAttribute(string xmlNamespace, string target) -> void ~Microsoft.Maui.Controls.XmlnsPrefixAttribute.Prefix.get -> string ~Microsoft.Maui.Controls.XmlnsPrefixAttribute.XmlNamespace.get -> string ~Microsoft.Maui.Controls.XmlnsPrefixAttribute.XmlnsPrefixAttribute(string xmlNamespace, string prefix) -> void diff --git a/src/Controls/src/Core/PublicAPI/net/PublicAPI.Unshipped.txt b/src/Controls/src/Core/PublicAPI/net/PublicAPI.Unshipped.txt index 14dd646338f9..9aae72bcdf67 100644 --- a/src/Controls/src/Core/PublicAPI/net/PublicAPI.Unshipped.txt +++ b/src/Controls/src/Core/PublicAPI/net/PublicAPI.Unshipped.txt @@ -100,6 +100,9 @@ static Microsoft.Maui.Controls.ViewExtensions.ScaleToAsync(this Microsoft.Maui.C static Microsoft.Maui.Controls.ViewExtensions.ScaleXToAsync(this Microsoft.Maui.Controls.VisualElement! view, double scale, uint length = 250, Microsoft.Maui.Easing? easing = null) -> System.Threading.Tasks.Task! static Microsoft.Maui.Controls.ViewExtensions.ScaleYToAsync(this Microsoft.Maui.Controls.VisualElement! view, double scale, uint length = 250, Microsoft.Maui.Easing? easing = null) -> System.Threading.Tasks.Task! static Microsoft.Maui.Controls.ViewExtensions.TranslateToAsync(this Microsoft.Maui.Controls.VisualElement! view, double x, double y, uint length = 250, Microsoft.Maui.Easing? easing = null) -> System.Threading.Tasks.Task! +Microsoft.Maui.Controls.Xaml.Internals.AllowImplicitXmlnsDeclarationAttribute +Microsoft.Maui.Controls.Xaml.Internals.AllowImplicitXmlnsDeclarationAttribute.Allow.get -> bool +Microsoft.Maui.Controls.Xaml.Internals.AllowImplicitXmlnsDeclarationAttribute.AllowImplicitXmlnsDeclarationAttribute(bool allow = true) -> void ~Microsoft.Maui.Controls.Internals.TypedBindingBase.UpdateSourceEventName.set -> void ~Microsoft.Maui.Controls.ResourceDictionary.SetAndCreateSource(System.Uri value) -> void ~Microsoft.Maui.Controls.Xaml.IXamlTypeResolver.Resolve(string qualifiedTypeName, System.IServiceProvider serviceProvider = null, bool expandToExtension = true) -> System.Type @@ -369,3 +372,4 @@ const Microsoft.Maui.Controls.BrushTypeConverter.Rgba = "rgba" -> string! Microsoft.Maui.Controls.BrushTypeConverter.GradientBrushParser.GradientBrushParser(Microsoft.Maui.Graphics.Converters.ColorTypeConverter? colorConverter = null) -> void Microsoft.Maui.Controls.BrushTypeConverter.GradientBrushParser.Parse(string? css) -> Microsoft.Maui.Controls.GradientBrush? static Microsoft.Maui.Controls.Shapes.PathFigureCollectionConverter.ParseStringToPathFigureCollection(Microsoft.Maui.Controls.Shapes.PathFigureCollection! pathFigureCollection, string? pathString) -> void +~Microsoft.Maui.Controls.XmlnsDefinitionAttribute.Target.get -> string diff --git a/src/Controls/src/Core/PublicAPI/netstandard/PublicAPI.Shipped.txt b/src/Controls/src/Core/PublicAPI/netstandard/PublicAPI.Shipped.txt index 3792b5f9c96a..e2c9056a24a6 100644 --- a/src/Controls/src/Core/PublicAPI/netstandard/PublicAPI.Shipped.txt +++ b/src/Controls/src/Core/PublicAPI/netstandard/PublicAPI.Shipped.txt @@ -1681,7 +1681,7 @@ ~Microsoft.Maui.Controls.XmlnsDefinitionAttribute.AssemblyName.set -> void ~Microsoft.Maui.Controls.XmlnsDefinitionAttribute.ClrNamespace.get -> string ~Microsoft.Maui.Controls.XmlnsDefinitionAttribute.XmlNamespace.get -> string -~Microsoft.Maui.Controls.XmlnsDefinitionAttribute.XmlnsDefinitionAttribute(string xmlNamespace, string clrNamespace) -> void +~Microsoft.Maui.Controls.XmlnsDefinitionAttribute.XmlnsDefinitionAttribute(string xmlNamespace, string target) -> void ~Microsoft.Maui.Controls.XmlnsPrefixAttribute.Prefix.get -> string ~Microsoft.Maui.Controls.XmlnsPrefixAttribute.XmlNamespace.get -> string ~Microsoft.Maui.Controls.XmlnsPrefixAttribute.XmlnsPrefixAttribute(string xmlNamespace, string prefix) -> void diff --git a/src/Controls/src/Core/PublicAPI/netstandard/PublicAPI.Unshipped.txt b/src/Controls/src/Core/PublicAPI/netstandard/PublicAPI.Unshipped.txt index a73d5b7157f6..e33a5569b6b8 100644 --- a/src/Controls/src/Core/PublicAPI/netstandard/PublicAPI.Unshipped.txt +++ b/src/Controls/src/Core/PublicAPI/netstandard/PublicAPI.Unshipped.txt @@ -102,6 +102,7 @@ static Microsoft.Maui.Controls.ViewExtensions.TranslateToAsync(this Microsoft.Ma ~override Microsoft.Maui.Controls.Compatibility.StackLayout.ComputeConstraintForView(Microsoft.Maui.Controls.View view) -> void ~override Microsoft.Maui.Controls.ContentPresenter.ComputeConstraintForView(Microsoft.Maui.Controls.View view) -> void ~override Microsoft.Maui.Controls.ScrollView.ComputeConstraintForView(Microsoft.Maui.Controls.View view) -> void +~Microsoft.Maui.Controls.XmlnsDefinitionAttribute.Target.get -> string ~override Microsoft.Maui.Controls.ShellContent.OnPropertyChanged(string propertyName = null) -> void *REMOVED*~static Microsoft.Maui.Controls.Application.ControlsApplicationMapper -> Microsoft.Maui.IPropertyMapper ~override Microsoft.Maui.Controls.TemplatedPage.ComputeConstraintForView(Microsoft.Maui.Controls.View view) -> void @@ -368,3 +369,6 @@ const Microsoft.Maui.Controls.BrushTypeConverter.Rgba = "rgba" -> string! Microsoft.Maui.Controls.BrushTypeConverter.GradientBrushParser.GradientBrushParser(Microsoft.Maui.Graphics.Converters.ColorTypeConverter? colorConverter = null) -> void Microsoft.Maui.Controls.BrushTypeConverter.GradientBrushParser.Parse(string? css) -> Microsoft.Maui.Controls.GradientBrush? static Microsoft.Maui.Controls.Shapes.PathFigureCollectionConverter.ParseStringToPathFigureCollection(Microsoft.Maui.Controls.Shapes.PathFigureCollection! pathFigureCollection, string? pathString) -> void +Microsoft.Maui.Controls.Xaml.Internals.AllowImplicitXmlnsDeclarationAttribute +Microsoft.Maui.Controls.Xaml.Internals.AllowImplicitXmlnsDeclarationAttribute.Allow.get -> bool +Microsoft.Maui.Controls.Xaml.Internals.AllowImplicitXmlnsDeclarationAttribute.AllowImplicitXmlnsDeclarationAttribute(bool allow = true) -> void diff --git a/src/Controls/src/Core/XmlnsDefinitionAttribute.cs b/src/Controls/src/Core/XmlnsDefinitionAttribute.cs index ce07d170a8f4..c84259e35986 100644 --- a/src/Controls/src/Core/XmlnsDefinitionAttribute.cs +++ b/src/Controls/src/Core/XmlnsDefinitionAttribute.cs @@ -2,25 +2,34 @@ using System; using System.Diagnostics; -namespace Microsoft.Maui.Controls +namespace Microsoft.Maui.Controls; + +/// +[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] +[DebuggerDisplay("{XmlNamespace}, {Target}, {AssemblyName}")] +public sealed class XmlnsDefinitionAttribute : Attribute { - /// - [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] - [DebuggerDisplay("{XmlNamespace}, {ClrNamespace}, {AssemblyName}")] - public sealed class XmlnsDefinitionAttribute : Attribute + /// + public string XmlNamespace { get; } + + /// + public string Target { get; } + /// + [Obsolete("Use Target for ClrNamespace or other xmlns")] + public string ClrNamespace => Target; + /// + public string AssemblyName { get; set; } + + /// + public XmlnsDefinitionAttribute(string xmlNamespace, string target) { - /// - public string XmlNamespace { get; } - /// - public string ClrNamespace { get; } - /// - public string AssemblyName { get; set; } + //TODO we need an analyzer to check before runtime + if (target == "http://schemas.microsoft.com/winfx/2009/xaml" || target == "http://schemas.microsoft.com/winfx/2006/xaml") + throw new ArgumentException($"Target cannot be {target}. That namespace can't be aggregated", nameof(target)); - /// - public XmlnsDefinitionAttribute(string xmlNamespace, string clrNamespace) - { - ClrNamespace = clrNamespace ?? throw new ArgumentNullException(nameof(xmlNamespace)); - XmlNamespace = xmlNamespace ?? throw new ArgumentNullException(nameof(clrNamespace)); - } + if (target.StartsWith("http", StringComparison.Ordinal) && xmlNamespace != "http://schemas.microsoft.com/dotnet/maui/global") + throw new ArgumentException($"We only support xmlns aggregation in http://schemas.microsoft.com/dotnet/maui/global", nameof(target)); + Target = target ?? throw new ArgumentNullException(nameof(target)); + XmlNamespace = xmlNamespace ?? throw new ArgumentNullException(nameof(xmlNamespace)); } } diff --git a/src/Controls/src/SourceGen/AnalyzerReleases.Unshipped.md b/src/Controls/src/SourceGen/AnalyzerReleases.Unshipped.md index 73f6c9134b93..dd98720fd12a 100644 --- a/src/Controls/src/SourceGen/AnalyzerReleases.Unshipped.md +++ b/src/Controls/src/SourceGen/AnalyzerReleases.Unshipped.md @@ -6,3 +6,4 @@ Rule ID | Category | Severity | Notes --------|----------|----------|------- MAUIG1001 | XamlParsing | Error | XamlParsingFailed +MAUIG1002 | XamlParsing | Error | AmbiguousType diff --git a/src/Controls/src/SourceGen/CodeBehindGenerator.cs b/src/Controls/src/SourceGen/CodeBehindGenerator.cs index 880c828a91d9..55b0da09fbcb 100644 --- a/src/Controls/src/SourceGen/CodeBehindGenerator.cs +++ b/src/Controls/src/SourceGen/CodeBehindGenerator.cs @@ -39,6 +39,15 @@ public void Initialize(IncrementalGeneratorInitializationContext initContext) // System.Diagnostics.Debugger.Launch(); //} #endif + // Only provide a new Compilation when the references change + var referenceCompilationProvider = initContext.CompilationProvider + .WithComparer(new CompilationReferencesComparer()) + .WithTrackingName(TrackingNames.ReferenceCompilationProvider); + + var xmlnsDefinitionsProvider = referenceCompilationProvider + .Select(GetAssemblyAttributes) + .WithTrackingName(TrackingNames.XmlnsDefinitionsProvider); + var projectItemProvider = initContext.AdditionalTextsProvider .Combine(initContext.AnalyzerConfigOptionsProvider) .Select(ComputeProjectItem) @@ -46,6 +55,7 @@ public void Initialize(IncrementalGeneratorInitializationContext initContext) var xamlProjectItemProvider = projectItemProvider .Where(static p => p?.Kind == "Xaml") + .Combine(xmlnsDefinitionsProvider) .Select(ComputeXamlProjectItem) .WithTrackingName(TrackingNames.XamlProjectItemProvider); @@ -53,30 +63,18 @@ public void Initialize(IncrementalGeneratorInitializationContext initContext) .Where(static p => p?.Kind == "Css") .WithTrackingName(TrackingNames.CssProjectItemProvider); - // Only provide a new Compilation when the references change - var referenceCompilationProvider = initContext.CompilationProvider - .WithComparer(new CompilationReferencesComparer()) - .WithTrackingName(TrackingNames.ReferenceCompilationProvider); - - var xmlnsDefinitionsProvider = referenceCompilationProvider - .Select(GetAssemblyAttributes) - .WithTrackingName(TrackingNames.XmlnsDefinitionsProvider); - var referenceTypeCacheProvider = referenceCompilationProvider .Select(GetTypeCache) .WithTrackingName(TrackingNames.ReferenceTypeCacheProvider); var xamlSourceProvider = xamlProjectItemProvider - .Combine(xmlnsDefinitionsProvider) - .Combine(referenceTypeCacheProvider) - .Combine(referenceCompilationProvider) - .Select(static (t, _) => (t.Left.Left, t.Left.Right, t.Right)) + .Combine(xmlnsDefinitionsProvider, referenceTypeCacheProvider, referenceCompilationProvider) .WithTrackingName(TrackingNames.XamlSourceProvider); // Register the XAML pipeline initContext.RegisterSourceOutput(xamlSourceProvider, static (sourceProductionContext, provider) => { - var ((xamlItem, xmlnsCache), typeCache, compilation) = provider; + var (xamlItem, xmlnsCache, typeCache, compilation) = provider; GenerateXamlCodeBehind(xamlItem, compilation, sourceProductionContext, xmlnsCache, typeCache); }); @@ -91,6 +89,44 @@ public void Initialize(IncrementalGeneratorInitializationContext initContext) GenerateCssCodeBehind(cssItem, sourceProductionContext); }); + + initContext.RegisterPostInitializationOutput(static context => + { + context.AddSource("GlobalXmlns.g.cs", SourceText.From( +$""" +{AutoGeneratedHeaderText} + +[assembly: global::Microsoft.Maui.Controls.XmlnsDefinition("{XamlParser.MauiGlobalUri}", "{XamlParser.MauiUri}")] +[assembly: global::Microsoft.Maui.Controls.XmlnsPrefix("{XamlParser.MauiGlobalUri}", "global")] + +#if MauiAllowImplicitXmlnsDeclaration +[assembly: global::Microsoft.Maui.Controls.Xaml.Internals.AllowImplicitXmlnsDeclaration] +#endif +""" + , Encoding.UTF8)); + }); + + initContext.RegisterSourceOutput(xmlnsDefinitionsProvider, static (sourceProductionContext, xmlnsCache) => + { + var source = GenerateGlobalXmlns(sourceProductionContext, xmlnsCache); + if (!string.IsNullOrEmpty(source)) + sourceProductionContext.AddSource("Global.Xmlns.cs", SourceText.From(source!, Encoding.UTF8)); + }); + } + + private static string? GenerateGlobalXmlns(SourceProductionContext sourceProductionContext, AssemblyCaches xmlnsCache) + { + if (xmlnsCache.GlobalGeneratedXmlnsDefinitions.Count == 0) + { + return null; + } + var sb = new StringBuilder(); + sb.AppendLine(AutoGeneratedHeaderText); + foreach (var xmlns in xmlnsCache.GlobalGeneratedXmlnsDefinitions) + { + sb.AppendLine($"[assembly: global::Microsoft.Maui.Controls.XmlnsDefinition(\"{xmlns.XmlNamespace}\", \"{xmlns.Target}\", AssemblyName = \"{EscapeIdentifier(xmlns.AssemblyName)}\")]"); + } + return sb.ToString(); } static string EscapeIdentifier(string identifier) @@ -117,8 +153,10 @@ static string EscapeIdentifier(string identifier) return new ProjectItem(additionalText, targetPath: targetPath, relativePath: relativePath, manifestResourceName: manifestResourceName, kind: kind, targetFramework: targetFramework); } - static XamlProjectItem? ComputeXamlProjectItem(ProjectItem? projectItem, CancellationToken cancellationToken) + static XamlProjectItem? ComputeXamlProjectItem((ProjectItem?, AssemblyCaches) itemAndCaches, CancellationToken cancellationToken) { + var projectItem = itemAndCaches.Item1; + var xmlnsCache = itemAndCaches.Item2; if (projectItem == null) { return null; @@ -130,10 +168,25 @@ static string EscapeIdentifier(string identifier) return null; } + var allowImplicitXmlns = itemAndCaches.Item2.AllowImplicitXmlns; + + var nsmgr = new XmlNamespaceManager(new NameTable()); + nsmgr.AddNamespace("__f__", XamlParser.MauiUri); + nsmgr.AddNamespace("__g__", XamlParser.MauiGlobalUri); + if (allowImplicitXmlns) + { + nsmgr.AddNamespace("", XamlParser.DefaultImplicitUri); + foreach (var xmlnsPrefix in xmlnsCache.XmlnsPrefixes) + nsmgr.AddNamespace(xmlnsPrefix.Prefix, xmlnsPrefix.XmlNamespace); + } + using var reader = XmlReader.Create(new StringReader(text.ToString()), + new XmlReaderSettings { ConformanceLevel = allowImplicitXmlns ? ConformanceLevel.Fragment : ConformanceLevel.Document }, + new XmlParserContext(nsmgr.NameTable, nsmgr, null, XmlSpace.None)); + var xmlDoc = new XmlDocument(); try { - xmlDoc.LoadXml(text.ToString()); + xmlDoc.Load(reader); } catch (XmlException xe) { @@ -149,9 +202,6 @@ static string EscapeIdentifier(string identifier) cancellationToken.ThrowIfCancellationRequested(); - var nsmgr = new XmlNamespaceManager(xmlDoc.NameTable); - nsmgr.AddNamespace("__f__", XamlParser.MauiUri); - var root = xmlDoc.SelectSingleNode("/*", nsmgr); if (root == null) { @@ -189,6 +239,12 @@ static AssemblyCaches GetAssemblyAttributes(Compilation compilation, Cancellatio // [assembly: InternalsVisibleTo] INamedTypeSymbol? internalsVisibleToAttribute = compilation.GetTypeByMetadataName(typeof(InternalsVisibleToAttribute).FullName); + INamedTypeSymbol? xmlnsPrefixAttribute = compilation.GetTypesByMetadataName(typeof(XmlnsPrefixAttribute).FullName) + .SingleOrDefault(t => t.ContainingAssembly.Identity.Name == "Microsoft.Maui.Controls"); + + INamedTypeSymbol? allowImplicitXmlnsAttribute = compilation.GetTypesByMetadataName(typeof(Xaml.Internals.AllowImplicitXmlnsDeclarationAttribute).FullName) + .SingleOrDefault(t => t.ContainingAssembly.Identity.Name == "Microsoft.Maui.Controls"); + if (xmlnsDefinitonAttribute is null || internalsVisibleToAttribute is null) { return AssemblyCaches.Empty; @@ -196,20 +252,32 @@ static AssemblyCaches GetAssemblyAttributes(Compilation compilation, Cancellatio var xmlnsDefinitions = new List(); var internalsVisible = new List(); - + var xmlnsPrefixes = new List(); + var allowImplicitXmlns = compilation.Assembly.GetAttributes() + .Any(a => + SymbolEqualityComparer.Default.Equals(a.AttributeClass, allowImplicitXmlnsAttribute) + && (a.ConstructorArguments.Length == 0 || a.ConstructorArguments[0].Value is bool b && b)); internalsVisible.Add(compilation.Assembly); - // load from references + IList assemblies = new List(); + assemblies.Add(compilation.Assembly); foreach (var reference in compilation.References) { cancellationToken.ThrowIfCancellationRequested(); - if (compilation.GetAssemblyOrModuleSymbol(reference) is not IAssemblySymbol symbol) + var assembly = compilation.GetAssemblyOrModuleSymbol(reference); + if (assembly is IAssemblySymbol assemblySymbol) { - continue; + assemblies.Add(assemblySymbol); } + } + + // load from references + foreach (var assembly in assemblies) + { + cancellationToken.ThrowIfCancellationRequested(); - foreach (var attr in symbol.GetAttributes()) + foreach (var attr in assembly.GetAttributes()) { if (SymbolEqualityComparer.Default.Equals(attr.AttributeClass, xmlnsDefinitonAttribute)) { @@ -221,7 +289,14 @@ static AssemblyCaches GetAssemblyAttributes(Compilation compilation, Cancellatio } else { - xmlnsDef.AssemblyName = symbol.Name; + xmlnsDef.AssemblyName = assembly.Name; + } + + //only add globalxmlns definition from the current assembly + if (xmlnsDef.XmlNamespace == XamlParser.MauiGlobalUri + && !SymbolEqualityComparer.Default.Equals(assembly, compilation.Assembly)) + { + continue; } xmlnsDefinitions.Add(xmlnsDef); @@ -231,12 +306,31 @@ static AssemblyCaches GetAssemblyAttributes(Compilation compilation, Cancellatio // [assembly: InternalsVisibleTo] if (attr.ConstructorArguments[0].Value is string assemblyName && new AssemblyName(assemblyName).Name == compilation.Assembly.Identity.Name) { - internalsVisible.Add(symbol); + internalsVisible.Add(assembly); } } + else if (SymbolEqualityComparer.Default.Equals(attr.AttributeClass, xmlnsPrefixAttribute)) + { + // [assembly: XmlnsPrefix] + var xmlnsPrefix = new XmlnsPrefixAttribute(attr.ConstructorArguments[0].Value as string, attr.ConstructorArguments[1].Value as string); + xmlnsPrefixes.Add(xmlnsPrefix); + } + } + } + + var globalXmlns = xmlnsDefinitions.Where(x => x.XmlNamespace == XamlParser.MauiGlobalUri).ToList(); + var globalGeneratedXmlnsDefinitions = new List(); + foreach (var global in globalXmlns) + { + var pointedXmlns = xmlnsDefinitions.Where(x => x.XmlNamespace == global.Target).ToList(); + foreach (var pointed in pointedXmlns) + { + xmlnsDefinitions.Add(new XmlnsDefinitionAttribute(global.XmlNamespace, pointed.Target) { AssemblyName = pointed.AssemblyName }); + globalGeneratedXmlnsDefinitions.Add(new XmlnsDefinitionAttribute(global.XmlNamespace, pointed.Target) { AssemblyName = pointed.AssemblyName }); } } - return new AssemblyCaches(xmlnsDefinitions, internalsVisible); + + return new AssemblyCaches([.. xmlnsDefinitions.Distinct()], xmlnsPrefixes, [.. globalGeneratedXmlnsDefinitions.Distinct()], internalsVisible, allowImplicitXmlns); } static IDictionary GetTypeCache(Compilation compilation, CancellationToken cancellationToken) @@ -275,7 +369,7 @@ static void GenerateXamlCodeBehind(XamlProjectItem? xamlItem, Compilation compil } var uid = Crc64.ComputeHashString($"{compilation.AssemblyName}.{itemName}"); - if (!TryParseXaml(xamlItem, uid, compilation, xmlnsCache, typeCache, context.CancellationToken, out var accessModifier, out var rootType, out var rootClrNamespace, out var generateDefaultCtor, out var addXamlCompilationAttribute, out var hideFromIntellisense, out var XamlResourceIdOnly, out var baseType, out var namedFields)) + if (!TryParseXaml(xamlItem, uid, compilation, xmlnsCache, typeCache, context.CancellationToken, context.ReportDiagnostic, out var accessModifier, out var rootType, out var rootClrNamespace, out var generateDefaultCtor, out var addXamlCompilationAttribute, out var hideFromIntellisense, out var XamlResourceIdOnly, out var baseType, out var namedFields)) { return; } @@ -288,12 +382,13 @@ static void GenerateXamlCodeBehind(XamlProjectItem? xamlItem, Compilation compil if (projItem.ManifestResourceName != null && projItem.TargetPath != null) { sb.AppendLine($"[assembly: global::Microsoft.Maui.Controls.Xaml.XamlResourceId(\"{projItem.ManifestResourceName}\", \"{projItem.TargetPath.Replace('\\', '/')}\", {(rootType == null ? "null" : "typeof(global::" + rootClrNamespace + "." + rootType + ")")})]"); - } - if (XamlResourceIdOnly) - { - context.AddSource(hintName, SourceText.From(sb.ToString(), Encoding.UTF8)); - return; + + if (XamlResourceIdOnly) + { + context.AddSource(hintName, SourceText.From(sb.ToString(), Encoding.UTF8)); + return; + } } if (rootType == null) @@ -377,7 +472,7 @@ static void GenerateXamlCodeBehind(XamlProjectItem? xamlItem, Compilation compil context.AddSource(hintName, SourceText.From(sb.ToString(), Encoding.UTF8)); } - static bool TryParseXaml(XamlProjectItem parseResult, string uid, Compilation compilation, AssemblyCaches xmlnsCache, IDictionary typeCache, CancellationToken cancellationToken, out string? accessModifier, out string? rootType, out string? rootClrNamespace, out bool generateDefaultCtor, out bool addXamlCompilationAttribute, out bool hideFromIntellisense, out bool xamlResourceIdOnly, out string? baseType, out IEnumerable<(string, string, string)>? namedFields) + static bool TryParseXaml(XamlProjectItem parseResult, string uid, Compilation compilation, AssemblyCaches xmlnsCache, IDictionary typeCache, CancellationToken cancellationToken, Action reportDiagnostic, out string? accessModifier, out string? rootType, out string? rootClrNamespace, out bool generateDefaultCtor, out bool addXamlCompilationAttribute, out bool hideFromIntellisense, out bool xamlResourceIdOnly, out string? baseType, out IEnumerable<(string, string?, string)>? namedFields) { accessModifier = null; rootType = null; @@ -427,9 +522,11 @@ static bool TryParseXaml(XamlProjectItem parseResult, string uid, Compilation co return true; } - namedFields = GetNamedFields(root, nsmgr, compilation, xmlnsCache, typeCache, cancellationToken); + namedFields = GetNamedFields(root, nsmgr, compilation, xmlnsCache, typeCache, cancellationToken, reportDiagnostic); var typeArguments = GetAttributeValue(root, "TypeArguments", XamlParser.X2006Uri, XamlParser.X2009Uri); - baseType = GetTypeName(new XmlType(root.NamespaceURI, root.LocalName, typeArguments != null ? TypeArgumentsParser.ParseExpression(typeArguments, nsmgr, null) : null), compilation, xmlnsCache, typeCache); + baseType = GetTypeName(new XmlType(root.NamespaceURI, root.LocalName, typeArguments != null ? TypeArgumentsParser.ParseExpression(typeArguments, nsmgr, null) : null), compilation, xmlnsCache, typeCache, reportDiagnostic); + if (baseType == null) + return false; // x:ClassModifier attribute var classModifier = GetAttributeValue(root, "ClassModifier", XamlParser.X2006Uri, XamlParser.X2009Uri); @@ -457,7 +554,7 @@ static bool GetXamlCompilationProcessingInstruction(XmlDocument xmlDoc) return true; } - static IEnumerable<(string name, string type, string accessModifier)> GetNamedFields(XmlNode root, XmlNamespaceManager nsmgr, Compilation compilation, AssemblyCaches xmlnsCache, IDictionary typeCache, CancellationToken cancellationToken) + static IEnumerable<(string name, string? type, string accessModifier)> GetNamedFields(XmlNode root, XmlNamespaceManager nsmgr, Compilation compilation, AssemblyCaches xmlnsCache, IDictionary typeCache, CancellationToken cancellationToken, Action reportDiagnostic) { var xPrefix = nsmgr.LookupPrefix(XamlParser.X2006Uri) ?? nsmgr.LookupPrefix(XamlParser.X2009Uri); if (xPrefix == null) @@ -468,7 +565,8 @@ static bool GetXamlCompilationProcessingInstruction(XmlDocument xmlDoc) XmlNodeList names = root.SelectNodes( "//*[@" + xPrefix + ":Name" + - "][not(ancestor:: __f__:DataTemplate) and not(ancestor:: __f__:ControlTemplate) and not(ancestor:: __f__:Style) and not(ancestor:: __f__:VisualStateManager.VisualStateGroups)]", nsmgr); + "][not(ancestor:: __f__:DataTemplate) and not(ancestor:: __f__:ControlTemplate) and not(ancestor:: __f__:Style) and not(ancestor:: __f__:VisualStateManager.VisualStateGroups)" + + "and not(ancestor:: __g__:DataTemplate) and not(ancestor:: __g__:ControlTemplate) and not(ancestor:: __g__:Style) and not(ancestor:: __g__:VisualStateManager.VisualStateGroups)]", nsmgr); foreach (XmlNode node in names) { cancellationToken.ThrowIfCancellationRequested(); @@ -488,11 +586,11 @@ static bool GetXamlCompilationProcessingInstruction(XmlDocument xmlDoc) accessModifier = "private"; } - yield return (name ?? "", GetTypeName(xmlType, compilation, xmlnsCache, typeCache), accessModifier); + yield return (name ?? "", GetTypeName(xmlType, compilation, xmlnsCache, typeCache, reportDiagnostic), accessModifier); } } - static string GetTypeName(XmlType xmlType, Compilation compilation, AssemblyCaches xmlnsCache, IDictionary typeCache) + static string? GetTypeName(XmlType xmlType, Compilation compilation, AssemblyCaches xmlnsCache, IDictionary typeCache, Action reportDiagnostic) { if (typeCache.TryGetValue(xmlType, out string returnType)) { @@ -507,12 +605,17 @@ static string GetTypeName(XmlType xmlType, Compilation compilation, AssemblyCach else { // It's an external, non-built-in namespace URL. - returnType = GetTypeNameFromCustomNamespace(xmlType, compilation, xmlnsCache); + returnType = GetTypeNameFromCustomNamespace(xmlType, compilation, xmlnsCache, reportDiagnostic); } if (xmlType.TypeArguments != null) { - returnType = $"{returnType}<{string.Join(", ", xmlType.TypeArguments.Select(typeArg => GetTypeName(typeArg, compilation, xmlnsCache, typeCache)))}>"; + returnType = $"{returnType}<{string.Join(", ", xmlType.TypeArguments.Select(typeArg => GetTypeName(typeArg, compilation, xmlnsCache, typeCache, reportDiagnostic)))}>"; + } + + if (returnType == null) + { + return null; } returnType = $"global::{returnType}"; @@ -537,10 +640,10 @@ static string GetTypeName(XmlType xmlType, Compilation compilation, AssemblyCach return XmlnsHelper.ParseNamespaceFromXmlns(namespaceuri); } - static string GetTypeNameFromCustomNamespace(XmlType xmlType, Compilation compilation, AssemblyCaches xmlnsCache) + static string GetTypeNameFromCustomNamespace(XmlType xmlType, Compilation compilation, AssemblyCaches xmlnsCache, Action reportDiagnostic) { #nullable disable - string typeName = xmlType.GetTypeReference(xmlnsCache.XmlnsDefinitions, null, + IEnumerable typeNames = xmlType.GetTypeReferences(xmlnsCache.XmlnsDefinitions, null, (typeInfo) => { string typeName = typeInfo.typeName.Replace('+', '/'); //Nested types @@ -576,7 +679,13 @@ static string GetTypeNameFromCustomNamespace(XmlType xmlType, Compilation compil return null; }); - return typeName; + if (typeNames.Distinct().Skip(1).Any()) + { + reportDiagnostic(Diagnostic.Create(Descriptors.AmbiguousType, Location.None, + new[] { xmlType.Name, xmlType.NamespaceUri }.ToArray())); + + } + return typeNames.FirstOrDefault(); #nullable enable } @@ -755,17 +864,22 @@ public XamlProjectItem(ProjectItem projectItem, Exception exception) class AssemblyCaches { - public static readonly AssemblyCaches Empty = new(Array.Empty(), Array.Empty()); + public static readonly AssemblyCaches Empty = new(Array.Empty(), Array.Empty(), Array.Empty(), Array.Empty(), false); - public AssemblyCaches(IReadOnlyList xmlnsDefinitions, IReadOnlyList internalsVisible) + public AssemblyCaches(IReadOnlyList xmlnsDefinitions, IReadOnlyList xmlnsPrefixes, IReadOnlyList globalGeneratedXmlnsDefinitions, IReadOnlyList internalsVisible, bool allowImplicitXmlns) { XmlnsDefinitions = xmlnsDefinitions; + XmlnsPrefixes = xmlnsPrefixes; + GlobalGeneratedXmlnsDefinitions = globalGeneratedXmlnsDefinitions; InternalsVisible = internalsVisible; + AllowImplicitXmlns = allowImplicitXmlns; } public IReadOnlyList XmlnsDefinitions { get; } - + public IReadOnlyList XmlnsPrefixes { get; } + public IReadOnlyList GlobalGeneratedXmlnsDefinitions { get; } public IReadOnlyList InternalsVisible { get; } + public bool AllowImplicitXmlns { get; } } class CompilationReferencesComparer : IEqualityComparer @@ -785,9 +899,6 @@ public bool Equals(Compilation x, Compilation y) return x.ExternalReferences.OfType().SequenceEqual(y.ExternalReferences.OfType()); } - public int GetHashCode(Compilation obj) - { - return obj.References.GetHashCode(); - } + public int GetHashCode(Compilation obj) => obj.References.GetHashCode(); } } diff --git a/src/Controls/src/SourceGen/Controls.SourceGen.csproj b/src/Controls/src/SourceGen/Controls.SourceGen.csproj index ab0f81f64a3b..1590927936fb 100644 --- a/src/Controls/src/SourceGen/Controls.SourceGen.csproj +++ b/src/Controls/src/SourceGen/Controls.SourceGen.csproj @@ -21,6 +21,8 @@ + + diff --git a/src/Controls/src/SourceGen/Descriptors.cs b/src/Controls/src/SourceGen/Descriptors.cs index 0fd68752da24..1728f4c6121d 100644 --- a/src/Controls/src/SourceGen/Descriptors.cs +++ b/src/Controls/src/SourceGen/Descriptors.cs @@ -12,6 +12,14 @@ public static class Descriptors category: "XamlParsing", defaultSeverity: DiagnosticSeverity.Error, isEnabledByDefault: true); + + public static DiagnosticDescriptor AmbiguousType = new DiagnosticDescriptor( + id: "MAUIG1002", + title: new LocalizableResourceString(nameof(MauiGResources.AmbiguousTypeTitle), MauiGResources.ResourceManager, typeof(MauiGResources)), + messageFormat: new LocalizableResourceString(nameof(MauiGResources.AmbiguousTypeMessage), MauiGResources.ResourceManager, typeof(MauiGResources)), + category: "XamlParsing", + defaultSeverity: DiagnosticSeverity.Error, + isEnabledByDefault: true); } } diff --git a/src/Controls/src/SourceGen/IncrementalValueProviderExtensions.cs b/src/Controls/src/SourceGen/IncrementalValueProviderExtensions.cs new file mode 100644 index 000000000000..14ec8fcd35cd --- /dev/null +++ b/src/Controls/src/SourceGen/IncrementalValueProviderExtensions.cs @@ -0,0 +1,21 @@ +using System; +using Microsoft.CodeAnalysis; + +namespace Microsoft.Maui.Controls.SourceGen; + +//https://github.com/dotnet/roslyn/pull/78316 +public static class IncrementalValueProviderExtensions +{ + public static IncrementalValuesProvider<(TProvider1 provider1, TProvider2 provider2, TProvider3 provider3)> Combine(this IncrementalValuesProvider provider1, IncrementalValueProvider provider2, IncrementalValueProvider provider3) => provider1.Combine(provider2).Combine(provider3).Select(static (t, _) => (t.Left.Left, t.Left.Right, t.Right)); + + public static IncrementalValuesProvider<(TProvider1 provider1, TProvider2 provider2, TProvider3 provider3, TProvider4 provider4)> Combine(this IncrementalValuesProvider provider1, IncrementalValueProvider provider2, IncrementalValueProvider provider3, IncrementalValueProvider provider4) => provider1.Combine(provider2).Combine(provider3).Combine(provider4).Select(static (t, _) => (t.Left.Left.Left, t.Left.Left.Right, t.Left.Right, t.Right)); + + public static IncrementalValuesProvider<(TProvider1 provider1, TProvider2 provider2, TProvider3 provider3, TProvider4 provider4, TProvider5 provider5)> Combine(this IncrementalValuesProvider provider1, IncrementalValueProvider provider2, IncrementalValueProvider provider3, IncrementalValueProvider provider4, IncrementalValueProvider provider5) => provider1.Combine(provider2).Combine(provider3).Combine(provider4).Combine(provider5).Select(static (t, _) => (t.Left.Left.Left.Left, t.Left.Left.Left.Right, t.Left.Left.Right, t.Left.Right, t.Right)); + + public static IncrementalValueProvider<(TProvider1 provider1, TProvider2 provider2, TProvider3 provider3)> Combine(this IncrementalValueProvider provider1, IncrementalValueProvider provider2, IncrementalValueProvider provider3) => provider1.Combine(provider2).Combine(provider3).Select(static (t, _) => (t.Left.Left, t.Left.Right, t.Right)); + + public static IncrementalValueProvider<(TProvider1 provider1, TProvider2 provider2, TProvider3 provider3, TProvider4 provider4)> Combine(this IncrementalValueProvider provider1, IncrementalValueProvider provider2, IncrementalValueProvider provider3, IncrementalValueProvider provider4) => provider1.Combine(provider2).Combine(provider3).Combine(provider4).Select(static (t, _) => (t.Left.Left.Left, t.Left.Left.Right, t.Left.Right, t.Right)); + + public static IncrementalValueProvider<(TProvider1 provider1, TProvider2 provider2, TProvider3 provider3, TProvider4 provider4, TProvider5 provider5)> Combine(this IncrementalValueProvider provider1, IncrementalValueProvider provider2, IncrementalValueProvider provider3, IncrementalValueProvider provider4, IncrementalValueProvider provider5) => provider1.Combine(provider2).Combine(provider3).Combine(provider4).Combine(provider5).Select(static (t, _) => (t.Left.Left.Left.Left, t.Left.Left.Left.Right, t.Left.Left.Right, t.Left.Right, t.Right)); + +} diff --git a/src/Controls/src/SourceGen/MauiGResources.Designer.cs b/src/Controls/src/SourceGen/MauiGResources.Designer.cs index e0d8b241e515..e788f92f6a53 100644 --- a/src/Controls/src/SourceGen/MauiGResources.Designer.cs +++ b/src/Controls/src/SourceGen/MauiGResources.Designer.cs @@ -77,5 +77,21 @@ internal static string XamlParsingFailed { return ResourceManager.GetString("XamlParsingFailed", resourceCulture); } } - } + + internal static string AmbiguousTypeTitle + { + get + { + return ResourceManager.GetString("AmbiguousTypeTitle", resourceCulture); + } + } + + internal static string AmbiguousTypeMessage + { + get + { + return ResourceManager.GetString("AmbiguousTypeMessage", resourceCulture); + } + } + } } diff --git a/src/Controls/src/SourceGen/MauiGResources.resx b/src/Controls/src/SourceGen/MauiGResources.resx index 15c046cb8f47..e9dd621cd7f9 100644 --- a/src/Controls/src/SourceGen/MauiGResources.resx +++ b/src/Controls/src/SourceGen/MauiGResources.resx @@ -1,6 +1,6 @@  - + + + diff --git a/src/Controls/tests/Xaml.UnitTests/GlobalXmlns.xaml b/src/Controls/tests/Xaml.UnitTests/GlobalXmlns.xaml new file mode 100644 index 000000000000..30b8b6913255 --- /dev/null +++ b/src/Controls/tests/Xaml.UnitTests/GlobalXmlns.xaml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/src/Controls/tests/Xaml.UnitTests/GlobalXmlns.xaml.cs b/src/Controls/tests/Xaml.UnitTests/GlobalXmlns.xaml.cs new file mode 100644 index 000000000000..603dafcde90e --- /dev/null +++ b/src/Controls/tests/Xaml.UnitTests/GlobalXmlns.xaml.cs @@ -0,0 +1,40 @@ +using Microsoft.Maui.ApplicationModel; +using Microsoft.Maui.Controls.Core.UnitTests; +using Microsoft.Maui.Dispatching; +using Microsoft.Maui.UnitTests; +using NUnit.Framework; + +namespace Microsoft.Maui.Controls.Xaml.UnitTests; + +public partial class GlobalXmlns +{ + public GlobalXmlns() => InitializeComponent(); + + public GlobalXmlns(bool useCompiledXaml) + { + //this stub will be replaced at compile time + } + + [TestFixture] + class Test + { + [SetUp] + public void Setup() + { + Application.SetCurrentApplication(new MockApplication()); + DispatcherProvider.SetCurrent(new DispatcherProviderStub()); + } + + [TearDown] public void TearDown() => AppInfo.SetCurrent(null); + + [Test] + public void WorksWithoutXDeclaration([Values] bool useCompiledXaml) + { + if (useCompiledXaml) + MockCompiler.Compile(typeof(GlobalXmlns)); + var page = new GlobalXmlns(useCompiledXaml); + Assert.That(page.label, Is.Not.Null); + Assert.That(page.label.Text, Is.EqualTo("No xmlns:x declaration, but x: usage anyway")); + } + } +} \ No newline at end of file diff --git a/src/Controls/tests/Xaml.UnitTests/GlobalXmlnsWithDataTemplate.xaml b/src/Controls/tests/Xaml.UnitTests/GlobalXmlnsWithDataTemplate.xaml new file mode 100644 index 000000000000..1b1693961511 --- /dev/null +++ b/src/Controls/tests/Xaml.UnitTests/GlobalXmlnsWithDataTemplate.xaml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Controls/tests/Xaml.UnitTests/GlobalXmlnsWithDataTemplate.xaml.cs b/src/Controls/tests/Xaml.UnitTests/GlobalXmlnsWithDataTemplate.xaml.cs new file mode 100644 index 000000000000..39435a36ce01 --- /dev/null +++ b/src/Controls/tests/Xaml.UnitTests/GlobalXmlnsWithDataTemplate.xaml.cs @@ -0,0 +1,20 @@ +using NUnit.Framework; + +namespace Microsoft.Maui.Controls.Xaml.UnitTests; + +public partial class GlobalXmlnsWithDataTemplate : ContentPage +{ + public GlobalXmlnsWithDataTemplate() + { + InitializeComponent(); + } + public GlobalXmlnsWithDataTemplate(bool useCompiledXaml) + { + } + + [Test] + public void GlobalXmlnsWithDataTemplateTest([Values] bool useCompiledXaml) + { + var page = new GlobalXmlnsWithDataTemplate(useCompiledXaml); + } +} \ No newline at end of file diff --git a/src/Controls/tests/Xaml.UnitTests/GlobalXmlnsWithStyle.xaml b/src/Controls/tests/Xaml.UnitTests/GlobalXmlnsWithStyle.xaml new file mode 100644 index 000000000000..ae64430aae44 --- /dev/null +++ b/src/Controls/tests/Xaml.UnitTests/GlobalXmlnsWithStyle.xaml @@ -0,0 +1,16 @@ + + + + + #0000FF + + \ No newline at end of file diff --git a/src/Controls/tests/Xaml.UnitTests/GlobalXmlnsWithStyle.xaml.cs b/src/Controls/tests/Xaml.UnitTests/GlobalXmlnsWithStyle.xaml.cs new file mode 100644 index 000000000000..4bfb320cf658 --- /dev/null +++ b/src/Controls/tests/Xaml.UnitTests/GlobalXmlnsWithStyle.xaml.cs @@ -0,0 +1,26 @@ +using Microsoft.Maui.Graphics; +using NUnit.Framework; + +namespace Microsoft.Maui.Controls.Xaml.UnitTests; + +public partial class GlobalXmlnsWithStyle : ContentPage +{ + public GlobalXmlnsWithStyle() + { + InitializeComponent(); + } + public GlobalXmlnsWithStyle(bool useCompiledXaml) + { + // this stub will be replaced at compile time + } + + [Test] + public void GlobalXmlnsWithStyleTest([Values] bool useCompiledXaml) + { + + var page = new GlobalXmlnsWithStyle(useCompiledXaml); + Assert.That(page.label0.TextColor, Is.EqualTo(Colors.Red)); + Assert.That(page.label0.BackgroundColor, Is.EqualTo(Colors.Blue)); + + } +} \ No newline at end of file diff --git a/src/Controls/tests/Xaml.UnitTests/GlobalXmlnsWithXStatic.xaml b/src/Controls/tests/Xaml.UnitTests/GlobalXmlnsWithXStatic.xaml new file mode 100644 index 000000000000..b999d076abff --- /dev/null +++ b/src/Controls/tests/Xaml.UnitTests/GlobalXmlnsWithXStatic.xaml @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/src/Controls/tests/Xaml.UnitTests/GlobalXmlnsWithXStatic.xaml.cs b/src/Controls/tests/Xaml.UnitTests/GlobalXmlnsWithXStatic.xaml.cs new file mode 100644 index 000000000000..39d8db574693 --- /dev/null +++ b/src/Controls/tests/Xaml.UnitTests/GlobalXmlnsWithXStatic.xaml.cs @@ -0,0 +1,51 @@ +using Microsoft.Maui.Graphics; +using Microsoft.Maui.Controls; +using NUnit.Framework; + +[assembly: XmlnsDefinition("http://schemas.microsoft.com/dotnet/maui/global", "Microsoft.Maui.Controls.Xaml.UnitTests.NSGlobalXmlnsWithXStatic")] + +namespace Microsoft.Maui.Controls.Xaml.UnitTests.NSGlobalXmlnsWithXStatic +{ + public class MockxStatic + { + public static string MockStaticProperty { get { return "Property"; } } + public const string MockConstant = "MConstant"; + public static string MockField = "Field"; + public static string MockFieldRef = Icons.CLOSE; + public string InstanceProperty { get { return "InstanceProperty"; } } + public static readonly Color BackgroundColor = Colors.Fuchsia; + + public class Nested + { + public static string Foo = "FOO"; + } + } +} + +namespace Microsoft.Maui.Controls.Xaml.UnitTests +{ + public partial class GlobalXmlnsWithXStatic : ContentPage + { + public GlobalXmlnsWithXStatic() + { + InitializeComponent(); + } + + public GlobalXmlnsWithXStatic(bool useCompiledXaml) + { + //this stub will be replaced at compile time + } + + [Test] + public void XStaticWithAggregatedXmlns([Values] bool useCompiledXaml) + { + if (useCompiledXaml) + MockCompiler.Compile(typeof(GlobalXmlnsWithXStatic)); + + var page = new GlobalXmlnsWithXStatic(useCompiledXaml); + Assert.That(page.label0.Text, Is.EqualTo("MConstant")); + } + + + } +} \ No newline at end of file diff --git a/src/Controls/tests/Xaml.UnitTests/HRTests.cs b/src/Controls/tests/Xaml.UnitTests/HRTests.cs index 36f18e082173..c9edd7c59547 100644 --- a/src/Controls/tests/Xaml.UnitTests/HRTests.cs +++ b/src/Controls/tests/Xaml.UnitTests/HRTests.cs @@ -7,6 +7,12 @@ namespace Microsoft.Maui.Controls.Xaml.UnitTests [TestFixture] public class HRTests { + [SetUp] + public void SetUp() + { + Application.Current = null; + } + [TearDown] public void TearDown() { diff --git a/src/Controls/tests/Xaml.UnitTests/Issues/Unreported003.xaml.cs b/src/Controls/tests/Xaml.UnitTests/Issues/Unreported003.xaml.cs index 491d5b46dc3d..a3f831dd052a 100644 --- a/src/Controls/tests/Xaml.UnitTests/Issues/Unreported003.xaml.cs +++ b/src/Controls/tests/Xaml.UnitTests/Issues/Unreported003.xaml.cs @@ -23,6 +23,9 @@ class Tests [TestCase(true), TestCase(false)] public void AllowCtorArgsForValueTypes(bool useCompiledXaml) { + if (useCompiledXaml) + MockCompiler.Compile(typeof(Unreported003)); + var page = new Unreported003(useCompiledXaml); } } diff --git a/src/Controls/tests/Xaml.UnitTests/MSBuild/MSBuildTests.cs b/src/Controls/tests/Xaml.UnitTests/MSBuild/MSBuildTests.cs index 23bea48b6a68..3132516d3269 100644 --- a/src/Controls/tests/Xaml.UnitTests/MSBuild/MSBuildTests.cs +++ b/src/Controls/tests/Xaml.UnitTests/MSBuild/MSBuildTests.cs @@ -388,7 +388,7 @@ public void DesignTimeBuild() var xamlCStamp = IOPath.Combine(intermediateDirectory, "XamlC.stamp"); //The assembly should not be compiled - AssertDoesNotExist(assembly); + //AssertDoesNotExist(assembly); AssertDoesNotExist(xamlCStamp); //XamlC should be skipped //Build again, a full build diff --git a/src/Controls/tests/Xaml.UnitTests/XmlnsAggregattion.xaml b/src/Controls/tests/Xaml.UnitTests/XmlnsAggregattion.xaml new file mode 100644 index 000000000000..9f9cefcd4560 --- /dev/null +++ b/src/Controls/tests/Xaml.UnitTests/XmlnsAggregattion.xaml @@ -0,0 +1,11 @@ + + + \ No newline at end of file diff --git a/src/Controls/tests/Xaml.UnitTests/XmlnsAggregattion.xaml.cs b/src/Controls/tests/Xaml.UnitTests/XmlnsAggregattion.xaml.cs new file mode 100644 index 000000000000..0a78149d8a49 --- /dev/null +++ b/src/Controls/tests/Xaml.UnitTests/XmlnsAggregattion.xaml.cs @@ -0,0 +1,35 @@ +using Microsoft.Maui.ApplicationModel; +using Microsoft.Maui.Controls; +using Microsoft.Maui.Controls.Core.UnitTests; +using NUnit.Framework; + +[assembly: XmlnsDefinition("http://schemas.microsoft.com/dotnet/maui/global", "http://schemas.microsoft.com/dotnet/2021/maui")] + +namespace Microsoft.Maui.Controls.Xaml.UnitTests; + +public partial class XmlnsAggregattion : ContentPage +{ + public XmlnsAggregattion() => InitializeComponent(); + public XmlnsAggregattion(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 XamlWithAggregatedXmlns([Values] bool useCompiledXaml) + { + if (useCompiledXaml) + MockCompiler.Compile(typeof(XmlnsAggregattion)); + + var layout = new XmlnsAggregattion(useCompiledXaml); + Assert.That(layout.label.Text, Is.EqualTo("Welcome to .NET MAUI!")); + + } + } +} \ No newline at end of file diff --git a/src/Controls/tests/Xaml.UnitTests/XmlnsCollision.xaml b/src/Controls/tests/Xaml.UnitTests/XmlnsCollision.xaml new file mode 100644 index 000000000000..0c7a6499b74c --- /dev/null +++ b/src/Controls/tests/Xaml.UnitTests/XmlnsCollision.xaml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/src/Controls/tests/Xaml.UnitTests/XmlnsCollision.xaml.cs b/src/Controls/tests/Xaml.UnitTests/XmlnsCollision.xaml.cs new file mode 100644 index 000000000000..a5eca7ff50d2 --- /dev/null +++ b/src/Controls/tests/Xaml.UnitTests/XmlnsCollision.xaml.cs @@ -0,0 +1,66 @@ +using Microsoft.Maui.ApplicationModel; +using Microsoft.Maui.Controls; +using Microsoft.Maui.Controls.Build.Tasks; +using Microsoft.Maui.Controls.Core.UnitTests; +using NUnit.Framework; + +[assembly: XmlnsDefinition("http://companyone.com/schemas/toolkit", "CompanyOne.Controls")] +[assembly: XmlnsPrefix("c1", "http://companyone.com/schemas/toolkit")] +[assembly: XmlnsDefinition("http://companytwo.com/schemas/toolkit", "CompanyTwo.Controls")] +[assembly: XmlnsPrefix("c2", "http://companytwo.com/schemas/toolkit")] +[assembly: XmlnsDefinition("http://schemas.microsoft.com/dotnet/maui/global", "http://companyone.com/schemas/toolkit")] +[assembly: XmlnsDefinition("http://schemas.microsoft.com/dotnet/maui/global", "http://companytwo.com/schemas/toolkit")] + +namespace CompanyOne.Controls +{ + public class ConflictingLabel : Label + { + } +} + +namespace CompanyTwo.Controls +{ + public class ConflictingLabel : Label + { + } +} + +namespace Microsoft.Maui.Controls.Xaml.UnitTests +{ + [XamlCompilation(XamlCompilationOptions.Skip)] + public partial class XmlnsCollision : ContentPage + { + public XmlnsCollision() + { + InitializeComponent(); + } + public XmlnsCollision(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 ConflictInXmlns([Values] bool useCompiledXaml) + { + if (useCompiledXaml) + Assert.Throws(() => + { + MockCompiler.Compile(typeof(XmlnsCollision), out var hasLoggedErrors); + Assert.IsTrue(hasLoggedErrors); + }); + + else + Assert.Throws(() => + { + var layout = new XmlnsCollision(useCompiledXaml); + }); + } + } + } +} \ No newline at end of file