From 7d8757ff6915ce511b4d2a2460974dce808b6c50 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Thu, 16 Apr 2026 14:05:45 -0500 Subject: [PATCH] Compute Java name hashes on demand instead of pre-computing both Replace JavaNameHash32/JavaNameHash64 with a single JavaNameHash field in both MonoVM and CoreCLR type mapping generators. Hashes are now computed on demand in GenerateAndSortJavaHashes() where the target is known, instead of pre-computing both 32-bit and 64-bit hashes during Construct(). Since each architecture gets its own generator instance, only one hash width is ever needed. This eliminates redundant computation and storage. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- ...peMappingReleaseNativeAssemblyGenerator.cs | 55 +++++----------- ...appingReleaseNativeAssemblyGeneratorCLR.cs | 62 +++++-------------- 2 files changed, 32 insertions(+), 85 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingReleaseNativeAssemblyGenerator.cs b/src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingReleaseNativeAssemblyGenerator.cs index feeecac2453..5a7d2d06b8c 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingReleaseNativeAssemblyGenerator.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingReleaseNativeAssemblyGenerator.cs @@ -131,10 +131,7 @@ sealed class TypeMapJava public string JavaName; [NativeAssembler (Ignore = true)] - public uint JavaNameHash32; - - [NativeAssembler (Ignore = true)] - public ulong JavaNameHash64; + public ulong JavaNameHash; public uint module_index; @@ -155,19 +152,11 @@ public ModuleMapData (string symbolLabel, List> + sealed class JavaNameHashComparer : IComparer> { public int Compare (StructureInstance a, StructureInstance b) { - return a.Instance.JavaNameHash32.CompareTo (b.Instance.JavaNameHash32); - } - } - - sealed class JavaNameHash64Comparer : IComparer> - { - public int Compare (StructureInstance a, StructureInstance b) - { - return a.Instance.JavaNameHash64.CompareTo (b.Instance.JavaNameHash64); + return a.Instance.JavaNameHash.CompareTo (b.Instance.JavaNameHash); } } @@ -184,8 +173,7 @@ sealed class ConstructionState StructureInfo typeMapJavaStructureInfo; StructureInfo typeMapModuleStructureInfo; StructureInfo typeMapModuleEntryStructureInfo; - JavaNameHash32Comparer javaNameHash32Comparer; - JavaNameHash64Comparer javaNameHash64Comparer; + JavaNameHashComparer javaNameHashComparer; ulong moduleCounter = 0; @@ -193,8 +181,7 @@ public TypeMappingReleaseNativeAssemblyGenerator (TaskLoggingHelper log, NativeT : base (log) { this.mappingData = mappingData ?? throw new ArgumentNullException (nameof (mappingData)); - javaNameHash32Comparer = new JavaNameHash32Comparer (); - javaNameHash64Comparer = new JavaNameHash64Comparer (); + javaNameHashComparer = new JavaNameHashComparer (); } protected override void Construct (LlvmIrModule module) @@ -208,7 +195,6 @@ protected override void Construct (LlvmIrModule module) cs.JavaNames = new List (); InitJavaMap (cs); InitMapModules (cs); - HashJavaNames (cs); PrepareModules (cs); module.AddGlobalVariable ("map_module_count", mappingData.MapModuleCount); @@ -249,7 +235,6 @@ void UpdateJavaIndexes (LlvmIrVariable variable, LlvmIrModuleTarget target, obje { ConstructionState cs = EnsureConstructionState (callerState); LlvmIrGlobalVariable gv = EnsureGlobalVariable (variable); - IComparer> hashComparer = target.Is64Bit ? javaNameHash64Comparer : javaNameHash32Comparer; var entries = (List>)variable.Value; foreach (StructureInstance entry in entries) { @@ -259,7 +244,7 @@ void UpdateJavaIndexes (LlvmIrVariable variable, LlvmIrModuleTarget target, obje uint GetJavaEntryIndex (TypeMapJava javaEntry) { var key = new StructureInstance (typeMapJavaStructureInfo, javaEntry); - int idx = cs.JavaMap.BinarySearch (key, hashComparer); + int idx = cs.JavaMap.BinarySearch (key, javaNameHashComparer); if (idx < 0) { throw new InvalidOperationException ($"Could not map entry '{javaEntry.JavaName}' to array index"); } @@ -282,24 +267,28 @@ void GenerateAndSortJavaHashes (LlvmIrVariable variable, LlvmIrModuleTarget targ { ConstructionState cs = EnsureConstructionState (callerState); LlvmIrGlobalVariable gv = EnsureGlobalVariable (variable); + + for (int i = 0; i < cs.JavaMap.Count; i++) { + TypeMapJava entry = cs.JavaMap[i].Instance; + entry.JavaNameHash = TypeMapHelper.HashJavaName (entry.JavaName, target.Is64Bit); + } + + cs.JavaMap.Sort ((StructureInstance a, StructureInstance b) => a.Instance.JavaNameHash.CompareTo (b.Instance.JavaNameHash)); + Type listType; IList hashes; if (target.Is64Bit) { listType = typeof(List); - cs.JavaMap.Sort ((StructureInstance a, StructureInstance b) => a.Instance.JavaNameHash64.CompareTo (b.Instance.JavaNameHash64)); - var list = new List (); foreach (StructureInstance si in cs.JavaMap) { - list.Add (si.Instance.JavaNameHash64); + list.Add (si.Instance.JavaNameHash); } hashes = list; } else { listType = typeof(List); - cs.JavaMap.Sort ((StructureInstance a, StructureInstance b) => a.Instance.JavaNameHash32.CompareTo (b.Instance.JavaNameHash32)); - var list = new List (); foreach (StructureInstance si in cs.JavaMap) { - list.Add (si.Instance.JavaNameHash32); + list.Add ((uint)si.Instance.JavaNameHash); } hashes = list; } @@ -405,17 +394,5 @@ void PrepareModules (ConstructionState cs) } } - void HashJavaNames (ConstructionState cs) - { - // We generate both 32-bit and 64-bit hashes at the construction time. Which set will be used depends on the target. - // Java map list will also be sorted when the target is known - for (int i = 0; i < cs.JavaMap.Count; i++) { - TypeMapJava entry = cs.JavaMap[i].Instance; - - // The cast is safe, xxHash will return a 32-bit value which (for convenience) was upcast to 64-bit - entry.JavaNameHash32 = (uint)TypeMapHelper.HashJavaName (entry.JavaName, is64Bit: false); - entry.JavaNameHash64 = TypeMapHelper.HashJavaName (entry.JavaName, is64Bit: true); - } - } } } diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingReleaseNativeAssemblyGeneratorCLR.cs b/src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingReleaseNativeAssemblyGeneratorCLR.cs index 61276a109bd..d4589d3b0a8 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingReleaseNativeAssemblyGeneratorCLR.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingReleaseNativeAssemblyGeneratorCLR.cs @@ -145,10 +145,7 @@ sealed class TypeMapJava public string ManagedTypeName; [NativeAssembler (Ignore = true)] - public uint JavaNameHash32; - - [NativeAssembler (Ignore = true)] - public ulong JavaNameHash64; + public ulong JavaNameHash; public uint module_index; @@ -174,19 +171,11 @@ public ModuleMapData (List> entries) } } - sealed class JavaNameHash32Comparer : IComparer> - { - public int Compare (StructureInstance a, StructureInstance b) - { - return a.Instance.JavaNameHash32.CompareTo (b.Instance.JavaNameHash32); - } - } - - sealed class JavaNameHash64Comparer : IComparer> + sealed class JavaNameHashComparer : IComparer> { public int Compare (StructureInstance a, StructureInstance b) { - return a.Instance.JavaNameHash64.CompareTo (b.Instance.JavaNameHash64); + return a.Instance.JavaNameHash.CompareTo (b.Instance.JavaNameHash); } } @@ -207,8 +196,7 @@ sealed class ConstructionState StructureInfo typeMapJavaStructureInfo; StructureInfo typeMapModuleStructureInfo; StructureInfo typeMapModuleEntryStructureInfo; - JavaNameHash32Comparer javaNameHash32Comparer; - JavaNameHash64Comparer javaNameHash64Comparer; + JavaNameHashComparer javaNameHashComparer; #pragma warning disable CS0414 // Field is assigned but its value is never used - might be used for debugging or future functionality ulong moduleCounter = 0; @@ -218,8 +206,7 @@ public TypeMappingReleaseNativeAssemblyGeneratorCLR (TaskLoggingHelper log, Nati : base (log) { this.mappingData = mappingData ?? throw new ArgumentNullException (nameof (mappingData)); - javaNameHash32Comparer = new JavaNameHash32Comparer (); - javaNameHash64Comparer = new JavaNameHash64Comparer (); + javaNameHashComparer = new JavaNameHashComparer (); // Unfortunate, but we have to fix this up before proceeding foreach (TypeMapGenerator.ModuleReleaseData module in mappingData.Modules) { @@ -245,7 +232,6 @@ protected override void Construct (LlvmIrModule module) cs.JavaTypesByName = new Dictionary (StringComparer.Ordinal); InitJavaMap (cs); InitMapModules (cs); - HashJavaNames (cs); PrepareModules (cs); module.AddGlobalVariable ("managed_to_java_map_module_count", mappingData.MapModuleCount); @@ -301,7 +287,6 @@ void SortEntriesAndUpdateJavaIndexes (LlvmIrVariable variable, LlvmIrModuleTarge { ConstructionState cs = EnsureConstructionState (callerState); LlvmIrGlobalVariable gv = EnsureGlobalVariable (variable); - IComparer> hashComparer = target.Is64Bit ? javaNameHash64Comparer : javaNameHash32Comparer; var array = (LlvmIrSectionedArray>)variable.Value; foreach (LlvmIrArraySection> section in array.Sections) { @@ -323,7 +308,7 @@ void SortEntriesAndUpdateJavaIndexes (LlvmIrVariable variable, LlvmIrModuleTarge uint GetJavaEntryIndex (TypeMapJava javaEntry) { var key = new StructureInstance (typeMapJavaStructureInfo, javaEntry); - int idx = cs.JavaMap.BinarySearch (key, hashComparer); + int idx = cs.JavaMap.BinarySearch (key, javaNameHashComparer); if (idx < 0) { throw new InvalidOperationException ($"Could not map entry '{javaEntry.JavaName}' to array index"); } @@ -346,24 +331,28 @@ void GenerateAndSortJavaHashes (LlvmIrVariable variable, LlvmIrModuleTarget targ { ConstructionState cs = EnsureConstructionState (callerState); LlvmIrGlobalVariable gv = EnsureGlobalVariable (variable); + + for (int i = 0; i < cs.JavaMap.Count; i++) { + TypeMapJava entry = cs.JavaMap[i].Instance; + entry.JavaNameHash = TypeMapHelper.HashJavaNameForCLR (entry.JavaName, target.Is64Bit); + } + + cs.JavaMap.Sort ((StructureInstance a, StructureInstance b) => a.Instance.JavaNameHash.CompareTo (b.Instance.JavaNameHash)); + Type listType; IList hashes; if (target.Is64Bit) { listType = typeof(List); - cs.JavaMap.Sort ((StructureInstance a, StructureInstance b) => a.Instance.JavaNameHash64.CompareTo (b.Instance.JavaNameHash64)); - var list = new List (); foreach (StructureInstance si in cs.JavaMap) { - list.Add (si.Instance.JavaNameHash64); + list.Add (si.Instance.JavaNameHash); } hashes = list; } else { listType = typeof(List); - cs.JavaMap.Sort ((StructureInstance a, StructureInstance b) => a.Instance.JavaNameHash32.CompareTo (b.Instance.JavaNameHash32)); - var list = new List (); foreach (StructureInstance si in cs.JavaMap) { - list.Add (si.Instance.JavaNameHash32); + list.Add ((uint)si.Instance.JavaNameHash); } hashes = list; } @@ -508,24 +497,5 @@ void PrepareModules (ConstructionState cs) } } - void HashJavaNames (ConstructionState cs) - { - // We generate both 32-bit and 64-bit hashes at the construction time. Which set will be used depends on the target. - // Java map list will also be sorted when the target is known - var hashes32 = new HashSet (); - var hashes64 = new HashSet (); - - // Generate Java type name hashes... - for (int i = 0; i < cs.JavaMap.Count; i++) { - TypeMapJava entry = cs.JavaMap[i].Instance; - - // The cast is safe, xxHash will return a 32-bit value which (for convenience) was upcast to 64-bit - entry.JavaNameHash32 = (uint)TypeMapHelper.HashJavaNameForCLR (entry.JavaName, is64Bit: false); - hashes32.Add (entry.JavaNameHash32); - - entry.JavaNameHash64 = TypeMapHelper.HashJavaNameForCLR (entry.JavaName, is64Bit: true); - hashes64.Add (entry.JavaNameHash64); - } - } } }