From a958adfa7f350d541d1f8afdb8c335054c381d45 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 4 Aug 2025 15:49:26 +0000 Subject: [PATCH 01/19] Initial plan From 60a2bfda3b6fd1f02b5a5fbc15dbed306c6bee2b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 4 Aug 2025 16:17:41 +0000 Subject: [PATCH 02/19] Fix NRT annotations in MarshalMethodsClassifier.cs Co-authored-by: jonathanpeppers <840039+jonathanpeppers@users.noreply.github.com> --- .../Utilities/MarshalMethodsClassifier.cs | 60 +++++++++++-------- 1 file changed, 34 insertions(+), 26 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsClassifier.cs b/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsClassifier.cs index 91fff9bf147..d5ad4ac0b5e 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsClassifier.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsClassifier.cs @@ -1,4 +1,4 @@ -#nullable disable +#nullable enable using System; using System.Collections.Generic; @@ -39,8 +39,8 @@ class MarshalMethodEntry : MethodEntry /// public MethodDefinition? NativeCallbackWrapper { get; set; } public MethodDefinition? Connector { get; } - public MethodDefinition? RegisteredMethod { get; } - public MethodDefinition? ImplementedMethod { get; } + public MethodDefinition RegisteredMethod { get; } + public MethodDefinition ImplementedMethod { get; } public FieldDefinition? CallbackField { get; } public string JniTypeName { get; } public string JniMethodName { get; } @@ -50,8 +50,8 @@ class MarshalMethodEntry : MethodEntry public MethodDefinition NativeCallback => NativeCallbackWrapper ?? nativeCallbackReal; public bool IsSpecial { get; } - public MarshalMethodEntry (TypeDefinition declaringType, MethodDefinition nativeCallback, MethodDefinition connector, MethodDefinition - registeredMethod, MethodDefinition implementedMethod, FieldDefinition callbackField, string jniTypeName, + public MarshalMethodEntry (TypeDefinition declaringType, MethodDefinition nativeCallback, MethodDefinition? connector, MethodDefinition + registeredMethod, MethodDefinition implementedMethod, FieldDefinition? callbackField, string jniTypeName, string jniName, string jniSignature, bool needsBlittableWorkaround) : base (declaringType) { @@ -85,7 +85,7 @@ public MarshalMethodEntry (MarshalMethodEntry other, MethodDefinition nativeCall string EnsureNonEmpty (string s, string argName) { - if (String.IsNullOrEmpty (s)) { + if (s.IsNullOrEmpty ()) { throw new ArgumentException ("must not be null or empty", argName); } @@ -104,8 +104,8 @@ sealed class ConvertedMarshalMethodEntry : MarshalMethodEntry { public MethodDefinition ConvertedNativeCallback { get; } - public ConvertedMarshalMethodEntry (TypeDefinition declaringType, MethodDefinition nativeCallback, MethodDefinition connector, MethodDefinition - registeredMethod, MethodDefinition implementedMethod, FieldDefinition callbackField, string jniTypeName, + public ConvertedMarshalMethodEntry (TypeDefinition declaringType, MethodDefinition nativeCallback, MethodDefinition? connector, MethodDefinition + registeredMethod, MethodDefinition implementedMethod, FieldDefinition? callbackField, string jniTypeName, string jniName, string jniSignature, bool needsBlittableWorkaround, MethodDefinition convertedNativeCallback) : base (declaringType, nativeCallback, connector, registeredMethod, implementedMethod, callbackField, jniTypeName, jniName, jniSignature, needsBlittableWorkaround) { @@ -133,8 +133,8 @@ class MarshalMethodsClassifier public sealed class ConnectorInfo { public string MethodName { get; } - public string TypeName { get; } - public AssemblyNameReference AssemblyName { get; } + public string? TypeName { get; } + public AssemblyNameReference? AssemblyName { get; } public ConnectorInfo (string spec) { @@ -200,7 +200,7 @@ string MapType (TypeReference typeRef) { string? typeName = null; if (!typeRef.IsGenericParameter && !typeRef.IsArray) { - TypeDefinition typeDef = cache.Resolve (typeRef); + TypeDefinition? typeDef = cache.Resolve (typeRef); if (typeDef == null) { throw new InvalidOperationException ($"Unable to resolve type '{typeRef.FullName}'"); } @@ -210,7 +210,7 @@ string MapType (TypeReference typeRef) } } - if (String.IsNullOrEmpty (typeName)) { + if (typeName.IsNullOrEmpty ()) { typeName = typeRef.FullName; } @@ -343,7 +343,7 @@ string GetAssemblyPathInfo (AssemblyDefinition? asmdef) } string? path = asmdef.MainModule.FileName; - if (String.IsNullOrEmpty (path)) { + if (path.IsNullOrEmpty ()) { path = "unknown"; } @@ -391,12 +391,12 @@ bool IsStandardHandler (TypeDefinition topType, ConnectorInfo connector, MethodD string nativeConvertedCallbackName = $"n_{callbackNameCore}_mm_wrapper"; string delegateFieldName = $"cb_{Char.ToLowerInvariant (callbackNameCore[0])}{callbackNameCore.Substring (1)}"; - TypeDefinition connectorDeclaringType = connector.AssemblyName == null ? registeredMethod.DeclaringType : FindType (resolver.Resolve (connector.AssemblyName), connector.TypeName); + TypeDefinition? connectorDeclaringType = connector.AssemblyName == null ? registeredMethod.DeclaringType : FindType (resolver.Resolve (connector.AssemblyName), connector.TypeName!); var ncbs = new NativeCallbackSignature (registeredMethod, log, tdCache); - MethodDefinition nativeCallbackMethod = FindMethod (connectorDeclaringType, nativeCallbackName, ncbs); + MethodDefinition? nativeCallbackMethod = FindMethod (connectorDeclaringType, nativeCallbackName, ncbs); if (nativeCallbackMethod == null) { - log.LogWarning ($"Unable to find native callback method '{nativeCallbackName}' in type '{connectorDeclaringType.FullName}', matching the '{registeredMethod.FullName}' signature (jniName: '{jniName}') {GetAssemblyPathInfo (connectorDeclaringType)}"); + log.LogWarning ($"Unable to find native callback method '{nativeCallbackName}' in type '{connectorDeclaringType?.FullName}', matching the '{registeredMethod.FullName}' signature (jniName: '{jniName}') {GetAssemblyPathInfo (connectorDeclaringType)}"); return false; } @@ -406,22 +406,22 @@ bool IsStandardHandler (TypeDefinition topType, ConnectorInfo connector, MethodD MethodDefinition? nativeConvertedCallbackMethod = FindMethod (connectorDeclaringType, nativeConvertedCallbackName, ncbs); - MethodDefinition connectorMethod = FindMethod (connectorDeclaringType, connectorName); + MethodDefinition? connectorMethod = FindMethod (connectorDeclaringType, connectorName); // If the marshal method has already been converted, the connector method will have been removed if (connectorMethod == null && nativeConvertedCallbackMethod == null) { - log.LogWarning ($"Connector method '{connectorName}' not found in type '{connectorDeclaringType.FullName}' {GetAssemblyPathInfo (connectorDeclaringType)}"); + log.LogWarning ($"Connector method '{connectorName}' not found in type '{connectorDeclaringType?.FullName}' {GetAssemblyPathInfo (connectorDeclaringType)}"); return false; } if (connectorMethod != null && !MonoAndroidHelper.StringEquals ("System.Delegate", connectorMethod.ReturnType.FullName)) { - log.LogWarning ($"Connector '{connectorName}' in type '{connectorDeclaringType.FullName}' has invalid return type, expected 'System.Delegate', found '{connectorMethod.ReturnType.FullName}' {GetAssemblyPathInfo (connectorDeclaringType)}"); + log.LogWarning ($"Connector '{connectorName}' in type '{connectorDeclaringType?.FullName}' has invalid return type, expected 'System.Delegate', found '{connectorMethod.ReturnType.FullName}' {GetAssemblyPathInfo (connectorDeclaringType)}"); return false; } // In the standard handler "pattern", the native callback backing field is private, static and thus in the same type // as the native callback. - FieldDefinition delegateField = FindField (nativeCallbackMethod.DeclaringType, delegateFieldName); + FieldDefinition? delegateField = FindField (nativeCallbackMethod.DeclaringType, delegateFieldName); if (delegateField != null) { if (!MonoAndroidHelper.StringEquals ("System.Delegate", delegateField.FieldType.FullName)) { log.LogWarning ($"delegate field '{delegateFieldName}' in type '{nativeCallbackMethod.DeclaringType.FullName}' has invalid type, expected 'System.Delegate', found '{delegateField.FieldType.FullName}' {GetAssemblyPathInfo (delegateField)}"); @@ -568,11 +568,11 @@ void WarnWhy (string why) } } - TypeDefinition FindType (AssemblyDefinition asm, string typeName) + TypeDefinition? FindType (AssemblyDefinition asm, string typeName) { foreach (ModuleDefinition md in asm.Modules) { foreach (TypeDefinition td in md.Types) { - TypeDefinition match = GetMatchingType (td); + TypeDefinition? match = GetMatchingType (td); if (match != null) { return match; } @@ -581,7 +581,7 @@ TypeDefinition FindType (AssemblyDefinition asm, string typeName) return null; - TypeDefinition GetMatchingType (TypeDefinition def) + TypeDefinition? GetMatchingType (TypeDefinition def) { if (MonoAndroidHelper.StringEquals (def.FullName, typeName)) { return def; @@ -591,7 +591,7 @@ TypeDefinition GetMatchingType (TypeDefinition def) return null; } - TypeDefinition ret; + TypeDefinition? ret; foreach (TypeDefinition nested in def.NestedTypes) { ret = GetMatchingType (nested); if (ret != null) { @@ -603,8 +603,12 @@ TypeDefinition GetMatchingType (TypeDefinition def) } } - MethodDefinition FindMethod (TypeDefinition type, string methodName, IMethodSignatureMatcher signatureMatcher = null) + MethodDefinition? FindMethod (TypeDefinition? type, string methodName, IMethodSignatureMatcher? signatureMatcher = null) { + if (type == null) { + return null; + } + foreach (MethodDefinition method in type.Methods) { if (!method.IsManaged || method.IsConstructor) { continue; @@ -626,8 +630,12 @@ MethodDefinition FindMethod (TypeDefinition type, string methodName, IMethodSign return FindMethod (tdCache.Resolve (type.BaseType), methodName, signatureMatcher); } - FieldDefinition FindField (TypeDefinition type, string fieldName, bool lookForInherited = false) + FieldDefinition? FindField (TypeDefinition? type, string fieldName, bool lookForInherited = false) { + if (type == null) { + return null; + } + foreach (FieldDefinition field in type.Fields) { if (MonoAndroidHelper.StringEquals (field.Name, fieldName)) { return field; From 70063957474cc106b31430d332e8828a14d57b5d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 4 Aug 2025 16:25:23 +0000 Subject: [PATCH 03/19] Fix NRT annotations in ManifestDocument.cs Co-authored-by: jonathanpeppers <840039+jonathanpeppers@users.noreply.github.com> --- .../Utilities/ManifestDocument.cs | 70 +++++++++---------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocument.cs b/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocument.cs index 934bbe55206..b422c13126e 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocument.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocument.cs @@ -1,4 +1,4 @@ -#nullable disable +#nullable enable using System; using System.Collections.Generic; @@ -81,44 +81,44 @@ internal class ManifestDocument { "category", "android.intent.category.LAUNCHER" }, }; - public string PackageName { get; set; } - public string ApplicationLabel { get; set; } - public string [] Placeholders { get; set; } - public List Assemblies { get; set; } - public IAssemblyResolver Resolver { get; set; } - public string SdkDir { get; set; } - public string TargetSdkVersion { get; set; } - public string MinSdkVersion { get; set; } + public string? PackageName { get; set; } + public string? ApplicationLabel { get; set; } + public string[]? Placeholders { get; set; } + public List? Assemblies { get; set; } + public IAssemblyResolver? Resolver { get; set; } + public string? SdkDir { get; set; } + public string? TargetSdkVersion { get; set; } + public string? MinSdkVersion { get; set; } public bool Debug { get; set; } public bool MultiDex { get; set; } public bool NeedsInternet { get; set; } public bool ForceExtractNativeLibs { get; set; } public bool ForceDebuggable { get; set; } - public string VersionName { get; set; } + public string? VersionName { get; set; } public IVersionResolver VersionResolver { get; set; } = new MonoAndroidHelperVersionResolver (); public AndroidRuntime AndroidRuntime { get; set; } - string versionCode; + string? versionCode; /// /// NOTE: this property modifies the underlying XDocument /// public string VersionCode { get { - XAttribute attr = doc.Root.Attribute (versionCodeAttributeName); + XAttribute? attr = doc.Root?.Attribute (versionCodeAttributeName); if (attr != null) { string code = attr.Value; - if (!string.IsNullOrEmpty (code)) + if (!code.IsNullOrEmpty ()) return code; } return "1"; } set { - doc.Root.SetAttributeValue (versionCodeAttributeName, versionCode = value); + doc.Root?.SetAttributeValue (versionCodeAttributeName, versionCode = value); } } - public bool HasVersionCode => doc.Root.Attribute (versionCodeAttributeName) != null; + public bool HasVersionCode => doc.Root?.Attribute (versionCodeAttributeName) != null; // If MinSdkVersionName can't be parsed, set it to XABuildConfig.AndroidMinimumDotNetApiLevel string TryParseMinSdkVersionName () @@ -130,7 +130,7 @@ string TryParseMinSdkVersionName () } public string GetMinimumSdk () { - var minAttr = doc.Root.Element ("uses-sdk")?.Attribute (androidNs + "minSdkVersion"); + var minAttr = doc.Root?.Element ("uses-sdk")?.Attribute (androidNs + "minSdkVersion"); if (minAttr == null) { return TryParseMinSdkVersionName (); } @@ -139,20 +139,20 @@ public string GetMinimumSdk () { public string GetTargetSdk () { - var targetAttr = doc.Root.Element ("uses-sdk")?.Attribute (androidNs + "targetSdkVersion"); + var targetAttr = doc.Root?.Element ("uses-sdk")?.Attribute (androidNs + "targetSdkVersion"); if (targetAttr == null) { return TargetSdkVersionName; } return targetAttr.Value; } - public ManifestDocument (string templateFilename) : base () + public ManifestDocument (string? templateFilename) : base () { Assemblies = new List (); attName = androidNs + "name"; - if (!string.IsNullOrEmpty (templateFilename)) { + if (!templateFilename.IsNullOrEmpty ()) { doc = XDocument.Load (templateFilename, LoadOptions.SetLineInfo); InjectAutoGeneratedComment (doc.Root, templateFilename); AndroidResource.UpdateXmlResource (doc.Root); @@ -165,7 +165,7 @@ public ManifestDocument (string templateFilename) : base () string TargetSdkVersionName => VersionResolver.GetIdFromApiLevel (TargetSdkVersion); string MinSdkVersionName => - string.IsNullOrEmpty (MinSdkVersion) ? + MinSdkVersion.IsNullOrEmpty () ? TargetSdkVersionName : VersionResolver.GetIdFromApiLevel (MinSdkVersion); @@ -178,15 +178,15 @@ string ToFullyQualifiedName (string typeName) return PackageName + "." + typeName; } - XElement GetActivityWithName (XElement app, string name) + XElement? GetActivityWithName (XElement app, string name) { name = ToFullyQualifiedName (name); return app.Elements ("activity").FirstOrDefault (e => ToFullyQualifiedName ((string) e.Attribute (androidNs + "name")) == name); } - void InjectAutoGeneratedComment (XElement root, string templateFilename = "") + void InjectAutoGeneratedComment (XElement? root, string templateFilename = "") { - if (root.PreviousNode is XComment) + if (root?.PreviousNode is XComment) return; root.AddBeforeSelf (new XComment (string.Format (Xamarin.Android.Tasks.Properties.Resources.XA_Manifest_AutoGenerated_Header, templateFilename))); } @@ -265,9 +265,9 @@ public IList Merge (TaskLoggingHelper log, TypeDefinitionCache cache, Li if (manifest == null || manifest.Name != "manifest") throw new Exception ("Root element must be 'manifest'"); - var manifest_package = (string) manifest.Attribute ("package"); - if (string.IsNullOrEmpty (manifest_package)) { - if (!string.IsNullOrEmpty (PackageName)) { + var manifest_package = (string?) manifest.Attribute ("package"); + if (manifest_package.IsNullOrEmpty ()) { + if (!PackageName.IsNullOrEmpty ()) { manifest.SetAttributeValue ("package", PackageName); } } else { @@ -283,22 +283,22 @@ public IList Merge (TaskLoggingHelper log, TypeDefinitionCache cache, Li if (manifest.Attribute (versionCodeAttributeName) == null) { manifest.SetAttributeValue (versionCodeAttributeName, - string.IsNullOrEmpty (versionCode) ? "1" : versionCode); + versionCode.IsNullOrEmpty () ? "1" : versionCode); } if (manifest.Attribute (androidNs + "versionName") == null) { manifest.SetAttributeValue (androidNs + "versionName", - string.IsNullOrEmpty (VersionName) ? "1.0" : VersionName); + VersionName.IsNullOrEmpty () ? "1.0" : VersionName); } app = CreateApplicationElement (manifest, applicationClass, subclasses, cache); - if (app.Attribute (androidNs + "label") == null && !string.IsNullOrEmpty (ApplicationLabel)) + if (app.Attribute (androidNs + "label") == null && !ApplicationLabel.IsNullOrEmpty ()) app.SetAttributeValue (androidNs + "label", ApplicationLabel); var existingTypes = new HashSet ( - app.Descendants ().Select (a => (string) a.Attribute (attName)).Where (v => v != null)); + app.Descendants ().Select (a => (string?) a.Attribute (attName)).Where (v => v != null)!); - if (!string.IsNullOrEmpty (bundledWearApplicationName)) { + if (!bundledWearApplicationName.IsNullOrEmpty ()) { if (!app.Elements ("meta-data").Any (e => e.Attributes (androidNs + "name").Any (a => a.Value == bundledWearApplicationName))) app.Add (new XElement ("meta-data", new XAttribute (androidNs + "name", "com.google.android.wearable.beta.app"), new XAttribute (androidNs + "resource", "@xml/wearable_app_desc"))); } @@ -931,7 +931,7 @@ void AddUsesFeatures (XElement application, TypeDefinitionCache cache) // Add unique features by Name or glESVersion to the manifest foreach (var feature in assemblyAttrs) { - if (!string.IsNullOrEmpty(feature.Name) && feature.GLESVersion == 0) { + if (!feature.Name.IsNullOrEmpty() && feature.GLESVersion == 0) { if (!application.Parent.Descendants ("uses-feature").Any (x => (string)x.Attribute (attName) == feature.Name)) { application.AddBeforeSelf (feature.ToElement (PackageName, cache)); } @@ -1024,7 +1024,7 @@ public void Save (Action logCodedWarning, TextWriter stream, boo } finally { MemoryStreamPool.Shared.Return (ms); } - if (!string.IsNullOrEmpty (PackageName)) + if (!PackageName.IsNullOrEmpty ()) s = s.Replace ("${applicationId}", PackageName); s = ReplacePlaceholders (Placeholders, s, logCodedWarning); stream.Write (s); @@ -1080,7 +1080,7 @@ internal static string ReplacePlaceholders (string [] placeholders, string text, public void SetAbi (string abi) { int code = 1; - if (!string.IsNullOrEmpty (VersionCode)) { + if (!VersionCode.IsNullOrEmpty ()) { code = Convert.ToInt32 (VersionCode); if (code > maxVersionCode || code < 0) throw new ArgumentOutOfRangeException ("VersionCode", $"VersionCode is outside 0, {maxVersionCode} interval"); @@ -1117,7 +1117,7 @@ public void CalculateVersionCode (string currentAbi, string versionCodePattern, continue; kvp.Add (keyValue [0], val); } - if (!kvp.ContainsKey ("abi") && !string.IsNullOrEmpty (currentAbi)) + if (!kvp.ContainsKey ("abi") && !currentAbi.IsNullOrEmpty ()) kvp.Add ("abi", GetAbiCode (currentAbi)); if (!kvp.ContainsKey ("versionCode")) { if (int.TryParse (VersionCode, out int parsedCode)) { From b122355a5d42755c835a1e035533bd064de072ff Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 4 Aug 2025 16:30:48 +0000 Subject: [PATCH 04/19] Fix NRT annotations in ManifestDocumentElement.cs Co-authored-by: jonathanpeppers <840039+jonathanpeppers@users.noreply.github.com> --- .../Utilities/ManifestDocumentElement.cs | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocumentElement.cs b/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocumentElement.cs index 614019ec952..f446237858e 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocumentElement.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocumentElement.cs @@ -1,4 +1,4 @@ -#nullable disable +#nullable enable using System; using System.Collections; @@ -47,10 +47,10 @@ public static TypeDefinition ResolveType (string type, ICustomAttributeProvider int c = type.IndexOf (','); string typeName = c < 0 ? type : type.Substring (0, c); - string assmName = c < 0 ? null : type.Substring (c+1); + string? assmName = c < 0 ? null : type.Substring (c+1); - var assmNameRef = AssemblyNameReference.Parse (assmName); - var assembly = assmName == null ? null : resolver.Resolve (assmNameRef); + var assmNameRef = assmName != null ? AssemblyNameReference.Parse (assmName) : null; + var assembly = assmName == null ? null : resolver.Resolve (assmNameRef!); if (assembly == null) { assembly = provider as AssemblyDefinition; if (assembly == null) { @@ -116,7 +116,7 @@ public void Add (string member, string attributeName, Action setter, }); } - public ICollection Load (T value, CustomAttribute attribute, TypeDefinitionCache cache) + public ICollection? Load (T value, CustomAttribute? attribute, TypeDefinitionCache cache) { if (attribute == null) return null; @@ -134,7 +134,7 @@ public ICollection Load (T value, CustomAttribute attribute, TypeDefinit } public XElement ToElement (T value, ICollection specified, string packageName, TypeDefinitionCache cache, - ICustomAttributeProvider provider = null, IAssemblyResolver resolver = null, int targetSdkVersion = 0) + ICustomAttributeProvider? provider = null, IAssemblyResolver? resolver = null, int targetSdkVersion = 0) { var r = new XElement (Element, specified.OrderBy (e => e) @@ -144,8 +144,8 @@ public XElement ToElement (T value, ICollection specified, string packag return r; } - XAttribute ToAttribute (string name, T value, string packageName, - ICustomAttributeProvider provider, IAssemblyResolver resolver, TypeDefinitionCache cache, int targetSdkVersion = 0) + XAttribute? ToAttribute (string name, T value, string packageName, + ICustomAttributeProvider? provider, IAssemblyResolver? resolver, TypeDefinitionCache cache, int targetSdkVersion = 0) { if (!Mappings.ContainsKey (name)) throw new ArgumentException ("Invalid attribute name: " + name); @@ -160,7 +160,7 @@ XAttribute ToAttribute (string name, T value, string packageName, return new XAttribute (ManifestDocument.AndroidXmlNamespace + m.AttributeName, v); } - string ToAttributeValue (string name, T value, ICustomAttributeProvider provider, IAssemblyResolver resolver, TypeDefinitionCache cache, int targetSdkVersion = 0) + string? ToAttributeValue (string name, T value, ICustomAttributeProvider? provider, IAssemblyResolver? resolver, TypeDefinitionCache cache, int targetSdkVersion = 0) { var m = Mappings [name]; if (m.AttributeValue != null) @@ -180,7 +180,7 @@ string ToAttributeValue (string name, T value, ICustomAttributeProvider provider return c (v, provider, resolver, targetSdkVersion, cache); } - static readonly Dictionary> ValueConverters = new () { + static readonly Dictionary> ValueConverters = new () { { typeof (bool), (value, p, r, v, c) => ToString ((bool) value) }, { typeof (int), (value, p, r, v, c) => value.ToString () }, { typeof (float), (value, p, r, v, c) => value.ToString () }, @@ -366,9 +366,9 @@ static string ToString (WindowRotationAnimation value) } } - static string ToString (string value, ICustomAttributeProvider provider, IAssemblyResolver resolver, TypeDefinitionCache cache) + static string ToString (string value, ICustomAttributeProvider? provider, IAssemblyResolver? resolver, TypeDefinitionCache cache) { - var typeDef = ResolveType (value, provider, resolver); + var typeDef = ResolveType (value, provider!, resolver!); return ToString (typeDef, cache); } From 48e3a45223e48ea1c7e74e17d9b09214e3e1890b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 4 Aug 2025 16:34:13 +0000 Subject: [PATCH 05/19] Fix NRT annotations in TypeMapGenerator.cs (basic setup) Co-authored-by: jonathanpeppers <840039+jonathanpeppers@users.noreply.github.com> --- .../Utilities/TypeMapGenerator.cs | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/TypeMapGenerator.cs b/src/Xamarin.Android.Build.Tasks/Utilities/TypeMapGenerator.cs index 3b47371ae8d..c03f78aaa41 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/TypeMapGenerator.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/TypeMapGenerator.cs @@ -1,4 +1,4 @@ -#nullable disable +#nullable enable using System; using System.Collections.Generic; @@ -40,8 +40,8 @@ public int Compare (ModuleReleaseData left, ModuleReleaseData right) internal sealed class TypeMapReleaseEntry { - public string JavaName; - public string ManagedTypeName; + public string JavaName = ""; + public string ManagedTypeName = ""; public uint Token; public int ModuleIndex = -1; public bool SkipInJavaToManaged; @@ -50,26 +50,26 @@ internal sealed class TypeMapReleaseEntry internal sealed class ModuleReleaseData { public Guid Mvid; - public byte[] MvidBytes; - public TypeMapReleaseEntry[] Types; - public List DuplicateTypes; - public string AssemblyName; + public byte[] MvidBytes = []; + public TypeMapReleaseEntry[] Types = []; + public List DuplicateTypes = []; + public string AssemblyName = ""; - public Dictionary TypesScratch; + public Dictionary TypesScratch = []; } internal sealed class TypeMapDebugEntry { - public string JavaName; - public string ManagedName; + public string JavaName = ""; + public string ManagedName = ""; public uint ManagedTypeTokenId; public bool SkipInJavaToManaged; - public TypeMapDebugEntry DuplicateForJavaToManaged; - public string AssemblyName; + public TypeMapDebugEntry? DuplicateForJavaToManaged; + public string AssemblyName = ""; // This field is only used by the Cecil adapter for temp storage while reading. // It is not used to create the typemap. - public TypeDefinition TypeDefinition; + public TypeDefinition? TypeDefinition; // These fields are only used by the XML adapter for temp storage while reading. // It is not used to create the typemap. @@ -88,14 +88,14 @@ public override string ToString () internal sealed class TypeMapDebugAssembly { public Guid MVID; - public byte[] MVIDBytes; - public string Name; + public byte[] MVIDBytes = []; + public string Name = ""; } internal sealed class TypeMapDebugDataSets { - public List JavaToManaged ; - public List ManagedToJava; + public List JavaToManaged = []; + public List ManagedToJava = []; public List? UniqueAssemblies; } @@ -103,8 +103,8 @@ internal sealed class TypeMapDebugDataSets internal sealed class ModuleDebugData { public uint EntryCount; - public List JavaToManagedMap; - public List ManagedToJavaMap; + public List JavaToManagedMap = []; + public List ManagedToJavaMap = []; public List? UniqueAssemblies; } @@ -138,7 +138,7 @@ public TypeMapGenerator (TaskLoggingHelper log, ITypeMapGeneratorAdapter state, public void Generate (bool debugBuild, bool skipJniAddNativeMethodRegistrationAttributeScan, string outputDirectory) { - if (String.IsNullOrEmpty (outputDirectory)) { + if (outputDirectory.IsNullOrEmpty ()) { throw new ArgumentException ("must not be null or empty", nameof (outputDirectory)); } Directory.CreateDirectory (outputDirectory); From c7b472e32db132b93e49fa328394da66d5fd00f1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 4 Aug 2025 16:40:00 +0000 Subject: [PATCH 06/19] Fix NRT annotations in several LlvmIrGenerator files Co-authored-by: jonathanpeppers <840039+jonathanpeppers@users.noreply.github.com> --- .../Utilities/LlvmIrGenerator/LlvmIrInstructions.cs | 12 ++++++------ .../Utilities/LlvmIrGenerator/StructureInfo.cs | 2 +- .../Utilities/LlvmIrGenerator/StructureInstance.cs | 2 +- .../Utilities/LlvmIrGenerator/StructureMemberInfo.cs | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrInstructions.cs b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrInstructions.cs index 69dee1b5659..972c07bc87f 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrInstructions.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrInstructions.cs @@ -1,4 +1,4 @@ -#nullable disable +#nullable enable using System; using System.Collections.Generic; @@ -19,7 +19,7 @@ abstract class LlvmIrInstruction : LlvmIrFunctionBodyItem protected LlvmIrInstruction (string mnemonic) { - if (String.IsNullOrEmpty (mnemonic)) { + if (mnemonic.IsNullOrEmpty ()) { throw new ArgumentException ("must not be null or empty", nameof (mnemonic)); } @@ -262,7 +262,7 @@ protected override void WritePreamble (GeneratorWriteContext context) _ => throw new InvalidOperationException ($"Internal error: call marker '{CallMarker}' not supported"), }; - if (!String.IsNullOrEmpty (callMarker)) { + if (!callMarker.IsNullOrEmpty ()) { context.Output.Write (callMarker); context.Output.Write (' '); } @@ -708,7 +708,7 @@ public Switch (LlvmIrVariable value, LlvmIrFunctionLabelItem defaultDest, string this.value = value; this.defaultDest = defaultDest; this.automaticLabelPrefix = automaticLabelPrefix; - if (!String.IsNullOrEmpty (automaticLabelPrefix)) { + if (!automaticLabelPrefix.IsNullOrEmpty ()) { items = new (); } } @@ -730,7 +730,7 @@ protected override void WriteBody (GeneratorWriteContext context) context.Generator.WriteValue (context, value.Type, constant); context.Output.Write (", label %"); context.Output.Write (label.Name); - if (!String.IsNullOrEmpty (comment)) { + if (!comment.IsNullOrEmpty ()) { context.Output.Write (' '); context.Generator.WriteCommentLine (context, comment); } else { @@ -752,7 +752,7 @@ void EnsureValidity (LlvmIrFunctionLabelItem? dest) if (dest != null) { return; } - if (String.IsNullOrEmpty (automaticLabelPrefix)) { + if (automaticLabelPrefix.IsNullOrEmpty ()) { throw new InvalidOperationException ($"Internal error: automatic label management requested, but prefix not defined"); } } diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/StructureInfo.cs b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/StructureInfo.cs index b4e6daf7831..268630b075f 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/StructureInfo.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/StructureInfo.cs @@ -1,4 +1,4 @@ -#nullable disable +#nullable enable using System; using System.Collections.Generic; diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/StructureInstance.cs b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/StructureInstance.cs index f5bdf4874b8..58cfed3fe05 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/StructureInstance.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/StructureInstance.cs @@ -1,4 +1,4 @@ -#nullable disable +#nullable enable using System; diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/StructureMemberInfo.cs b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/StructureMemberInfo.cs index 8336e921372..6a524d97a7e 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/StructureMemberInfo.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/StructureMemberInfo.cs @@ -1,4 +1,4 @@ -#nullable disable +#nullable enable using System; using System.Reflection; @@ -38,7 +38,7 @@ sealed class StructureMemberInfo public string MappedName { get { string? name = Info.GetOverriddenName (typeCache); - return String.IsNullOrEmpty (name) ? Info.Name : name; + return name.IsNullOrEmpty () ? Info.Name : name; } } From d2e73546027c49601b9dce0ab0d3a976da1292cc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 4 Aug 2025 16:44:18 +0000 Subject: [PATCH 07/19] Fix NRT annotations in remaining LlvmIrGenerator files Co-authored-by: jonathanpeppers <840039+jonathanpeppers@users.noreply.github.com> --- .../Utilities/LlvmIrGenerator/LlvmIrFunctionBody.cs | 10 +++++----- .../Utilities/LlvmIrGenerator/LlvmIrGenerator.cs | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrFunctionBody.cs b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrFunctionBody.cs index c9af647f1b1..fefb1091b60 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrFunctionBody.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrFunctionBody.cs @@ -1,4 +1,4 @@ -#nullable disable +#nullable enable using System; using System.Collections.Generic; @@ -24,7 +24,7 @@ abstract class LlvmIrFunctionBodyItem public void Write (GeneratorWriteContext context, LlvmIrGenerator generator) { DoWrite (context, generator); - if (!String.IsNullOrEmpty (Comment)) { + if (!Comment.IsNullOrEmpty ()) { context.Output.Write (' '); generator.WriteComment (context, Comment); } @@ -44,14 +44,14 @@ abstract class LlvmIrFunctionLocalItem : LlvmIrFunctionBodyItem public string Name { get { - if (String.IsNullOrEmpty (name)) { + if (name.IsNullOrEmpty ()) { throw new InvalidOperationException ("Internal error: name hasn't been set yet"); } return name; } protected set { - if (String.IsNullOrEmpty (value)) { + if (value.IsNullOrEmpty ()) { throw new InvalidOperationException ("Internal error: value must not be null or empty"); } name = value; @@ -84,7 +84,7 @@ protected void SetName (ulong num) Name = num.ToString (CultureInfo.InvariantCulture); } - protected bool NameIsSet () => !String.IsNullOrEmpty (name); + protected bool NameIsSet () => !name.IsNullOrEmpty (); } class LlvmIrFunctionLabelItem : LlvmIrFunctionLocalItem diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrGenerator.cs b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrGenerator.cs index dc3d7c37678..28fb072f80f 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrGenerator.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrGenerator.cs @@ -1,4 +1,4 @@ -#nullable disable +#nullable enable using System; using System.Buffers; From 869cd6b58e03b50731dc6c05beed314e9c06bfbc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 4 Aug 2025 17:31:38 +0000 Subject: [PATCH 08/19] Make TypeDefinition parameter non-nullable in FindMethod and FindField Co-authored-by: jonathanpeppers <840039+jonathanpeppers@users.noreply.github.com> --- .../Utilities/MarshalMethodsClassifier.cs | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsClassifier.cs b/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsClassifier.cs index d5ad4ac0b5e..875607e2608 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsClassifier.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsClassifier.cs @@ -392,6 +392,10 @@ bool IsStandardHandler (TypeDefinition topType, ConnectorInfo connector, MethodD string delegateFieldName = $"cb_{Char.ToLowerInvariant (callbackNameCore[0])}{callbackNameCore.Substring (1)}"; TypeDefinition? connectorDeclaringType = connector.AssemblyName == null ? registeredMethod.DeclaringType : FindType (resolver.Resolve (connector.AssemblyName), connector.TypeName!); + if (connectorDeclaringType == null) { + log.LogWarning ($"Unable to find connector declaring type '{connector.TypeName}' in assembly '{connector.AssemblyName}'"); + return false; + } var ncbs = new NativeCallbackSignature (registeredMethod, log, tdCache); MethodDefinition? nativeCallbackMethod = FindMethod (connectorDeclaringType, nativeCallbackName, ncbs); @@ -603,11 +607,8 @@ void WarnWhy (string why) } } - MethodDefinition? FindMethod (TypeDefinition? type, string methodName, IMethodSignatureMatcher? signatureMatcher = null) + MethodDefinition? FindMethod (TypeDefinition type, string methodName, IMethodSignatureMatcher? signatureMatcher = null) { - if (type == null) { - return null; - } foreach (MethodDefinition method in type.Methods) { if (!method.IsManaged || method.IsConstructor) { @@ -627,14 +628,16 @@ void WarnWhy (string why) return null; } - return FindMethod (tdCache.Resolve (type.BaseType), methodName, signatureMatcher); + TypeDefinition? baseType = tdCache.Resolve (type.BaseType); + if (baseType == null) { + return null; + } + + return FindMethod (baseType, methodName, signatureMatcher); } - FieldDefinition? FindField (TypeDefinition? type, string fieldName, bool lookForInherited = false) + FieldDefinition? FindField (TypeDefinition type, string fieldName, bool lookForInherited = false) { - if (type == null) { - return null; - } foreach (FieldDefinition field in type.Fields) { if (MonoAndroidHelper.StringEquals (field.Name, fieldName)) { @@ -646,7 +649,12 @@ void WarnWhy (string why) return null; } - return FindField (tdCache.Resolve (type.BaseType), fieldName, lookForInherited); + TypeDefinition? baseType = tdCache.Resolve (type.BaseType); + if (baseType == null) { + return null; + } + + return FindField (baseType, fieldName, lookForInherited); } } } From 741008624a64e15b360405c8ba41864d199d2249 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 4 Aug 2025 19:25:54 +0000 Subject: [PATCH 09/19] Fix core NRT issues in MarshalMethodsClassifier, ManifestDocument, and StructureInfo Co-authored-by: jonathanpeppers <840039+jonathanpeppers@users.noreply.github.com> --- .../LlvmIrGenerator/StructureInfo.cs | 4 +-- .../Utilities/ManifestDocument.cs | 30 +++++++++++-------- .../Utilities/ManifestDocumentElement.cs | 18 +++++------ .../Utilities/MarshalMethodsClassifier.cs | 15 +++++++--- 4 files changed, 40 insertions(+), 27 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/StructureInfo.cs b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/StructureInfo.cs index 268630b075f..132818bee09 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/StructureInfo.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/StructureInfo.cs @@ -37,7 +37,7 @@ public StructureInfo (LlvmIrModule module, Type type, LlvmIrTypeCache cache) public string? GetCommentFromProvider (StructureMemberInfo smi, StructureInstance instance) { - if (DataProvider == null || !smi.Info.UsesDataProvider (cache)) { + if (DataProvider == null || !smi.Info.UsesDataProvider (cache) || instance.Obj == null) { return null; } @@ -51,7 +51,7 @@ public StructureInfo (LlvmIrModule module, Type type, LlvmIrTypeCache cache) public ulong GetBufferSizeFromProvider (StructureMemberInfo smi, StructureInstance instance) { - if (DataProvider == null) { + if (DataProvider == null || instance.Obj == null) { return 0; } diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocument.cs b/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocument.cs index b422c13126e..1015852237b 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocument.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocument.cs @@ -44,7 +44,7 @@ internal class ManifestDocument XName attName; - XElement app; + XElement? app; // the elements and attributes which we apply the "." -> PackageName replacement on static readonly Dictionary ManifestAttributeFixups = new Dictionary { @@ -137,7 +137,7 @@ public string GetMinimumSdk () { return minAttr.Value; } - public string GetTargetSdk () + public string? GetTargetSdk () { var targetAttr = doc.Root?.Element ("uses-sdk")?.Attribute (androidNs + "targetSdkVersion"); if (targetAttr == null) { @@ -162,12 +162,12 @@ public ManifestDocument (string? templateFilename) : base () } } - string TargetSdkVersionName => VersionResolver.GetIdFromApiLevel (TargetSdkVersion); + string? TargetSdkVersionName => TargetSdkVersion != null ? VersionResolver.GetIdFromApiLevel (TargetSdkVersion) : null; - string MinSdkVersionName => + string? MinSdkVersionName => MinSdkVersion.IsNullOrEmpty () ? TargetSdkVersionName : - VersionResolver.GetIdFromApiLevel (MinSdkVersion); + (MinSdkVersion != null ? VersionResolver.GetIdFromApiLevel (MinSdkVersion) : null); string ToFullyQualifiedName (string typeName) { @@ -188,7 +188,8 @@ void InjectAutoGeneratedComment (XElement? root, string templateFilename = "") { if (root?.PreviousNode is XComment) return; - root.AddBeforeSelf (new XComment (string.Format (Xamarin.Android.Tasks.Properties.Resources.XA_Manifest_AutoGenerated_Header, templateFilename))); + if (root != null) + root.AddBeforeSelf (new XComment (string.Format (Xamarin.Android.Tasks.Properties.Resources.XA_Manifest_AutoGenerated_Header, templateFilename))); } void ReorderElements (XElement app) @@ -245,10 +246,10 @@ void ReorderActivityAliases (TaskLoggingHelper log, XElement app) var aliases = app.Elements ("activity-alias").ToList (); foreach (XElement alias in aliases) { - XAttribute attr = alias.Attribute (androidNs + "targetActivity"); + XAttribute? attr = alias.Attribute (androidNs + "targetActivity"); if (attr == null) continue; - XElement activity = GetActivityWithName (app, attr.Value); + XElement? activity = GetActivityWithName (app, attr.Value); if (activity != null) { alias.Remove (); activity.AddAfterSelf (alias); @@ -275,7 +276,8 @@ public IList Merge (TaskLoggingHelper log, TypeDefinitionCache cache, Li } if (PackageName.Contains ("${")) { // placeholder detected - PackageName = ReplacePlaceholders (Placeholders, PackageName); + if (Placeholders != null) + PackageName = ReplacePlaceholders (Placeholders, PackageName); manifest.SetAttributeValue ("package", PackageName); } @@ -741,6 +743,9 @@ bool IsMainLauncher (XElement intentFilter) /// public bool DirectBootAware () { + if (app == null) + return false; + var processAttrName = androidNs.GetName ("directBootAware"); var appAttr = app.Attribute (processAttrName); bool value; @@ -1026,11 +1031,12 @@ public void Save (Action logCodedWarning, TextWriter stream, boo } if (!PackageName.IsNullOrEmpty ()) s = s.Replace ("${applicationId}", PackageName); - s = ReplacePlaceholders (Placeholders, s, logCodedWarning); + if (Placeholders != null) + s = ReplacePlaceholders (Placeholders, s, logCodedWarning); stream.Write (s); } - public string GetLaunchableActivityName () + public string? GetLaunchableActivityName () { var application = doc.Root.Descendants ("application").FirstOrDefault (); var aName = androidNs + "name"; @@ -1061,7 +1067,7 @@ static int GetAbiCode (string abi) } } - internal static string ReplacePlaceholders (string [] placeholders, string text, Action logCodedWarning = null) + internal static string ReplacePlaceholders (string [] placeholders, string text, Action? logCodedWarning = null) { string result = text; if (placeholders == null) diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocumentElement.cs b/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocumentElement.cs index f446237858e..425e4fb908b 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocumentElement.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocumentElement.cs @@ -78,23 +78,23 @@ public ManifestDocumentElement (string element) public readonly string Element; class MappingInfo { - public string AttributeName; - public Func Getter; + public string AttributeName = ""; + public Func Getter = _ => null; public Action? Setter; - public Type MemberType; - public Func AttributeValue; - public Func AttributeValue2; + public Type MemberType = typeof(object); + public Func? AttributeValue; + public Func? AttributeValue2; } readonly IDictionary Mappings = new Dictionary (); - public void Add (string member, string attributeName, Func getter, Action? setter, Type memberType = null) + public void Add (string member, string attributeName, Func getter, Action? setter, Type? memberType = null) { Mappings.Add (member, new MappingInfo { AttributeName = attributeName, Getter = getter, Setter = setter, - MemberType = memberType, + MemberType = memberType ?? typeof(object), }); } @@ -153,7 +153,7 @@ public XElement ToElement (T value, ICollection specified, string packag if (m.AttributeName == null) return null; - string v = ToAttributeValue (name, value, provider, resolver, cache, targetSdkVersion); + string? v = ToAttributeValue (name, value, provider, resolver, cache, targetSdkVersion); if (v == null) return null; v = v.Replace ("@PACKAGE_NAME@", packageName); @@ -165,7 +165,7 @@ public XElement ToElement (T value, ICollection specified, string packag var m = Mappings [name]; if (m.AttributeValue != null) return m.AttributeValue (value); - if (m.AttributeValue2 != null) + if (m.AttributeValue2 != null && provider != null && resolver != null) return m.AttributeValue2 (value, provider, resolver, cache); if (m.Getter == null) diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsClassifier.cs b/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsClassifier.cs index 875607e2608..0eb8b8fc22a 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsClassifier.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsClassifier.cs @@ -39,8 +39,8 @@ class MarshalMethodEntry : MethodEntry /// public MethodDefinition? NativeCallbackWrapper { get; set; } public MethodDefinition? Connector { get; } - public MethodDefinition RegisteredMethod { get; } - public MethodDefinition ImplementedMethod { get; } + public MethodDefinition? RegisteredMethod { get; } + public MethodDefinition? ImplementedMethod { get; } public FieldDefinition? CallbackField { get; } public string JniTypeName { get; } public string JniMethodName { get; } @@ -71,6 +71,10 @@ public MarshalMethodEntry (TypeDefinition declaringType, MethodDefinition native : base (declaringType) { nativeCallbackReal = nativeCallback ?? throw new ArgumentNullException (nameof (nativeCallback)); + Connector = null; + RegisteredMethod = null; + ImplementedMethod = null; + CallbackField = null; JniTypeName = EnsureNonEmpty (jniTypeName, nameof (jniTypeName)); JniMethodName = EnsureNonEmpty (jniName, nameof (jniName)); JniMethodSignature = EnsureNonEmpty (jniSignature, nameof (jniSignature)); @@ -78,8 +82,8 @@ public MarshalMethodEntry (TypeDefinition declaringType, MethodDefinition native } public MarshalMethodEntry (MarshalMethodEntry other, MethodDefinition nativeCallback) - : this (other.DeclaringType, nativeCallback, other.Connector, other.RegisteredMethod, - other.ImplementedMethod, other.CallbackField, other.JniTypeName, other.JniMethodName, + : this (other.DeclaringType, nativeCallback, other.Connector, other.RegisteredMethod ?? throw new ArgumentException("Cannot copy from special MarshalMethodEntry"), + other.ImplementedMethod ?? throw new ArgumentException("Cannot copy from special MarshalMethodEntry"), other.CallbackField, other.JniTypeName, other.JniMethodName, other.JniMethodSignature, other.NeedsBlittableWorkaround) {} @@ -94,6 +98,9 @@ string EnsureNonEmpty (string s, string argName) public string GetStoreMethodKey (TypeDefinitionCache tdCache) { + if (RegisteredMethod == null) + throw new InvalidOperationException ("RegisteredMethod is null"); + MethodDefinition registeredMethod = RegisteredMethod; string typeName = registeredMethod.DeclaringType.FullName.Replace ('/', '+'); return $"{typeName}, {registeredMethod.DeclaringType.GetPartialAssemblyName (tdCache)}\t{registeredMethod.Name}"; From 3110aa0153357c03be5cb7aaa17bd4e2cff65e36 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 4 Aug 2025 19:38:16 +0000 Subject: [PATCH 10/19] Fix additional NRT issues in TypeMap, StructureInstance, and LlvmIr files Co-authored-by: jonathanpeppers <840039+jonathanpeppers@users.noreply.github.com> --- .../LlvmIrGenerator/LlvmIrInstructions.cs | 24 ++++++++++--------- .../LlvmIrGenerator/StructureInstance.cs | 4 ++-- .../LlvmIrGenerator/StructureMemberInfo.cs | 9 ++++--- ...halMethodsNativeAssemblyGeneratorMonoVM.cs | 4 ++-- .../Utilities/TypeMapCecilAdapter.cs | 3 ++- .../Utilities/TypeMapGenerator.cs | 4 ++-- ...eMappingDebugNativeAssemblyGeneratorCLR.cs | 10 ++++---- 7 files changed, 32 insertions(+), 26 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrInstructions.cs b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrInstructions.cs index 972c07bc87f..61f46ac5bbb 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrInstructions.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrInstructions.cs @@ -723,18 +723,20 @@ protected override void WriteBody (GeneratorWriteContext context) context.Output.Write (defaultDest.Name); context.Output.WriteLine (" ["); context.IncreaseIndent (); - foreach ((T constant, LlvmIrFunctionLabelItem label, string? comment) in items) { - context.Output.Write (context.CurrentIndent); - context.Output.Write (irType); - context.Output.Write (' '); - context.Generator.WriteValue (context, value.Type, constant); - context.Output.Write (", label %"); - context.Output.Write (label.Name); - if (!comment.IsNullOrEmpty ()) { + if (items != null) { + foreach ((T constant, LlvmIrFunctionLabelItem label, string? comment) in items) { + context.Output.Write (context.CurrentIndent); + context.Output.Write (irType); context.Output.Write (' '); - context.Generator.WriteCommentLine (context, comment); - } else { - context.Output.WriteLine (); + context.Generator.WriteValue (context, value.Type, constant); + context.Output.Write (", label %"); + context.Output.Write (label.Name); + if (!comment.IsNullOrEmpty ()) { + context.Output.Write (' '); + context.Generator.WriteCommentLine (context, comment); + } else { + context.Output.WriteLine (); + } } } context.DecreaseIndent (); diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/StructureInstance.cs b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/StructureInstance.cs index 58cfed3fe05..5c82915263f 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/StructureInstance.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/StructureInstance.cs @@ -83,10 +83,10 @@ protected StructureInstance (StructureInfo info, string? comment = null) /// sealed class StructureInstance : StructureInstance { - public T? Instance => (T)Obj; + public T? Instance => Obj is T t ? t : default(T); public StructureInstance (StructureInfo info, T instance, string? comment = null) - : base (info, instance, comment) + : base (info, instance!, comment) {} public StructureInstance (StructureInfo info, string? comment = null) diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/StructureMemberInfo.cs b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/StructureMemberInfo.cs index 6a524d97a7e..fad0fa15283 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/StructureMemberInfo.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/StructureMemberInfo.cs @@ -132,8 +132,11 @@ public bool IsSupportedForTarget (LlvmIrModuleTarget target) return fi.GetValue (instance); } - var pi = Info as PropertyInfo; - return pi.GetValue (instance); + if (Info is PropertyInfo pi) { + return pi.GetValue (instance); + } + + return null; } int GetArraySizeFromProvider (NativeAssemblerStructContextDataProvider? provider, string fieldName) @@ -142,7 +145,7 @@ int GetArraySizeFromProvider (NativeAssemblerStructContextDataProvider? provider return -1; } - return (int)provider.GetMaxInlineWidth (null, fieldName); + return (int)provider.GetMaxInlineWidth (this, fieldName); } } } diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsNativeAssemblyGeneratorMonoVM.cs b/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsNativeAssemblyGeneratorMonoVM.cs index 8409dd6bc61..52f2de59279 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsNativeAssemblyGeneratorMonoVM.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsNativeAssemblyGeneratorMonoVM.cs @@ -100,7 +100,7 @@ protected override void AddMarshalMethodNames (LlvmIrModule module, AssemblyCach id = 0, name = methodName.ToString (), }; - mm_method_names.Add (new StructureInstance (marshalMethodNameStructureInfo, name)); + mm_method_names.Add (new StructureInstance (marshalMethodNameStructureInfo!, name)); } // Must terminate with an "invalid" entry @@ -111,7 +111,7 @@ protected override void AddMarshalMethodNames (LlvmIrModule module, AssemblyCach id = 0, name = String.Empty, }; - mm_method_names.Add (new StructureInstance (marshalMethodNameStructureInfo, name)); + mm_method_names.Add (new StructureInstance (marshalMethodNameStructureInfo!, name)); var mm_method_names_variable = new LlvmIrGlobalVariable (mm_method_names, "mm_method_names", LlvmIrVariableOptions.GlobalConstant) { BeforeWriteCallback = UpdateMarshalMethodNameIds, diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/TypeMapCecilAdapter.cs b/src/Xamarin.Android.Build.Tasks/Utilities/TypeMapCecilAdapter.cs index e48923c4771..25bf3588ef9 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/TypeMapCecilAdapter.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/TypeMapCecilAdapter.cs @@ -180,7 +180,8 @@ static void HandleDebugDuplicates (Dictionary> j javaDuplicates.Add (entry.JavaName, new List { entry }); } else { TypeMapDebugEntry oldEntry = duplicates [0]; - if ((td.IsAbstract || td.IsInterface) && + if (oldEntry.TypeDefinition != null && + (td.IsAbstract || td.IsInterface) && !oldEntry.TypeDefinition.IsAbstract && !oldEntry.TypeDefinition.IsInterface && td.IsAssignableFrom (oldEntry.TypeDefinition, cache)) { diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/TypeMapGenerator.cs b/src/Xamarin.Android.Build.Tasks/Utilities/TypeMapGenerator.cs index c03f78aaa41..e980ad37594 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/TypeMapGenerator.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/TypeMapGenerator.cs @@ -288,7 +288,7 @@ public TypeMapDebugDataSets GetDebugNativeEntries (bool needUniqueAssemblies) var uniqueAssemblies = new Dictionary (StringComparer.OrdinalIgnoreCase); foreach (var xml in XmlFiles) { - if (!uniqueAssemblies.ContainsKey (xml.AssemblyName)) { + if (xml.AssemblyName != null && !uniqueAssemblies.ContainsKey (xml.AssemblyName)) { var assm = new TypeMapDebugAssembly { MVID = xml.AssemblyMvid, MVIDBytes = xml.AssemblyMvid.ToByteArray (), @@ -333,7 +333,7 @@ public ReleaseGenerationState GetReleaseGenerationState () var state = new ReleaseGenerationState (); foreach (var xml in XmlFiles) - if (xml.HasReleaseEntries) + if (xml.HasReleaseEntries && xml.ModuleReleaseData != null) state.TempModules.Add (xml.ModuleReleaseData.MvidBytes, xml.ModuleReleaseData); return state; diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingDebugNativeAssemblyGeneratorCLR.cs b/src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingDebugNativeAssemblyGeneratorCLR.cs index 0bd20902776..dc8e2c2881e 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingDebugNativeAssemblyGeneratorCLR.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingDebugNativeAssemblyGeneratorCLR.cs @@ -241,7 +241,7 @@ protected override void Construct (LlvmIrModule module) from_hash = typemap_uses_hashes ? MonoAndroidHelper.GetXxHash (entry.ManagedName, is64Bit: true) : 0, to = (uint)javaTypeNameOffset, }; - managedToJavaMap.Add (new StructureInstance (typeMapEntryStructureInfo, m2j)); + managedToJavaMap.Add (new StructureInstance (typeMapEntryStructureInfo!, m2j)); if (!typemap_uses_hashes) { continue; @@ -286,7 +286,7 @@ protected override void Construct (LlvmIrModule module) name_length = (ulong)assemblyNameLength, // without the trailing NUL name_offset = (ulong)assemblyNameOffset, }; - uniqueAssemblies.Add (new StructureInstance (typeMapAssemblyStructureInfo, entry)); + uniqueAssemblies.Add (new StructureInstance (typeMapAssemblyStructureInfo!, entry)); } uniqueAssemblies.Sort ((StructureInstance a, StructureInstance b) => { @@ -316,7 +316,7 @@ protected override void Construct (LlvmIrModule module) from_hash = 0, to = managedEntry.SkipInJavaToManaged ? uint.MaxValue : (uint)managedTypeNameOffset, }; - javaToManagedMap.Add (new StructureInstance (typeMapEntryStructureInfo, j2m)); + javaToManagedMap.Add (new StructureInstance (typeMapEntryStructureInfo!, j2m)); int assemblyNameOffset = assemblyNamesBlob.GetIndexOf (entry.AssemblyName); if (assemblyNameOffset < 0) { @@ -330,7 +330,7 @@ protected override void Construct (LlvmIrModule module) assembly_name_index = (uint)assemblyNameOffset, managed_type_token_id = entry.ManagedTypeTokenId, }; - managedTypeInfos.Add (new StructureInstance (typeMapManagedTypeInfoStructureInfo, typeInfo)); + managedTypeInfos.Add (new StructureInstance (typeMapManagedTypeInfoStructureInfo!, typeInfo)); } var map = new TypeMap { @@ -340,7 +340,7 @@ protected override void Construct (LlvmIrModule module) entry_count = data.EntryCount, unique_assemblies_count = (ulong)data.UniqueAssemblies.Count, }; - type_map = new StructureInstance (typeMapStructureInfo, map); + type_map = new StructureInstance (typeMapStructureInfo!, map); module.AddGlobalVariable (TypeMapSymbol, type_map, LlvmIrVariableOptions.GlobalConstant); module.AddGlobalVariable (ManagedToJavaSymbol, managedToJavaMap, LlvmIrVariableOptions.LocalConstant); From 03e969c2582fc6a0b0c6b99bde08311369c7d12f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 4 Aug 2025 20:06:55 +0000 Subject: [PATCH 11/19] Remove redundant null assignments in MarshalMethodEntry constructor Co-authored-by: jonathanpeppers <840039+jonathanpeppers@users.noreply.github.com> --- .../Utilities/MarshalMethodsClassifier.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsClassifier.cs b/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsClassifier.cs index 0eb8b8fc22a..8256b6576c7 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsClassifier.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsClassifier.cs @@ -71,10 +71,6 @@ public MarshalMethodEntry (TypeDefinition declaringType, MethodDefinition native : base (declaringType) { nativeCallbackReal = nativeCallback ?? throw new ArgumentNullException (nameof (nativeCallback)); - Connector = null; - RegisteredMethod = null; - ImplementedMethod = null; - CallbackField = null; JniTypeName = EnsureNonEmpty (jniTypeName, nameof (jniTypeName)); JniMethodName = EnsureNonEmpty (jniName, nameof (jniName)); JniMethodSignature = EnsureNonEmpty (jniSignature, nameof (jniSignature)); From 0d18d318860147f8ea39297aefd17f9cb0d5ff84 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 5 Aug 2025 15:00:41 +0000 Subject: [PATCH 12/19] Fix primary NRT errors in LlvmIrGenerator files mentioned in feedback Co-authored-by: jonathanpeppers <840039+jonathanpeppers@users.noreply.github.com> --- .../LlvmIrGenerator/LlvmIrGenerator.cs | 43 +++++++++++-------- .../LlvmIrGenerator/StructureMemberInfo.cs | 6 ++- 2 files changed, 31 insertions(+), 18 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrGenerator.cs b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrGenerator.cs index 28fb072f80f..a25b0e36bf6 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrGenerator.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrGenerator.cs @@ -199,8 +199,8 @@ void WriteStrings (GeneratorWriteContext context) foreach (LlvmIrStringGroup group in context.Module.Strings) { context.Output.WriteLine (); - if (!String.IsNullOrEmpty (group.Comment)) { - WriteCommentLine (context, group.Comment); + if (!group.Comment.IsNullOrEmpty ()) { + WriteCommentLine (context, group.Comment!); } foreach (LlvmIrStringVariable info in group.Strings) { @@ -237,10 +237,10 @@ void WriteGlobalVariables (GeneratorWriteContext context) context.NumberFormat = gv.NumberFormat; if (gv is LlvmIrGroupDelimiterVariable groupDelimiter) { - if (!context.InVariableGroup && !String.IsNullOrEmpty (groupDelimiter.Comment)) { + if (!context.InVariableGroup && !groupDelimiter.Comment.IsNullOrEmpty ()) { context.Output.WriteLine (); context.Output.Write (context.CurrentIndent); - WriteComment (context, groupDelimiter.Comment); + WriteComment (context, groupDelimiter.Comment!); } context.InVariableGroup = !context.InVariableGroup; @@ -263,8 +263,8 @@ void WriteGlobalVariables (GeneratorWriteContext context) void WriteGlobalVariableName (GeneratorWriteContext context, LlvmIrGlobalVariable variable) { - if (!String.IsNullOrEmpty (variable.Comment)) { - WriteCommentLine (context, variable.Comment); + if (!variable.Comment.IsNullOrEmpty ()) { + WriteCommentLine (context, variable.Comment!); } context.Output.Write ('@'); context.Output.Write (variable.Name); @@ -864,11 +864,11 @@ void WriteSectionedArrayValue (GeneratorWriteContext context, LlvmIrSectionedArr context.Output.WriteLine (); } - if (!String.IsNullOrEmpty (section.Header)) { + if (!section.Header.IsNullOrEmpty ()) { context.Output.Write (context.CurrentIndent); WriteCommentLine (context, $" Module map index: {globalCounter}"); context.Output.Write (context.CurrentIndent); - WriteCommentLine (context, section.Header); + WriteCommentLine (context, section.Header!); } WriteArrayEntries ( @@ -997,14 +997,14 @@ void WriteStructureValue (GeneratorWriteContext context, StructureInstance? inst } string? comment = info.GetCommentFromProvider (smi, instance); - if (String.IsNullOrEmpty (comment)) { + if (comment.IsNullOrEmpty ()) { var sb = new StringBuilder (" "); sb.Append (MapManagedTypeToNative (context, smi)); sb.Append (' '); sb.Append (smi.MappedName); comment = sb.ToString (); } - WriteCommentLine (context, comment); + WriteCommentLine (context, comment!); } context.DecreaseIndent (); @@ -1092,9 +1092,9 @@ void WriteArrayEntries (GeneratorWriteContext context, LlvmIrVariable? variable, void WritePrevItemCommentOrNewline () { - if (!ignoreComments && !String.IsNullOrEmpty (prevItemComment)) { + if (!ignoreComments && !prevItemComment.IsNullOrEmpty ()) { context.Output.Write (' '); - WriteCommentLine (context, prevItemComment); + WriteCommentLine (context, prevItemComment!); } else { context.Output.WriteLine (); } @@ -1161,6 +1161,11 @@ void WriteStreamedArrayValue (GeneratorWriteContext context, LlvmIrGlobalVariabl void WriteArrayValue (GeneratorWriteContext context, LlvmIrVariable variable) { + if (variable.Value == null) { + context.Output.Write ("zeroinitializer"); + return; + } + ICollection entries; if (variable.Type.ImplementsInterface (typeof(IDictionary))) { var list = new List (); @@ -1374,11 +1379,11 @@ void WriteFunctions (GeneratorWriteContext context) void WriteFunctionComment (GeneratorWriteContext context, LlvmIrFunction function) { - if (String.IsNullOrEmpty (function.Comment)) { + if (function.Comment.IsNullOrEmpty ()) { return; } - foreach (string commentLine in function.Comment.Split ('\n')) { + foreach (string commentLine in function.Comment!.Split ('\n')) { context.Output.Write (context.CurrentIndent); WriteCommentLine (context, commentLine); } @@ -1625,7 +1630,7 @@ public static void WriteParameterAttributes (GeneratorWriteContext context, Llvm uint ValueOrPointerSize (uint? value) { - if (value.Value == 0) { + if (!value.HasValue || value.Value == 0) { return context.Target.NativePointerSize; } @@ -1822,7 +1827,9 @@ public static string QuoteStringNoEscape (string s) public static string QuoteString (LlvmIrStringVariable variable, out ulong stringSize, bool nullTerminated = true) { if (variable.Encoding == LlvmIrStringEncoding.UTF8) { - var value = (StringHolder)variable.Value; + if (variable.Value is not StringHolder value) { + throw new InvalidOperationException ("Internal error: string variable must have StringHolder value."); + } if (value.Data == null) { throw new InvalidOperationException ("Internal error: null strings not supported here, they should be handled elsewhere."); } @@ -1847,7 +1854,9 @@ public static string QuoteString (LlvmIrStringVariable variable, out ulong strin static string QuoteUnicodeString (LlvmIrStringVariable variable, out ulong stringSize, bool nullTerminated = true) { - var value = (StringHolder)variable.Value; + if (variable.Value is not StringHolder value) { + throw new InvalidOperationException ("Internal error: string variable must have StringHolder value."); + } if (value.Data == null) { throw new InvalidOperationException ("Internal error: null strings not supported here, they should be handled elsewhere."); } diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/StructureMemberInfo.cs b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/StructureMemberInfo.cs index fad0fa15283..92d7f74e09d 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/StructureMemberInfo.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/StructureMemberInfo.cs @@ -126,8 +126,12 @@ public bool IsSupportedForTarget (LlvmIrModuleTarget target) }; } - public object? GetValue (object instance) + public object? GetValue (object? instance) { + if (instance == null) { + return null; + } + if (Info is FieldInfo fi) { return fi.GetValue (instance); } From e1e2c2177a99bb3a3198c2f083acc1fe755e8ef2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 5 Aug 2025 17:40:16 +0000 Subject: [PATCH 13/19] Continue fixing NRT errors in LLVM files mentioned in feedback Co-authored-by: jonathanpeppers <840039+jonathanpeppers@users.noreply.github.com> --- .../LlvmIrGenerator/LlvmIrFunctionBody.cs | 13 +++-- .../LlvmIrGenerator/LlvmIrGenerator.cs | 50 ++++++++++++++++--- .../LlvmIrGenerator/LlvmIrInstructions.cs | 17 ++++++- .../Utilities/LlvmIrGenerator/LlvmIrModule.cs | 15 ++++++ 4 files changed, 81 insertions(+), 14 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrFunctionBody.cs b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrFunctionBody.cs index fefb1091b60..c16cd3cb798 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrFunctionBody.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrFunctionBody.cs @@ -192,11 +192,14 @@ public void Add (LlvmIrFunctionLabelItem label, string? comment = null) previousLabel = label; if (comment == null) { - var sb = new StringBuilder (" preds = %"); - sb.Append (precedingBlock1.Name); - if (precedingBlock2 != null) { - sb.Append (", %"); - sb.Append (precedingBlock2.Name); + var sb = new StringBuilder (" preds = "); + if (precedingBlock1 != null) { + sb.Append ('%'); + sb.Append (precedingBlock1.Name); + if (precedingBlock2 != null) { + sb.Append (", %"); + sb.Append (precedingBlock2.Name); + } } comment = sb.ToString (); } diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrGenerator.cs b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrGenerator.cs index a25b0e36bf6..93499761c9d 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrGenerator.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrGenerator.cs @@ -442,7 +442,11 @@ void WriteType (GeneratorWriteContext context, StructureInstance si, StructureMe } if (memberInfo.IsIRStruct (context.TypeCache)) { - var sim = new GeneratorStructureInstance (context.Module.GetStructureInfo (memberInfo.MemberType), memberInfo.GetValue (si.Obj)); + object? value = memberInfo.GetValue (si.Obj); + if (value == null) { + throw new InvalidOperationException ($"Structure member {memberInfo.Info.Name} in structure {si.Info.Name} cannot have null value"); + } + var sim = new GeneratorStructureInstance (context.Module.GetStructureInfo (memberInfo.MemberType), value); WriteStructureType (context, sim, out typeInfo); return; } @@ -552,6 +556,9 @@ void WriteArrayType (GeneratorWriteContext context, Type elementType, ulong elem bool isPointer; if (elementType.IsStructureInstance (out Type? structureType)) { + if (structureType == null) { + throw new InvalidOperationException ($"Structure type cannot be null for element type {elementType}"); + } StructureInfo si = context.Module.GetStructureInfo (structureType); irType = $"%{si.NativeTypeDesignator}.{si.Name}"; @@ -685,11 +692,15 @@ void WriteValue (GeneratorWriteContext context, StructureInstance structInstance } if (smi.IsInlineArray) { - Array a = (Array)value; + if (value is not Array a) { + throw new InvalidOperationException ($"Expected array value for inline array field {smi.Info.Name} in structure {structInstance.Info.Name}"); + } ulong length = smi.ArrayElements == 0 ? (ulong)a.Length : smi.ArrayElements; if (smi.MemberType == typeof(byte[])) { - var bytes = (byte[])value; + if (value is not byte[] bytes) { + throw new InvalidOperationException ($"Expected byte array value for byte array field {smi.Info.Name} in structure {structInstance.Info.Name}"); + } // Byte arrays are represented in the same way as strings, without the explicit NUL termination byte AssertArraySize (structInstance, smi, length, smi.ArrayElements); @@ -702,6 +713,9 @@ void WriteValue (GeneratorWriteContext context, StructureInstance structInstance if (smi.IsIRStruct (context.TypeCache)) { StructureInfo si = context.Module.GetStructureInfo (smi.MemberType); + if (value == null) { + throw new InvalidOperationException ($"Structure field {smi.Info.Name} in structure {structInstance.Info.Name} cannot be null"); + } WriteValue (context, typeof(GeneratorStructureInstance), new GeneratorStructureInstance (si, value)); return; } @@ -726,6 +740,9 @@ bool WriteNativePointerValue (GeneratorWriteContext context, StructureInstance s if (si.Info.DataProvider == null) { throw new InvalidOperationException ($"Field '{smi.Info.Name}' of structure '{si.Info.Name}' points to a symbol, but symbol name wasn't provided and there's no configured data context provider"); } + if (si.Obj == null) { + throw new InvalidOperationException ($"Field '{smi.Info.Name}' of structure '{si.Info.Name}' points to a symbol, but structure instance object is null"); + } symbolName = si.Info.DataProvider.GetPointedToSymbolName (si.Obj, smi.Info.Name); } @@ -751,6 +768,10 @@ string ToHex (BasicType basicTypeDesc, Type type, object? value) const char prefixSigned = 's'; const char prefixUnsigned = 'u'; + if (value == null) { + throw new ArgumentNullException (nameof (value), "Value cannot be null for hexadecimal conversion"); + } + string hex; if (type == typeof(byte)) { hex = ((byte)value).ToString (basicTypeDesc.HexFormat, CultureInfo.InvariantCulture); @@ -791,6 +812,9 @@ public void WriteValue (GeneratorWriteContext context, Type type, object? value, } if (type == typeof(bool)) { + if (value == null) { + throw new ArgumentNullException (nameof (value), "Value cannot be null for bool conversion"); + } context.Output.Write ((bool)value ? "true" : "false"); return; } @@ -819,13 +843,19 @@ public void WriteValue (GeneratorWriteContext context, Type type, object? value, } if (type == typeof(LlvmIrStringBlob)) { - WriteStringBlobArray (context, (LlvmIrStringBlob)value); + if (value is not LlvmIrStringBlob blob) { + throw new ArgumentNullException (nameof (value), "Value cannot be null for LlvmIrStringBlob"); + } + WriteStringBlobArray (context, blob); return; } if (type.IsArray) { if (type == typeof(byte[])) { - WriteInlineArray (context, (byte[])value, encodeAsASCII: true); + if (value is not byte[] bytes) { + throw new ArgumentNullException (nameof (value), "Value cannot be null for byte array"); + } + WriteInlineArray (context, bytes, encodeAsASCII: true); return; } @@ -833,12 +863,18 @@ public void WriteValue (GeneratorWriteContext context, Type type, object? value, } if (type.IsSubclassOf (typeof(LlvmIrSectionedArrayBase))) { - WriteSectionedArrayValue (context, (LlvmIrSectionedArrayBase)value); + if (value is not LlvmIrSectionedArrayBase array) { + throw new ArgumentNullException (nameof (value), "Value cannot be null for LlvmIrSectionedArrayBase"); + } + WriteSectionedArrayValue (context, array); return; } if (type == typeof (LlvmIrVariableReference) || type.IsSubclassOf (typeof (LlvmIrVariableReference))) { - WriteVariableReference (context, (LlvmIrVariableReference)value); + if (value is not LlvmIrVariableReference variable) { + throw new ArgumentNullException (nameof (value), "Value cannot be null for LlvmIrVariableReference"); + } + WriteVariableReference (context, variable); return; } diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrInstructions.cs b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrInstructions.cs index 61f46ac5bbb..4b32316b71b 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrInstructions.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrInstructions.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Globalization; +using System.Linq; namespace Xamarin.Android.Tasks.LLVMIR; @@ -190,6 +191,9 @@ protected override void WriteBody (GeneratorWriteContext context) context.Output.Write (", label %"); context.Output.Write (ifTrue.Name); context.Output.Write (", label %"); + if (ifFalse == null) { + throw new InvalidOperationException ("Internal error: ifFalse cannot be null when cond is not null"); + } context.Output.Write (ifFalse.Name); } } @@ -238,7 +242,7 @@ public Call (LlvmIrFunction function, LlvmIrVariable? result = null, ICollection throw new ArgumentException ($"Internal error: function '{function.Signature.Name}' requires {argCount} arguments, but {arguments.Count} were provided", nameof (arguments)); } - this.arguments = new List (arguments).AsReadOnly (); + this.arguments = arguments.ToList ().AsReadOnly (); } } @@ -326,6 +330,9 @@ protected override void WriteBody (GeneratorWriteContext context) void WriteArgument (GeneratorWriteContext context, LlvmIrFunctionParameter? parameter, int index, bool isVararg) { + if (arguments == null) { + throw new InvalidOperationException ("Internal error: arguments cannot be null"); + } object? value = arguments[index]; if (value is LlvmIrInstructionArgumentValuePlaceholder placeholder) { value = placeholder.GetValue (context.Target); @@ -333,6 +340,9 @@ void WriteArgument (GeneratorWriteContext context, LlvmIrFunctionParameter? para string irType; if (!isVararg) { + if (parameter == null) { + throw new InvalidOperationException ($"Internal error: parameter cannot be null for non-vararg argument {index}"); + } irType = LlvmIrGenerator.MapToIRType (parameter.Type, context.TypeCache); } else if (value is LlvmIrVariable v1) { irType = LlvmIrGenerator.MapToIRType (v1.Type, context.TypeCache); @@ -353,7 +363,7 @@ void WriteArgument (GeneratorWriteContext context, LlvmIrFunctionParameter? para context.Output.Write (' '); if (value == null) { - if (!parameter.Type.IsNativePointer (context.TypeCache)) { + if (parameter != null && !parameter.Type.IsNativePointer (context.TypeCache)) { throw new InvalidOperationException ($"Internal error: value for argument {index} to function '{function.Signature.Name}' must not be null"); } @@ -746,6 +756,9 @@ protected override void WriteBody (GeneratorWriteContext context) public LlvmIrFunctionLabelItem Add (T val, LlvmIrFunctionLabelItem? dest = null, string? comment = null) { var label = MakeLabel (dest); + if (items == null) { + throw new InvalidOperationException ("Cannot add switch items when automatic label prefix is null or empty"); + } items.Add ((val, label, comment)); return label; } diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrModule.cs b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrModule.cs index a161094a896..aecb96046e1 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrModule.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrModule.cs @@ -143,6 +143,9 @@ public LlvmIrInstructions.Call CreatePuts (string text, LlvmIrVariable result) { EnsurePuts (); RegisterString (text); + if (puts == null) { + throw new InvalidOperationException ("Internal error: puts function not initialized"); + } return new LlvmIrInstructions.Call (puts, result, new List { text }); } @@ -153,6 +156,9 @@ public LlvmIrInstructions.Call AddPuts (LlvmIrFunction function, string text, Ll { EnsurePuts (); RegisterString (text); + if (puts == null) { + throw new InvalidOperationException ("Internal error: puts function not initialized"); + } return function.Body.Call (puts, result, new List { text }); } @@ -190,12 +196,18 @@ LlvmIrFunctionAttributeSet MakePutsAttributeSet () public LlvmIrInstructions.Call CreateAbort () { EnsureAbort (); + if (abort == null) { + throw new InvalidOperationException ("Internal error: abort function not initialized"); + } return new LlvmIrInstructions.Call (abort); } public LlvmIrInstructions.Call AddAbort (LlvmIrFunction function) { EnsureAbort (); + if (abort == null) { + throw new InvalidOperationException ("Internal error: abort function not initialized"); + } LlvmIrInstructions.Call ret = function.Body.Call (abort); function.Body.Unreachable (); @@ -238,6 +250,9 @@ public void AddIfThenElse (LlvmIrFunction function, LlvmIrVariable result, LlvmI AddInstructions (codeIfThen); if (codeIfElse != null) { + if (labelIfElse == null) { + throw new InvalidOperationException ("Internal error: labelIfElse should not be null when codeIfElse is not null"); + } function.Body.Add (labelIfElse); AddInstructions (codeIfElse); } From 189da69185474eb7dce49a4df81d5bf836fbf58b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 5 Aug 2025 20:28:48 +0000 Subject: [PATCH 14/19] Changes before error encountered Co-authored-by: jonathanpeppers <840039+jonathanpeppers@users.noreply.github.com> --- .../UsesPermissionAttribute.Partial.cs | 6 +- .../Tasks/GenerateMainAndroidManifest.cs | 8 ++- .../Utilities/ManifestDocument.cs | 69 +++++++++++-------- .../Utilities/ManifestDocumentElement.cs | 6 +- 4 files changed, 55 insertions(+), 34 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Mono.Android/UsesPermissionAttribute.Partial.cs b/src/Xamarin.Android.Build.Tasks/Mono.Android/UsesPermissionAttribute.Partial.cs index 7b957979b72..2329fc353d8 100644 --- a/src/Xamarin.Android.Build.Tasks/Mono.Android/UsesPermissionAttribute.Partial.cs +++ b/src/Xamarin.Android.Build.Tasks/Mono.Android/UsesPermissionAttribute.Partial.cs @@ -31,8 +31,12 @@ public static IEnumerable FromCustomAttributeProvider ( self.specified = mapping.Load (self, attr, cache); - foreach (var e in extra) + foreach (var e in extra) { + if (self.specified == null) { + self.specified = new HashSet (); + } self.specified.Add (e); + } yield return self; } diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/GenerateMainAndroidManifest.cs b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateMainAndroidManifest.cs index 9d9782f64ce..106ed5d1894 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/GenerateMainAndroidManifest.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateMainAndroidManifest.cs @@ -111,7 +111,9 @@ IList MergeManifest (NativeCodeGenState codeGenState, Dictionary item.ItemSpec)); + if (manifest.Assemblies != null) { + manifest.Assemblies.AddRange (userAssemblies?.Values?.Where (item => item?.ItemSpec != null).Select (item => item.ItemSpec) ?? []); + } if (!String.IsNullOrWhiteSpace (CheckedBuild)) { // We don't validate CheckedBuild value here, this will be done in BuildApk. We just know that if it's @@ -120,10 +122,10 @@ IList MergeManifest (NativeCodeGenState codeGenState, Dictionary additionalProviders = manifest.Merge (Log, codeGenState.TypeCache, codeGenState.AllJavaTypes, ApplicationJavaClass, EmbedAssemblies, BundledWearApplicationName, MergedManifestDocuments); + IList additionalProviders = manifest.Merge (Log, codeGenState.TypeCache, codeGenState.AllJavaTypes, ApplicationJavaClass, EmbedAssemblies, BundledWearApplicationName, MergedManifestDocuments ?? []); // Only write the new manifest if it actually changed - if (manifest.SaveIfChanged (Log, MergedAndroidManifestOutput)) { + if (!MergedAndroidManifestOutput.IsNullOrEmpty () && manifest.SaveIfChanged (Log, MergedAndroidManifestOutput)) { Log.LogDebugMessage ($"Saving: {MergedAndroidManifestOutput}"); } diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocument.cs b/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocument.cs index 1015852237b..f50a07eaaab 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocument.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocument.cs @@ -259,7 +259,7 @@ void ReorderActivityAliases (TaskLoggingHelper log, XElement app) } } - public IList Merge (TaskLoggingHelper log, TypeDefinitionCache cache, List subclasses, string applicationClass, bool embed, string bundledWearApplicationName, IEnumerable mergedManifestDocuments) + public IList Merge (TaskLoggingHelper log, TypeDefinitionCache cache, List subclasses, string? applicationClass, bool embed, string? bundledWearApplicationName, IEnumerable mergedManifestDocuments) { var manifest = doc.Root; @@ -274,7 +274,7 @@ public IList Merge (TaskLoggingHelper log, TypeDefinitionCache cache, Li } else { PackageName = manifest_package; } - if (PackageName.Contains ("${")) { + if (PackageName != null && PackageName.Contains ("${")) { // placeholder detected if (Placeholders != null) PackageName = ReplacePlaceholders (Placeholders, PackageName); @@ -326,7 +326,7 @@ public IList Merge (TaskLoggingHelper log, TypeDefinitionCache cache, Li if (tsv != null) targetSdkVersion = tsv.Value; else { - targetSdkVersion = TargetSdkVersionName; + targetSdkVersion = TargetSdkVersionName ?? "1"; uses.AddBeforeSelf (new XComment ("suppress UsesMinSdkAttributes")); } @@ -350,7 +350,7 @@ public IList Merge (TaskLoggingHelper log, TypeDefinitionCache cache, Li continue; } - Func generator = GetGenerator (t, cache); + Func? generator = GetGenerator (t, cache); if (generator == null) continue; @@ -358,13 +358,13 @@ public IList Merge (TaskLoggingHelper log, TypeDefinitionCache cache, Li (string name, string compatName) = GetNames (t, cache); // activity not present: create a launcher for it IFF it has attribute if (!existingTypes.Contains (name) && !existingTypes.Contains (compatName)) { - XElement fromCode = generator (t, name, cache, targetSdkVersionValue); + XElement? fromCode = generator (t, name, cache, targetSdkVersionValue); if (fromCode == null) continue; IEnumerable constructors = t.Methods.Where (m => m.IsConstructor).Cast (); if (!constructors.Any (c => !c.HasParameters && c.IsPublic)) { - SequencePoint sourceLocation = FindSource (constructors); + SequencePoint? sourceLocation = FindSource (constructors); if (sourceLocation != null && sourceLocation.Document?.Url != null) { log.LogError ( @@ -402,12 +402,16 @@ public IList Merge (TaskLoggingHelper log, TypeDefinitionCache cache, Li } } - PackageName = AndroidAppManifest.CanonicalizePackageName (PackageName); + if (PackageName != null) { + PackageName = AndroidAppManifest.CanonicalizePackageName (PackageName); - if (!PackageName.Contains ('.')) - throw new InvalidOperationException ("/manifest/@package attribute MUST contain a period ('.')."); + if (!PackageName.Contains ('.')) + throw new InvalidOperationException ("/manifest/@package attribute MUST contain a period ('.')."); - manifest.SetAttributeValue ("package", PackageName); + manifest.SetAttributeValue ("package", PackageName); + } else { + throw new InvalidOperationException ("PackageName is required."); + } if (MultiDex) app.Add (CreateMonoRuntimeProvider ("mono.android.MultiDexLoader", null, initOrder: --AppInitOrder)); @@ -468,12 +472,12 @@ public IList Merge (TaskLoggingHelper log, TypeDefinitionCache cache, Li return providerNames; - SequencePoint FindSource (IEnumerable methods) + SequencePoint? FindSource (IEnumerable methods) { if (methods == null) return null; - SequencePoint ret = null; + SequencePoint? ret = null; foreach (MethodDefinition method in methods.Where (m => m != null && m.HasBody && m.DebugInformation != null)) { foreach (Instruction ins in method.Body.Instructions) { SequencePoint seq = method.DebugInformation.GetSequencePoint (ins); @@ -549,7 +553,7 @@ void RemoveNodes () IEnumerable FixupNameElements(string packageName, IEnumerable nodes) { - foreach (var element in nodes.Select ( x => x as XElement).Where (x => x != null && ManifestAttributeFixups.ContainsKey (x.Name.LocalName))) { + foreach (var element in nodes.OfType().Where (x => ManifestAttributeFixups.ContainsKey (x.Name.LocalName))) { var attributes = ManifestAttributeFixups [element.Name.LocalName]; foreach (var attr in element.Attributes ().Where (x => attributes.Contains (x.Name.LocalName))) { var typeName = attr.Value; @@ -559,7 +563,7 @@ IEnumerable FixupNameElements(string packageName, IEnumerable node return nodes; } - Func GetGenerator (TypeDefinition type, TypeDefinitionCache cache) + Func? GetGenerator (TypeDefinition type, TypeDefinitionCache cache) { if (type.IsSubclassOf ("Android.App.Activity", cache)) return ActivityFromTypeDefinition; @@ -572,7 +576,7 @@ Func GetGenerator (T return null; } - XElement CreateApplicationElement (XElement manifest, string applicationClass, List subclasses, TypeDefinitionCache cache) + XElement CreateApplicationElement (XElement manifest, string? applicationClass, List subclasses, TypeDefinitionCache cache) { var application = manifest.Descendants ("application").FirstOrDefault (); @@ -581,8 +585,9 @@ XElement CreateApplicationElement (XElement manifest, string applicationClass, L List properties = []; List usesLibraryAttr = []; List usesConfigurationAttr = []; - foreach (var assemblyPath in Assemblies) { - var assembly = Resolver.GetAssembly (assemblyPath); + foreach (var assemblyPath in Assemblies ?? []) { + var assembly = Resolver?.GetAssembly (assemblyPath); + if (assembly == null) continue; if (ApplicationAttribute.FromCustomAttributeProvider (assembly, cache) is ApplicationAttribute a) { assemblyAttr.Add (a); } @@ -720,7 +725,7 @@ IList AddMonoRuntimeProviders (XElement app) int AppInitOrder = 2000000000; - XElement CreateMonoRuntimeProvider (string name, string processName, int initOrder) + XElement CreateMonoRuntimeProvider (string name, string? processName, int initOrder) { var directBootAware = DirectBootAware (); return new XElement ("provider", @@ -761,7 +766,7 @@ public bool DirectBootAware () return false; } - XElement ActivityFromTypeDefinition (TypeDefinition type, string name, TypeDefinitionCache cache, int targetSdkVersion) + XElement? ActivityFromTypeDefinition (TypeDefinition type, string name, TypeDefinitionCache cache, int targetSdkVersion) { if (name.StartsWith ("_", StringComparison.Ordinal)) throw new InvalidActivityNameException (string.Format ("Activity name '{0}' is invalid, because activity namespaces may not begin with an underscore.", type.FullName)); @@ -779,7 +784,7 @@ XElement ActivityFromTypeDefinition (TypeDefinition type, string name, TypeDefin cache); } - XElement InstrumentationFromTypeDefinition (TypeDefinition type, string name, TypeDefinitionCache cache) + XElement? InstrumentationFromTypeDefinition (TypeDefinition type, string name, TypeDefinitionCache cache) { return ToElement (type, name, (t, c) => InstrumentationAttribute.FromCustomAttributeProvider (t, c).FirstOrDefault (), @@ -791,13 +796,13 @@ XElement InstrumentationFromTypeDefinition (TypeDefinition type, string name, Ty cache); } - XElement ToElement (TypeDefinition type, string name, Func parser, Func toElement, TypeDefinitionCache cache) + XElement? ToElement (TypeDefinition type, string name, Func parser, Func toElement, TypeDefinitionCache cache) where TAttribute : class { return ToElement (type, name, parser, toElement, update: null, cache); } - XElement ToElement (TypeDefinition type, string name, Func parser, Func toElement, Action update, TypeDefinitionCache cache) + XElement? ToElement (TypeDefinition type, string name, Func parser, Func toElement, Action? update, TypeDefinitionCache cache) where TAttribute : class { TAttribute attr = parser (type, cache); @@ -821,7 +826,7 @@ XElement ToElement (TypeDefinition type, string name, Func (string)x.Attribute (attName) == permInternet)) + if (app != null && (!doc.Root?.Descendants ("uses-permission").Any (x => (string)x.Attribute (attName) == permInternet) ?? false)) app.AddBeforeSelf (new XElement ("uses-permission", new XAttribute (attName, permInternet))); } void AddPermissions (XElement application, TypeDefinitionCache cache) { + if (Assemblies == null || Resolver == null || PackageName == null) return; + var assemblyAttrs = Assemblies.SelectMany (path => PermissionAttribute.FromCustomAttributeProvider (Resolver.GetAssembly (path), cache)); // Add unique permissions to the manifest @@ -885,6 +892,8 @@ void AddPermissions (XElement application, TypeDefinitionCache cache) void AddPermissionGroups (XElement application, TypeDefinitionCache cache) { + if (Assemblies == null || Resolver == null || PackageName == null) return; + var assemblyAttrs = Assemblies.SelectMany (path => PermissionGroupAttribute.FromCustomAttributeProvider (Resolver.GetAssembly (path), cache)); @@ -896,6 +905,8 @@ void AddPermissionGroups (XElement application, TypeDefinitionCache cache) void AddPermissionTrees (XElement application, TypeDefinitionCache cache) { + if (Assemblies == null || Resolver == null || PackageName == null) return; + var assemblyAttrs = Assemblies.SelectMany (path => PermissionTreeAttribute.FromCustomAttributeProvider (Resolver.GetAssembly (path), cache)); @@ -1067,7 +1078,7 @@ static int GetAbiCode (string abi) } } - internal static string ReplacePlaceholders (string [] placeholders, string text, Action? logCodedWarning = null) + internal static string ReplacePlaceholders (string[]? placeholders, string text, Action? logCodedWarning = null) { string result = text; if (placeholders == null) @@ -1112,7 +1123,7 @@ public bool ValidateVersionCode (out string error, out string errorCode) return true; } - public void CalculateVersionCode (string currentAbi, string versionCodePattern, string versionCodeProperties) + public void CalculateVersionCode (string? currentAbi, string versionCodePattern, string? versionCodeProperties) { var regex = new Regex ("\\{(?([A-Za-z]+)):?[D0-9]*[\\}]"); var kvp = new Dictionary (); @@ -1152,7 +1163,7 @@ public void CalculateVersionCode (string currentAbi, string versionCodePattern, public interface IVersionResolver { string? GetIdFromApiLevel (string apiLevel); - int? GetApiLevelFromId (string id); + int? GetApiLevelFromId (string? id); } class MonoAndroidHelperVersionResolver : IVersionResolver @@ -1160,7 +1171,7 @@ class MonoAndroidHelperVersionResolver : IVersionResolver public string? GetIdFromApiLevel (string apiLevel) => MonoAndroidHelper.SupportedVersions.GetIdFromApiLevel (apiLevel); - public int? GetApiLevelFromId (string id) - => MonoAndroidHelper.SupportedVersions.GetApiLevelFromId (id); + public int? GetApiLevelFromId (string? id) + => id == null ? null : MonoAndroidHelper.SupportedVersions.GetApiLevelFromId (id); } } diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocumentElement.cs b/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocumentElement.cs index 425e4fb908b..cf18df074a6 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocumentElement.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocumentElement.cs @@ -133,9 +133,13 @@ public void Add (string member, string attributeName, Action setter, return specified; } - public XElement ToElement (T value, ICollection specified, string packageName, TypeDefinitionCache cache, + public XElement ToElement (T value, ICollection? specified, string packageName, TypeDefinitionCache cache, ICustomAttributeProvider? provider = null, IAssemblyResolver? resolver = null, int targetSdkVersion = 0) { + if (specified == null) { + specified = new HashSet (); + } + var r = new XElement (Element, specified.OrderBy (e => e) .Select (e => ToAttribute (e, value, packageName, provider, resolver, cache, targetSdkVersion)) From 3f41160a05f11d4830f318566e9b3d2a22b10b09 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 6 Aug 2025 14:59:12 +0000 Subject: [PATCH 15/19] Apply code style improvements suggested in review feedback Co-authored-by: jonathanpeppers <840039+jonathanpeppers@users.noreply.github.com> --- .../Mono.Android/UsesPermissionAttribute.Partial.cs | 4 +--- .../Tasks/GenerateMainAndroidManifest.cs | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Mono.Android/UsesPermissionAttribute.Partial.cs b/src/Xamarin.Android.Build.Tasks/Mono.Android/UsesPermissionAttribute.Partial.cs index 2329fc353d8..72643cc4255 100644 --- a/src/Xamarin.Android.Build.Tasks/Mono.Android/UsesPermissionAttribute.Partial.cs +++ b/src/Xamarin.Android.Build.Tasks/Mono.Android/UsesPermissionAttribute.Partial.cs @@ -32,9 +32,7 @@ public static IEnumerable FromCustomAttributeProvider ( self.specified = mapping.Load (self, attr, cache); foreach (var e in extra) { - if (self.specified == null) { - self.specified = new HashSet (); - } + self.specified ??= new HashSet (); self.specified.Add (e); } diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/GenerateMainAndroidManifest.cs b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateMainAndroidManifest.cs index 106ed5d1894..a394919e35f 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/GenerateMainAndroidManifest.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateMainAndroidManifest.cs @@ -111,9 +111,7 @@ IList MergeManifest (NativeCodeGenState codeGenState, Dictionary item?.ItemSpec != null).Select (item => item.ItemSpec) ?? []); - } + manifest.Assemblies?.AddRange (userAssemblies?.Values?.Where (item => item?.ItemSpec != null).Select (item => item.ItemSpec) ?? []); if (!String.IsNullOrWhiteSpace (CheckedBuild)) { // We don't validate CheckedBuild value here, this will be done in BuildApk. We just know that if it's From 04ba1757a74c5857fc2a35db55c04a1dc3b1780c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 6 Aug 2025 15:33:52 +0000 Subject: [PATCH 16/19] Fix remaining null reference errors in ManifestDocument.cs methods Co-authored-by: jonathanpeppers <840039+jonathanpeppers@users.noreply.github.com> --- .../Utilities/ManifestDocument.cs | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocument.cs b/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocument.cs index f50a07eaaab..f6b4d3b5035 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocument.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocument.cs @@ -918,6 +918,11 @@ void AddPermissionTrees (XElement application, TypeDefinitionCache cache) void AddUsesPermissions (XElement application, TypeDefinitionCache cache) { + if (Resolver == null) + throw new ArgumentNullException (nameof (Resolver)); + if (PackageName == null) + throw new ArgumentNullException (nameof (PackageName)); + var assemblyAttrs = Assemblies.SelectMany (path => UsesPermissionAttribute.FromCustomAttributeProvider (Resolver.GetAssembly (path), cache)); @@ -942,6 +947,11 @@ void AddUsesLibraries (XElement application, List librarie void AddUsesFeatures (XElement application, TypeDefinitionCache cache) { + if (Resolver == null) + throw new ArgumentNullException (nameof (Resolver)); + if (PackageName == null) + throw new ArgumentNullException (nameof (PackageName)); + var assemblyAttrs = Assemblies.SelectMany (path => UsesFeatureAttribute.FromCustomAttributeProvider (Resolver.GetAssembly (path), cache)); @@ -963,6 +973,11 @@ void AddUsesFeatures (XElement application, TypeDefinitionCache cache) void AddSupportsGLTextures (XElement application, TypeDefinitionCache cache) { + if (Resolver == null) + throw new ArgumentNullException (nameof (Resolver)); + if (PackageName == null) + throw new ArgumentNullException (nameof (PackageName)); + var assemblyAttrs = Assemblies.SelectMany (path => SupportsGLTextureAttribute.FromCustomAttributeProvider (Resolver.GetAssembly (path), cache)); @@ -976,6 +991,11 @@ void AddSupportsGLTextures (XElement application, TypeDefinitionCache cache) void AddInstrumentations (XElement manifest, IList subclasses, int targetSdkVersion, TypeDefinitionCache cache) { + if (Resolver == null) + throw new ArgumentNullException (nameof (Resolver)); + if (PackageName == null) + throw new ArgumentNullException (nameof (PackageName)); + var assemblyAttrs = Assemblies.SelectMany (path => InstrumentationAttribute.FromCustomAttributeProvider (Resolver.GetAssembly (path), cache)); From abe06e59a84d3cbc52b8dc8d235a83de25462ee3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 6 Aug 2025 21:39:52 +0000 Subject: [PATCH 17/19] Fix KeyNotFoundException for System.Object in ValueConverters dictionary Co-authored-by: jonathanpeppers <840039+jonathanpeppers@users.noreply.github.com> --- .../Utilities/ManifestDocumentElement.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocumentElement.cs b/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocumentElement.cs index cf18df074a6..1e6a59622b2 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocumentElement.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocumentElement.cs @@ -180,7 +180,10 @@ public XElement ToElement (T value, ICollection? specified, string packa return null; var t = m.MemberType ?? v.GetType (); - var c = ValueConverters [t]; + if (!ValueConverters.TryGetValue (t, out var c)) { + // Fallback for unknown types: use ToString() + return v.ToString (); + } return c (v, provider, resolver, targetSdkVersion, cache); } From ae81d7a77d64edc761763f639659b346abcab797 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 7 Aug 2025 15:19:51 +0000 Subject: [PATCH 18/19] Address code review feedback on ManifestDocumentElement.cs Co-authored-by: jonathanpeppers <840039+jonathanpeppers@users.noreply.github.com> --- .../Utilities/ManifestDocumentElement.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocumentElement.cs b/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocumentElement.cs index 1e6a59622b2..cf18df074a6 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocumentElement.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocumentElement.cs @@ -180,10 +180,7 @@ public XElement ToElement (T value, ICollection? specified, string packa return null; var t = m.MemberType ?? v.GetType (); - if (!ValueConverters.TryGetValue (t, out var c)) { - // Fallback for unknown types: use ToString() - return v.ToString (); - } + var c = ValueConverters [t]; return c (v, provider, resolver, targetSdkVersion, cache); } From e7c50f1c9199543531971a080dca31b93c13b369 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 7 Aug 2025 15:32:46 +0000 Subject: [PATCH 19/19] Implement all code review feedback on ManifestDocumentElement.cs Co-authored-by: jonathanpeppers <840039+jonathanpeppers@users.noreply.github.com> --- .../Mono.Android/ActivityAttribute.Partial.cs | 2 +- .../ApplicationAttribute.Partial.cs | 2 +- .../BroadcastReceiverAttribute.Partial.cs | 2 +- .../ContentProviderAttribute.Partial.cs | 2 +- .../GrantUriPermissionAttribute.Partial.cs | 2 +- .../InstrumentationAttribute.Partial.cs | 2 +- .../Mono.Android/LayoutAttribute.Partial.cs | 2 +- .../Mono.Android/MetaDataAttribute.Partial.cs | 2 +- .../PermissionAttribute.Partial.cs | 2 +- .../PermissionGroupAttribute.Partial.cs | 2 +- .../PermissionTreeAttribute.Partial.cs | 2 +- .../Mono.Android/PropertyAttribute.Partial.cs | 2 +- .../Mono.Android/ServiceAttribute.Partial.cs | 2 +- .../SupportsGLTextureAttribute.Partial.cs | 2 +- .../UsesConfigurationAttribute.Partial.cs | 2 +- .../UsesFeatureAttribute.Partial.cs | 2 +- .../UsesLibraryAttribute.Partial.cs | 2 +- .../UsesPermissionAttribute.Partial.cs | 2 +- .../Utilities/ManifestDocumentElement.cs | 26 +++++++++---------- 19 files changed, 30 insertions(+), 32 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Mono.Android/ActivityAttribute.Partial.cs b/src/Xamarin.Android.Build.Tasks/Mono.Android/ActivityAttribute.Partial.cs index 4200e9a1272..7aa0ff0db3b 100644 --- a/src/Xamarin.Android.Build.Tasks/Mono.Android/ActivityAttribute.Partial.cs +++ b/src/Xamarin.Android.Build.Tasks/Mono.Android/ActivityAttribute.Partial.cs @@ -54,7 +54,7 @@ public static ActivityAttribute FromTypeDefinition (TypeDefinition type, TypeDef internal XElement ToElement (IAssemblyResolver resolver, string packageName, TypeDefinitionCache cache, int targetSdkVersion) { - return mapping.ToElement (this, specified, packageName, cache, type, resolver, targetSdkVersion); + return mapping.ToElement (this, specified ?? new HashSet (), packageName, cache, type, resolver, targetSdkVersion); } } } diff --git a/src/Xamarin.Android.Build.Tasks/Mono.Android/ApplicationAttribute.Partial.cs b/src/Xamarin.Android.Build.Tasks/Mono.Android/ApplicationAttribute.Partial.cs index 2997d50006e..002ef15dc14 100644 --- a/src/Xamarin.Android.Build.Tasks/Mono.Android/ApplicationAttribute.Partial.cs +++ b/src/Xamarin.Android.Build.Tasks/Mono.Android/ApplicationAttribute.Partial.cs @@ -76,7 +76,7 @@ public static ApplicationAttribute FromCustomAttributeProvider (ICustomAttribute internal XElement ToElement (IAssemblyResolver resolver, string packageName, TypeDefinitionCache cache) { - return mapping.ToElement (this, specified, packageName, cache, provider, resolver); + return mapping.ToElement (this, specified ?? new HashSet (), packageName, cache, provider, resolver); } static string ToNameAttribute (ApplicationAttribute self, ICustomAttributeProvider provider, IAssemblyResolver resolver, TypeDefinitionCache cache) diff --git a/src/Xamarin.Android.Build.Tasks/Mono.Android/BroadcastReceiverAttribute.Partial.cs b/src/Xamarin.Android.Build.Tasks/Mono.Android/BroadcastReceiverAttribute.Partial.cs index 99cdcc07e71..95ea1465570 100644 --- a/src/Xamarin.Android.Build.Tasks/Mono.Android/BroadcastReceiverAttribute.Partial.cs +++ b/src/Xamarin.Android.Build.Tasks/Mono.Android/BroadcastReceiverAttribute.Partial.cs @@ -29,7 +29,7 @@ public static BroadcastReceiverAttribute FromTypeDefinition (TypeDefinition type public XElement ToElement (string packageName, TypeDefinitionCache cache) { - return mapping.ToElement (this, specified, packageName, cache); + return mapping.ToElement (this, specified ?? new HashSet (), packageName, cache); } } } diff --git a/src/Xamarin.Android.Build.Tasks/Mono.Android/ContentProviderAttribute.Partial.cs b/src/Xamarin.Android.Build.Tasks/Mono.Android/ContentProviderAttribute.Partial.cs index 84fe8f45649..c00dbae227a 100644 --- a/src/Xamarin.Android.Build.Tasks/Mono.Android/ContentProviderAttribute.Partial.cs +++ b/src/Xamarin.Android.Build.Tasks/Mono.Android/ContentProviderAttribute.Partial.cs @@ -46,7 +46,7 @@ public static ContentProviderAttribute FromTypeDefinition (TypeDefinition type, public XElement ToElement (string packageName, TypeDefinitionCache cache) { - return mapping.ToElement (this, specified, packageName, cache); + return mapping.ToElement (this, specified ?? new HashSet (), packageName, cache); } } } diff --git a/src/Xamarin.Android.Build.Tasks/Mono.Android/GrantUriPermissionAttribute.Partial.cs b/src/Xamarin.Android.Build.Tasks/Mono.Android/GrantUriPermissionAttribute.Partial.cs index 79bfd2265d4..e946cbe8a27 100644 --- a/src/Xamarin.Android.Build.Tasks/Mono.Android/GrantUriPermissionAttribute.Partial.cs +++ b/src/Xamarin.Android.Build.Tasks/Mono.Android/GrantUriPermissionAttribute.Partial.cs @@ -28,7 +28,7 @@ public static IEnumerable FromTypeDefinition (TypeD public XElement ToElement (string packageName, TypeDefinitionCache cache) { - return mapping.ToElement (this, specified, packageName, cache); + return mapping.ToElement (this, specified ?? new HashSet (), packageName, cache); } } } diff --git a/src/Xamarin.Android.Build.Tasks/Mono.Android/InstrumentationAttribute.Partial.cs b/src/Xamarin.Android.Build.Tasks/Mono.Android/InstrumentationAttribute.Partial.cs index 98b5a0611fc..9443d6b3764 100644 --- a/src/Xamarin.Android.Build.Tasks/Mono.Android/InstrumentationAttribute.Partial.cs +++ b/src/Xamarin.Android.Build.Tasks/Mono.Android/InstrumentationAttribute.Partial.cs @@ -33,7 +33,7 @@ public void SetTargetPackage (string package) public XElement ToElement (string packageName, TypeDefinitionCache cache) { - return mapping.ToElement (this, specified, packageName, cache); + return mapping.ToElement (this, specified ?? new HashSet (), packageName, cache); } } } diff --git a/src/Xamarin.Android.Build.Tasks/Mono.Android/LayoutAttribute.Partial.cs b/src/Xamarin.Android.Build.Tasks/Mono.Android/LayoutAttribute.Partial.cs index 70d9a81225c..a478bb669fc 100644 --- a/src/Xamarin.Android.Build.Tasks/Mono.Android/LayoutAttribute.Partial.cs +++ b/src/Xamarin.Android.Build.Tasks/Mono.Android/LayoutAttribute.Partial.cs @@ -37,7 +37,7 @@ public static LayoutAttribute FromTypeDefinition (TypeDefinition type, TypeDefin internal XElement ToElement (IAssemblyResolver resolver, string packageName, TypeDefinitionCache cache) { - return mapping.ToElement (this, specified, packageName, cache, type, resolver); + return mapping.ToElement (this, specified ?? new HashSet (), packageName, cache, type, resolver); } } } diff --git a/src/Xamarin.Android.Build.Tasks/Mono.Android/MetaDataAttribute.Partial.cs b/src/Xamarin.Android.Build.Tasks/Mono.Android/MetaDataAttribute.Partial.cs index 7581012015f..9dcaf4298fd 100644 --- a/src/Xamarin.Android.Build.Tasks/Mono.Android/MetaDataAttribute.Partial.cs +++ b/src/Xamarin.Android.Build.Tasks/Mono.Android/MetaDataAttribute.Partial.cs @@ -29,7 +29,7 @@ public static IEnumerable FromCustomAttributeProvider (ICusto public XElement ToElement (string packageName, TypeDefinitionCache cache) { - return mapping.ToElement (this, specified, packageName, cache); + return mapping.ToElement (this, specified ?? new HashSet (), packageName, cache); } } } diff --git a/src/Xamarin.Android.Build.Tasks/Mono.Android/PermissionAttribute.Partial.cs b/src/Xamarin.Android.Build.Tasks/Mono.Android/PermissionAttribute.Partial.cs index 2f85d446562..ca4c13d4dda 100644 --- a/src/Xamarin.Android.Build.Tasks/Mono.Android/PermissionAttribute.Partial.cs +++ b/src/Xamarin.Android.Build.Tasks/Mono.Android/PermissionAttribute.Partial.cs @@ -33,7 +33,7 @@ public static IEnumerable FromCustomAttributeProvider (ICus internal XElement ToElement (string packageName, TypeDefinitionCache cache) { - return mapping.ToElement (this, specified, packageName, cache); + return mapping.ToElement (this, specified ?? new HashSet (), packageName, cache); } internal class PermissionAttributeComparer : IEqualityComparer diff --git a/src/Xamarin.Android.Build.Tasks/Mono.Android/PermissionGroupAttribute.Partial.cs b/src/Xamarin.Android.Build.Tasks/Mono.Android/PermissionGroupAttribute.Partial.cs index 51d1a9f0d95..fc1a04b1f17 100644 --- a/src/Xamarin.Android.Build.Tasks/Mono.Android/PermissionGroupAttribute.Partial.cs +++ b/src/Xamarin.Android.Build.Tasks/Mono.Android/PermissionGroupAttribute.Partial.cs @@ -33,7 +33,7 @@ public static IEnumerable FromCustomAttributeProvider internal XElement ToElement (string packageName, TypeDefinitionCache cache) { - return mapping.ToElement (this, specified, packageName, cache); + return mapping.ToElement (this, specified ?? new HashSet (), packageName, cache); } internal class PermissionGroupAttributeComparer : IEqualityComparer diff --git a/src/Xamarin.Android.Build.Tasks/Mono.Android/PermissionTreeAttribute.Partial.cs b/src/Xamarin.Android.Build.Tasks/Mono.Android/PermissionTreeAttribute.Partial.cs index 2c88bfdaaa7..6b5c6507aa7 100644 --- a/src/Xamarin.Android.Build.Tasks/Mono.Android/PermissionTreeAttribute.Partial.cs +++ b/src/Xamarin.Android.Build.Tasks/Mono.Android/PermissionTreeAttribute.Partial.cs @@ -33,7 +33,7 @@ public static IEnumerable FromCustomAttributeProvider ( internal XElement ToElement (string packageName, TypeDefinitionCache cache) { - return mapping.ToElement (this, specified, packageName, cache); + return mapping.ToElement (this, specified ?? new HashSet (), packageName, cache); } internal class PermissionTreeAttributeComparer : IEqualityComparer diff --git a/src/Xamarin.Android.Build.Tasks/Mono.Android/PropertyAttribute.Partial.cs b/src/Xamarin.Android.Build.Tasks/Mono.Android/PropertyAttribute.Partial.cs index 6ce9cae9464..e51f6b646a1 100644 --- a/src/Xamarin.Android.Build.Tasks/Mono.Android/PropertyAttribute.Partial.cs +++ b/src/Xamarin.Android.Build.Tasks/Mono.Android/PropertyAttribute.Partial.cs @@ -29,7 +29,7 @@ public static IEnumerable FromCustomAttributeProvider (ICusto public XElement ToElement (string packageName, TypeDefinitionCache cache) { - return mapping.ToElement (this, specified, packageName, cache); + return mapping.ToElement (this, specified ?? new HashSet (), packageName, cache); } } } diff --git a/src/Xamarin.Android.Build.Tasks/Mono.Android/ServiceAttribute.Partial.cs b/src/Xamarin.Android.Build.Tasks/Mono.Android/ServiceAttribute.Partial.cs index 2c232b8af1d..fb780d2a211 100644 --- a/src/Xamarin.Android.Build.Tasks/Mono.Android/ServiceAttribute.Partial.cs +++ b/src/Xamarin.Android.Build.Tasks/Mono.Android/ServiceAttribute.Partial.cs @@ -31,7 +31,7 @@ public static ServiceAttribute FromTypeDefinition (TypeDefinition type, TypeDefi public XElement ToElement (string packageName, TypeDefinitionCache cache) { - return mapping.ToElement (this, specified, packageName, cache); + return mapping.ToElement (this, specified ?? new HashSet (), packageName, cache); } } } diff --git a/src/Xamarin.Android.Build.Tasks/Mono.Android/SupportsGLTextureAttribute.Partial.cs b/src/Xamarin.Android.Build.Tasks/Mono.Android/SupportsGLTextureAttribute.Partial.cs index cd705268b73..b93cc074268 100644 --- a/src/Xamarin.Android.Build.Tasks/Mono.Android/SupportsGLTextureAttribute.Partial.cs +++ b/src/Xamarin.Android.Build.Tasks/Mono.Android/SupportsGLTextureAttribute.Partial.cs @@ -24,7 +24,7 @@ partial class SupportsGLTextureAttribute internal XElement ToElement (string packageName, TypeDefinitionCache cache) { - return mapping.ToElement (this, specified, packageName, cache); + return mapping.ToElement (this, specified ?? new HashSet (), packageName, cache); } ICollection? specified; diff --git a/src/Xamarin.Android.Build.Tasks/Mono.Android/UsesConfigurationAttribute.Partial.cs b/src/Xamarin.Android.Build.Tasks/Mono.Android/UsesConfigurationAttribute.Partial.cs index d1c0cf62e27..6dfb05ae7c9 100644 --- a/src/Xamarin.Android.Build.Tasks/Mono.Android/UsesConfigurationAttribute.Partial.cs +++ b/src/Xamarin.Android.Build.Tasks/Mono.Android/UsesConfigurationAttribute.Partial.cs @@ -15,7 +15,7 @@ partial class UsesConfigurationAttribute { internal XElement ToElement (string packageName, TypeDefinitionCache cache) { - return mapping.ToElement (this, specified, packageName, cache); + return mapping.ToElement (this, specified ?? new HashSet (), packageName, cache); } ICollection? specified; diff --git a/src/Xamarin.Android.Build.Tasks/Mono.Android/UsesFeatureAttribute.Partial.cs b/src/Xamarin.Android.Build.Tasks/Mono.Android/UsesFeatureAttribute.Partial.cs index 86ecc5f825c..b77de3bb093 100644 --- a/src/Xamarin.Android.Build.Tasks/Mono.Android/UsesFeatureAttribute.Partial.cs +++ b/src/Xamarin.Android.Build.Tasks/Mono.Android/UsesFeatureAttribute.Partial.cs @@ -30,7 +30,7 @@ internal string GLESVesionAsString () internal XElement ToElement (string packageName, TypeDefinitionCache cache) { - return mapping.ToElement (this, specified, packageName, cache); + return mapping.ToElement (this, specified ?? new HashSet (), packageName, cache); } ICollection? specified; diff --git a/src/Xamarin.Android.Build.Tasks/Mono.Android/UsesLibraryAttribute.Partial.cs b/src/Xamarin.Android.Build.Tasks/Mono.Android/UsesLibraryAttribute.Partial.cs index 4fa29d822f5..f2e880b7391 100644 --- a/src/Xamarin.Android.Build.Tasks/Mono.Android/UsesLibraryAttribute.Partial.cs +++ b/src/Xamarin.Android.Build.Tasks/Mono.Android/UsesLibraryAttribute.Partial.cs @@ -48,7 +48,7 @@ public static IEnumerable FromCustomAttributeProvider (ICu public XElement ToElement (string packageName, TypeDefinitionCache cache) { - return mapping.ToElement (this, specified, packageName, cache); + return mapping.ToElement (this, specified ?? new HashSet (), packageName, cache); } } } diff --git a/src/Xamarin.Android.Build.Tasks/Mono.Android/UsesPermissionAttribute.Partial.cs b/src/Xamarin.Android.Build.Tasks/Mono.Android/UsesPermissionAttribute.Partial.cs index 72643cc4255..a1d393c960f 100644 --- a/src/Xamarin.Android.Build.Tasks/Mono.Android/UsesPermissionAttribute.Partial.cs +++ b/src/Xamarin.Android.Build.Tasks/Mono.Android/UsesPermissionAttribute.Partial.cs @@ -42,7 +42,7 @@ public static IEnumerable FromCustomAttributeProvider ( public XElement ToElement (string packageName, TypeDefinitionCache cache) { - return mapping.ToElement (this, specified, packageName, cache); + return mapping.ToElement (this, specified ?? new HashSet (), packageName, cache); } internal class UsesPermissionComparer : IEqualityComparer diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocumentElement.cs b/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocumentElement.cs index cf18df074a6..7901ac0a340 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocumentElement.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocumentElement.cs @@ -81,7 +81,7 @@ class MappingInfo { public string AttributeName = ""; public Func Getter = _ => null; public Action? Setter; - public Type MemberType = typeof(object); + public Type? MemberType; public Func? AttributeValue; public Func? AttributeValue2; } @@ -94,7 +94,7 @@ public void Add (string member, string attributeName, Func getter, A AttributeName = attributeName, Getter = getter, Setter = setter, - MemberType = memberType ?? typeof(object), + MemberType = memberType, }); } @@ -133,23 +133,21 @@ public void Add (string member, string attributeName, Action setter, return specified; } - public XElement ToElement (T value, ICollection? specified, string packageName, TypeDefinitionCache cache, + public XElement ToElement (T value, ICollection specified, string packageName, TypeDefinitionCache cache, ICustomAttributeProvider? provider = null, IAssemblyResolver? resolver = null, int targetSdkVersion = 0) { - if (specified == null) { - specified = new HashSet (); - } - var r = new XElement (Element, specified.OrderBy (e => e) - .Select (e => ToAttribute (e, value, packageName, provider, resolver, cache, targetSdkVersion)) + .Select (e => provider != null && resolver != null + ? ToAttribute (e, value, packageName, provider, resolver, cache, targetSdkVersion) + : null) .Where (a => a != null)); AndroidResource.UpdateXmlResource (r); return r; } XAttribute? ToAttribute (string name, T value, string packageName, - ICustomAttributeProvider? provider, IAssemblyResolver? resolver, TypeDefinitionCache cache, int targetSdkVersion = 0) + ICustomAttributeProvider provider, IAssemblyResolver resolver, TypeDefinitionCache cache, int targetSdkVersion = 0) { if (!Mappings.ContainsKey (name)) throw new ArgumentException ("Invalid attribute name: " + name); @@ -164,12 +162,12 @@ public XElement ToElement (T value, ICollection? specified, string packa return new XAttribute (ManifestDocument.AndroidXmlNamespace + m.AttributeName, v); } - string? ToAttributeValue (string name, T value, ICustomAttributeProvider? provider, IAssemblyResolver? resolver, TypeDefinitionCache cache, int targetSdkVersion = 0) + string? ToAttributeValue (string name, T value, ICustomAttributeProvider provider, IAssemblyResolver resolver, TypeDefinitionCache cache, int targetSdkVersion = 0) { var m = Mappings [name]; if (m.AttributeValue != null) return m.AttributeValue (value); - if (m.AttributeValue2 != null && provider != null && resolver != null) + if (m.AttributeValue2 != null) return m.AttributeValue2 (value, provider, resolver, cache); if (m.Getter == null) @@ -184,7 +182,7 @@ public XElement ToElement (T value, ICollection? specified, string packa return c (v, provider, resolver, targetSdkVersion, cache); } - static readonly Dictionary> ValueConverters = new () { + static readonly Dictionary> ValueConverters = new () { { typeof (bool), (value, p, r, v, c) => ToString ((bool) value) }, { typeof (int), (value, p, r, v, c) => value.ToString () }, { typeof (float), (value, p, r, v, c) => value.ToString () }, @@ -370,9 +368,9 @@ static string ToString (WindowRotationAnimation value) } } - static string ToString (string value, ICustomAttributeProvider? provider, IAssemblyResolver? resolver, TypeDefinitionCache cache) + static string ToString (string value, ICustomAttributeProvider provider, IAssemblyResolver resolver, TypeDefinitionCache cache) { - var typeDef = ResolveType (value, provider!, resolver!); + var typeDef = ResolveType (value, provider, resolver); return ToString (typeDef, cache); }