diff --git a/src/Mono.Android/Android.Runtime/JNIEnv.cs b/src/Mono.Android/Android.Runtime/JNIEnv.cs index 904562f25b1..aa27ecba7bc 100644 --- a/src/Mono.Android/Android.Runtime/JNIEnv.cs +++ b/src/Mono.Android/Android.Runtime/JNIEnv.cs @@ -119,6 +119,9 @@ internal static bool ShouldWrapJavaException (Java.Lang.Throwable? t, [CallerMem [DllImport ("libc")] static extern int gettid (); +#if NETCOREAPP + [UnmanagedCallersOnly] +#endif static unsafe void RegisterJniNatives (IntPtr typeName_ptr, int typeName_len, IntPtr jniClass, IntPtr methods_ptr, int methods_len) { string typeName = new string ((char*) typeName_ptr, 0, typeName_len); diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/EnvironmentHelper.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/EnvironmentHelper.cs index 14d34d759a4..1fafe5c14e8 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/EnvironmentHelper.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/EnvironmentHelper.cs @@ -89,7 +89,7 @@ public sealed class ApplicationConfig "map_modules", "map_module_count", "java_type_count", - "java_name_width", + "map_java_hashes", "map_java", "mono_aot_mode_name", }; diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.apkdesc b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.apkdesc index cfd7832cd49..3cbcb3bb767 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.apkdesc +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.apkdesc @@ -5,10 +5,10 @@ "Size": 3032 }, "assemblies/Java.Interop.dll": { - "Size": 55111 + "Size": 55106 }, "assemblies/Mono.Android.dll": { - "Size": 88334 + "Size": 88461 }, "assemblies/rc.bin": { "Size": 1083 @@ -26,13 +26,13 @@ "Size": 2374 }, "assemblies/UnnamedProject.dll": { - "Size": 3551 + "Size": 3546 }, "classes.dex": { "Size": 345328 }, "lib/arm64-v8a/libmonodroid.so": { - "Size": 380832 + "Size": 382304 }, "lib/arm64-v8a/libmonosgen-2.0.so": { "Size": 3192432 @@ -47,7 +47,7 @@ "Size": 150032 }, "lib/arm64-v8a/libxamarin-app.so": { - "Size": 8688 + "Size": 9424 }, "META-INF/BNDLTOOL.RSA": { "Size": 1213 @@ -59,7 +59,7 @@ "Size": 2467 }, "res/drawable-hdpi-v4/icon.png": { - "Size": 4762 + "Size": 4791 }, "res/drawable-mdpi-v4/icon.png": { "Size": 2200 @@ -83,5 +83,5 @@ "Size": 1904 } }, - "PackageSize": 2701303 + "PackageSize": 2705399 } \ No newline at end of file diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleLegacy.apkdesc b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleLegacy.apkdesc index 18bbf23b1d2..33a48ec8ce0 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleLegacy.apkdesc +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleLegacy.apkdesc @@ -5,22 +5,22 @@ "Size": 2604 }, "assemblies/Java.Interop.dll": { - "Size": 67953 + "Size": 67956 }, "assemblies/Mono.Android.dll": { - "Size": 256591 + "Size": 256630 }, "assemblies/mscorlib.dll": { - "Size": 769016 + "Size": 769015 }, "assemblies/System.Core.dll": { - "Size": 28198 + "Size": 28199 }, "assemblies/System.dll": { "Size": 9180 }, "assemblies/UnnamedProject.dll": { - "Size": 2880 + "Size": 2881 }, "classes.dex": { "Size": 347796 @@ -32,7 +32,7 @@ "Size": 750976 }, "lib/arm64-v8a/libmonodroid.so": { - "Size": 297544 + "Size": 296192 }, "lib/arm64-v8a/libmonosgen-2.0.so": { "Size": 4030448 @@ -41,7 +41,7 @@ "Size": 65512 }, "lib/arm64-v8a/libxamarin-app.so": { - "Size": 18272 + "Size": 19960 }, "META-INF/ANDROIDD.RSA": { "Size": 1213 diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.apkdesc b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.apkdesc index 2694418af3e..f3acfe04246 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.apkdesc +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.apkdesc @@ -8,124 +8,124 @@ "Size": 7247 }, "assemblies/Java.Interop.dll": { - "Size": 62006 + "Size": 62017 }, "assemblies/Mono.Android.dll": { - "Size": 441706 + "Size": 442008 }, "assemblies/mscorlib.dll": { - "Size": 3798 + "Size": 3803 }, "assemblies/netstandard.dll": { - "Size": 5499 + "Size": 5503 }, "assemblies/rc.bin": { "Size": 1083 }, "assemblies/System.Collections.Concurrent.dll": { - "Size": 11227 + "Size": 11230 }, "assemblies/System.Collections.dll": { - "Size": 16736 + "Size": 16741 }, "assemblies/System.Collections.NonGeneric.dll": { - "Size": 8439 + "Size": 8443 }, "assemblies/System.ComponentModel.dll": { - "Size": 1961 + "Size": 1965 }, "assemblies/System.ComponentModel.Primitives.dll": { - "Size": 2566 + "Size": 2569 }, "assemblies/System.ComponentModel.TypeConverter.dll": { - "Size": 5968 + "Size": 5970 }, "assemblies/System.Console.dll": { - "Size": 6530 + "Size": 6532 }, "assemblies/System.Core.dll": { - "Size": 1928 + "Size": 1932 }, "assemblies/System.Diagnostics.TraceSource.dll": { - "Size": 6756 + "Size": 6761 }, "assemblies/System.dll": { - "Size": 2275 + "Size": 2279 }, "assemblies/System.Drawing.dll": { - "Size": 1957 + "Size": 1961 }, "assemblies/System.Drawing.Primitives.dll": { - "Size": 12199 + "Size": 12206 }, "assemblies/System.IO.Compression.dll": { - "Size": 17218 + "Size": 17221 }, "assemblies/System.IO.IsolatedStorage.dll": { - "Size": 10566 + "Size": 10569 }, "assemblies/System.Linq.dll": { - "Size": 19474 + "Size": 19480 }, "assemblies/System.Linq.Expressions.dll": { - "Size": 182081 + "Size": 182084 }, "assemblies/System.Net.Http.dll": { - "Size": 65831 + "Size": 65842 }, "assemblies/System.Net.Primitives.dll": { - "Size": 22364 + "Size": 22368 }, "assemblies/System.Net.Requests.dll": { - "Size": 3731 + "Size": 3734 }, "assemblies/System.ObjectModel.dll": { - "Size": 11970 + "Size": 11974 }, "assemblies/System.Private.CoreLib.dll": { - "Size": 757425 + "Size": 757472 }, "assemblies/System.Private.DataContractSerialization.dll": { - "Size": 191072 + "Size": 191079 }, "assemblies/System.Private.Uri.dll": { - "Size": 43677 + "Size": 43502 }, "assemblies/System.Private.Xml.dll": { - "Size": 220171 + "Size": 220183 }, "assemblies/System.Private.Xml.Linq.dll": { - "Size": 17101 + "Size": 17099 }, "assemblies/System.Runtime.CompilerServices.Unsafe.dll": { - "Size": 1214 + "Size": 1216 }, "assemblies/System.Runtime.dll": { - "Size": 2557 + "Size": 2561 }, "assemblies/System.Runtime.Serialization.dll": { - "Size": 1889 + "Size": 1893 }, "assemblies/System.Runtime.Serialization.Formatters.dll": { - "Size": 2634 + "Size": 2637 }, "assemblies/System.Runtime.Serialization.Primitives.dll": { - "Size": 3940 + "Size": 3943 }, "assemblies/System.Security.Cryptography.Algorithms.dll": { - "Size": 6809 + "Size": 6815 }, "assemblies/System.Security.Cryptography.Primitives.dll": { - "Size": 2968 + "Size": 2973 }, "assemblies/System.Text.RegularExpressions.dll": { - "Size": 76698 + "Size": 76702 }, "assemblies/System.Xml.dll": { - "Size": 1779 + "Size": 1782 }, "assemblies/UnnamedProject.dll": { - "Size": 117239 + "Size": 117237 }, "assemblies/Xamarin.AndroidX.Activity.dll": { "Size": 6069 @@ -134,40 +134,40 @@ "Size": 6095 }, "assemblies/Xamarin.AndroidX.AppCompat.dll": { - "Size": 112590 + "Size": 112591 }, "assemblies/Xamarin.AndroidX.CardView.dll": { - "Size": 6809 + "Size": 6810 }, "assemblies/Xamarin.AndroidX.CoordinatorLayout.dll": { "Size": 16603 }, "assemblies/Xamarin.AndroidX.Core.dll": { - "Size": 96723 + "Size": 96722 }, "assemblies/Xamarin.AndroidX.DrawerLayout.dll": { - "Size": 14273 + "Size": 14271 }, "assemblies/Xamarin.AndroidX.Fragment.dll": { - "Size": 39924 + "Size": 39926 }, "assemblies/Xamarin.AndroidX.Legacy.Support.Core.UI.dll": { - "Size": 6132 + "Size": 6133 }, "assemblies/Xamarin.AndroidX.Lifecycle.Common.dll": { "Size": 6592 }, "assemblies/Xamarin.AndroidX.Lifecycle.LiveData.Core.dll": { - "Size": 6671 + "Size": 6672 }, "assemblies/Xamarin.AndroidX.Lifecycle.ViewModel.dll": { - "Size": 3273 + "Size": 3272 }, "assemblies/Xamarin.AndroidX.Loader.dll": { - "Size": 12671 + "Size": 12670 }, "assemblies/Xamarin.AndroidX.RecyclerView.dll": { - "Size": 84688 + "Size": 84687 }, "assemblies/Xamarin.AndroidX.SavedState.dll": { "Size": 5077 @@ -176,13 +176,13 @@ "Size": 10382 }, "assemblies/Xamarin.AndroidX.ViewPager.dll": { - "Size": 17986 + "Size": 17985 }, "assemblies/Xamarin.Forms.Core.dll": { "Size": 528450 }, "assemblies/Xamarin.Forms.Platform.Android.dll": { - "Size": 384996 + "Size": 384997 }, "assemblies/Xamarin.Forms.Platform.dll": { "Size": 56878 @@ -191,13 +191,13 @@ "Size": 60774 }, "assemblies/Xamarin.Google.Android.Material.dll": { - "Size": 40134 + "Size": 40135 }, "classes.dex": { "Size": 3458288 }, "lib/arm64-v8a/libmonodroid.so": { - "Size": 382776 + "Size": 382304 }, "lib/arm64-v8a/libmonosgen-2.0.so": { "Size": 3192432 @@ -212,7 +212,7 @@ "Size": 150032 }, "lib/arm64-v8a/libxamarin-app.so": { - "Size": 133192 + "Size": 98624 }, "META-INF/android.support.design_material.version": { "Size": 12 @@ -1967,5 +1967,5 @@ "Size": 341228 } }, - "PackageSize": 7930399 + "PackageSize": 7942687 } \ No newline at end of file diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsLegacy.apkdesc b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsLegacy.apkdesc index aa11b5cb651..b4adbbf5847 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsLegacy.apkdesc +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsLegacy.apkdesc @@ -8,19 +8,19 @@ "Size": 7215 }, "assemblies/Java.Interop.dll": { - "Size": 68919 + "Size": 68921 }, "assemblies/Mono.Android.dll": { - "Size": 567108 + "Size": 567161 }, "assemblies/Mono.Security.dll": { - "Size": 68431 + "Size": 68433 }, "assemblies/mscorlib.dll": { "Size": 915405 }, "assemblies/System.Core.dll": { - "Size": 164046 + "Size": 164045 }, "assemblies/System.dll": { "Size": 388864 @@ -32,7 +32,7 @@ "Size": 110642 }, "assemblies/System.Numerics.dll": { - "Size": 15682 + "Size": 15683 }, "assemblies/System.Runtime.Serialization.dll": { "Size": 186660 @@ -44,13 +44,13 @@ "Size": 395657 }, "assemblies/UnnamedProject.dll": { - "Size": 116892 + "Size": 116898 }, "assemblies/Xamarin.AndroidX.Activity.dll": { "Size": 7701 }, "assemblies/Xamarin.AndroidX.AppCompat.AppCompatResources.dll": { - "Size": 6650 + "Size": 6651 }, "assemblies/Xamarin.AndroidX.AppCompat.dll": { "Size": 125337 @@ -65,7 +65,7 @@ "Size": 131939 }, "assemblies/Xamarin.AndroidX.DrawerLayout.dll": { - "Size": 15429 + "Size": 15430 }, "assemblies/Xamarin.AndroidX.Fragment.dll": { "Size": 43150 @@ -80,7 +80,7 @@ "Size": 7195 }, "assemblies/Xamarin.AndroidX.Lifecycle.ViewModel.dll": { - "Size": 4874 + "Size": 4875 }, "assemblies/Xamarin.AndroidX.Loader.dll": { "Size": 13589 @@ -92,7 +92,7 @@ "Size": 6283 }, "assemblies/Xamarin.AndroidX.SwipeRefreshLayout.dll": { - "Size": 11271 + "Size": 11270 }, "assemblies/Xamarin.AndroidX.ViewPager.dll": { "Size": 19429 @@ -122,7 +122,7 @@ "Size": 750976 }, "lib/arm64-v8a/libmonodroid.so": { - "Size": 297544 + "Size": 296192 }, "lib/arm64-v8a/libmonosgen-2.0.so": { "Size": 4030448 @@ -131,7 +131,7 @@ "Size": 65512 }, "lib/arm64-v8a/libxamarin-app.so": { - "Size": 139912 + "Size": 105016 }, "META-INF/android.support.design_material.version": { "Size": 12 @@ -1883,5 +1883,5 @@ "Size": 341040 } }, - "PackageSize": 9521310 + "PackageSize": 9533598 } \ No newline at end of file diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrGenerator.cs b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrGenerator.cs index d216ba52726..ec30d96f62a 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrGenerator.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrGenerator.cs @@ -51,7 +51,7 @@ public PackedStructureMember (StructureMemberInfo memberInfo, object? value, } } - sealed class StringSymbolInfo + public sealed class StringSymbolInfo { public readonly string SymbolName; public readonly ulong Size; @@ -377,8 +377,8 @@ bool MaybeWriteStructureString (StructureInfo info, StructureMemberInfo return false; } - string symbolName = WriteUniqueString ($"__{info.Name}_{smi.Info.Name}", str, ref structStringCounter, out ulong size); - instance.AddPointerData (smi, symbolName, size); + StringSymbolInfo stringSymbol = WriteUniqueString ($"__{info.Name}_{smi.Info.Name}", str, ref structStringCounter); + instance.AddPointerData (smi, stringSymbol.SymbolName, stringSymbol.Size); return true; } @@ -477,10 +477,10 @@ public void WriteStructureArray (StructureInfo info, IList instance = instances[i]; + arrayOutput.WriteLine ($"{Indent}; {i}"); WriteStructureBody (info, instance, bodyWriterOptions); if (i < count - 1) { arrayOutput.Write (", "); @@ -508,6 +508,76 @@ public void WriteStructureArray (StructureInfo info, IList (info, instances, LlvmIrVariableOptions.Default, symbolName, writeFieldComment, initialComment); } + public void WriteArray (IList values, string symbolName) + { + WriteEOL (); + WriteEOL (symbolName); + + ulong arrayStringCounter = 0; + var strings = new List (); + + foreach (string s in values) { + StringSymbolInfo symbol = WriteUniqueString ($"__{symbolName}", s, ref arrayStringCounter, LlvmIrVariableOptions.LocalConstexprString); + strings.Add (new StringSymbolInfo (symbol.SymbolName, symbol.Size)); + } + + if (strings.Count > 0) { + Output.WriteLine (); + } + + WriteStringArray (symbolName, LlvmIrVariableOptions.GlobalConstantStringPointer, strings); + } + + public void WriteArray (IList values, LlvmIrVariableOptions options, string symbolName, Func? commentProvider = null) where T: struct + { + bool optimizeOutput = commentProvider == null; + + WriteGlobalSymbolStart (symbolName, options); + string elementType = MapManagedTypeToIR (typeof (T), out ulong size); + Output.WriteLine ($"[{values.Count} x {elementType}] ["); + Output.Write (Indent); + for (int i = 0; i < values.Count; i++) { + if (i != 0) { + if (optimizeOutput) { + Output.Write (','); + if (i % 8 == 0) { + Output.WriteLine ($" ; {i - 8}..{i - 1}"); + Output.Write (Indent); + } else { + Output.Write (' '); + } + } else { + Output.Write (Indent); + } + } + + Output.Write ($"{elementType} {values[i]}"); + + if (!optimizeOutput) { + bool last = i == values.Count - 1; + if (!last) { + Output.Write (','); + } + + string? comment = commentProvider (i, values[i]); + if (!String.IsNullOrEmpty (comment)) { + Output.Write ($" ; {comment}"); + } + + if (!last) { + Output.WriteLine (); + } + } + } + if (optimizeOutput && values.Count / 8 != 0) { + int idx = values.Count - (values.Count % 8); + Output.Write ($" ; {idx}..{values.Count - 1}"); + } + + Output.WriteLine (); + Output.WriteLine ($"], align {GetAggregateAlignment ((int)size, size * (ulong)values.Count)}"); + } + void AssertArraySize (StructureInfo info, StructureMemberInfo smi, ulong length, ulong expectedLength) { if (length == expectedLength) { @@ -911,7 +981,7 @@ public void WriteNameValueArray (string symbolName, IDictionary WriteEOL (); WriteEOL (symbolName); - var strings = new List<(ulong stringSize, string varName)> (); + var strings = new List (); long i = 0; ulong arrayStringCounter = 0; @@ -923,19 +993,31 @@ public void WriteNameValueArray (string symbolName, IDictionary WriteArrayString (value, $"v_{i}"); i++; } + if (strings.Count > 0) { Output.WriteLine (); } - WriteGlobalSymbolStart (symbolName, LlvmIrVariableOptions.GlobalConstantStringPointer); + WriteStringArray (symbolName, LlvmIrVariableOptions.GlobalConstantStringPointer, strings); + + void WriteArrayString (string str, string symbolSuffix) + { + StringSymbolInfo symbol = WriteUniqueString ($"__{symbolName}_{symbolSuffix}", str, ref arrayStringCounter, LlvmIrVariableOptions.LocalConstexprString); + strings.Add (new StringSymbolInfo (symbol.SymbolName, symbol.Size)); + } + } + + void WriteStringArray (string symbolName, LlvmIrVariableOptions options, List strings) + { + WriteGlobalSymbolStart (symbolName, options); Output.Write ($"[{strings.Count} x i8*]"); if (strings.Count > 0) { Output.WriteLine (" ["); for (int j = 0; j < strings.Count; j++) { - ulong size = strings[j].stringSize; - string varName = strings[j].varName; + ulong size = strings[j].Size; + string varName = strings[j].SymbolName; // // Syntax: https://llvm.org/docs/LangRef.html#getelementptr-instruction @@ -961,12 +1043,6 @@ public void WriteNameValueArray (string symbolName, IDictionary Output.Write ("]"); } Output.WriteLine ($", align {GetAggregateAlignment (PointerSize, arraySize)}"); - - void WriteArrayString (string str, string symbolSuffix) - { - string name = WriteUniqueString ($"__{symbolName}_{symbolSuffix}", str, ref arrayStringCounter, LlvmIrVariableOptions.LocalConstexprString, out ulong size); - strings.Add (new (size, name)); - } } /// @@ -1073,20 +1149,9 @@ public string WriteString (string symbolName, string value, LlvmIrVariableOption /// string value. If a new symbol is written, its name is constructed by combining prefix () with value /// of a string counter referenced by the parameter. Symbol is created as a local, C++ constexpr style string. /// - public string WriteUniqueString (string potentialSymbolName, string value, ref ulong counter) - { - return WriteUniqueString (potentialSymbolName, value, ref counter, LlvmIrVariableOptions.LocalConstexprString, out _); - } - - /// - /// Writes a string, creating a new symbol if the is unique or returns name of a previously created symbol with the same - /// string value. If a new symbol is written, its name is constructed by combining prefix () with value - /// of a string counter referenced by the parameter. Symbol is created as a local, C++ constexpr style string. - // String size (in bytes) is returned in . - /// - public string WriteUniqueString (string potentialSymbolName, string value, ref ulong counter, out ulong stringSize) + public StringSymbolInfo WriteUniqueString (string potentialSymbolName, string value, ref ulong counter) { - return WriteUniqueString (potentialSymbolName, value, ref counter, LlvmIrVariableOptions.LocalConstexprString, out stringSize); + return WriteUniqueString (potentialSymbolName, value, ref counter, LlvmIrVariableOptions.LocalConstexprString); } /// @@ -1095,25 +1160,23 @@ public string WriteUniqueString (string potentialSymbolName, string value, ref u /// of a string counter referenced by the parameter. Symbol options (writeability, visibility etc) are specified in the parameter. String size (in bytes) is returned in . /// - public string WriteUniqueString (string potentialSymbolNamePrefix, string value, ref ulong counter, LlvmIrVariableOptions options, out ulong stringSize) + public StringSymbolInfo WriteUniqueString (string potentialSymbolNamePrefix, string value, ref ulong counter, LlvmIrVariableOptions options) { if (value == null) { - stringSize = 0; return null; } StringSymbolInfo info; if (stringSymbolCache.TryGetValue (value, out info)) { - stringSize = info.Size; - return info.SymbolName; + return info; } string newSymbolName = $"{potentialSymbolNamePrefix}.{counter++}"; - WriteString (newSymbolName, value, options, out stringSize); + WriteString (newSymbolName, value, options, out ulong stringSize); info = new StringSymbolInfo (newSymbolName, stringSize); stringSymbolCache.Add (value, info); - return info.SymbolName; + return info; } public virtual void WriteFileTop () diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/NativeTypeMappingData.cs b/src/Xamarin.Android.Build.Tasks/Utilities/NativeTypeMappingData.cs index b5b57c23ec1..f1a5205f746 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/NativeTypeMappingData.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/NativeTypeMappingData.cs @@ -7,31 +7,21 @@ namespace Xamarin.Android.Tasks class NativeTypeMappingData { public TypeMapGenerator.ModuleReleaseData[] Modules { get; } - public IDictionary AssemblyNames { get; } - public string[] JavaTypeNames { get; } public TypeMapGenerator.TypeMapReleaseEntry[] JavaTypes { get; } public uint MapModuleCount { get; } public uint JavaTypeCount { get; } - public uint JavaNameWidth { get; } - public NativeTypeMappingData (Action logger, TypeMapGenerator.ModuleReleaseData[] modules, int javaNameWidth) + public NativeTypeMappingData (Action logger, TypeMapGenerator.ModuleReleaseData[] modules) { Modules = modules ?? throw new ArgumentNullException (nameof (modules)); MapModuleCount = (uint)modules.Length; - JavaNameWidth = (uint)javaNameWidth; - - AssemblyNames = new Dictionary (StringComparer.Ordinal); var tempJavaTypes = new Dictionary (StringComparer.Ordinal); - int managedStringCounter = 0; var moduleComparer = new TypeMapGenerator.ModuleUUIDArrayComparer (); foreach (TypeMapGenerator.ModuleReleaseData data in modules) { - data.AssemblyNameLabel = $"map_aname.{managedStringCounter++}"; - AssemblyNames.Add (data.AssemblyNameLabel, data.AssemblyName); - int moduleIndex = Array.BinarySearch (modules, data, moduleComparer); if (moduleIndex < 0) throw new InvalidOperationException ($"Unable to map module with MVID {data.Mvid} to array index"); @@ -44,16 +34,7 @@ public NativeTypeMappingData (Action logger, TypeMapGenerator.ModuleRele } } - var javaNames = tempJavaTypes.Keys.ToArray (); - Array.Sort (javaNames, StringComparer.Ordinal); - - var javaTypes = new TypeMapGenerator.TypeMapReleaseEntry[javaNames.Length]; - for (int i = 0; i < javaNames.Length; i++) { - javaTypes[i] = tempJavaTypes[javaNames[i]]; - } - - JavaTypes = javaTypes; - JavaTypeNames = javaNames; + JavaTypes = tempJavaTypes.Values.ToArray (); JavaTypeCount = (uint)JavaTypes.Length; } } diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/TypeMapGenerator.cs b/src/Xamarin.Android.Build.Tasks/Utilities/TypeMapGenerator.cs index 15989928466..d5f2c5e17da 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/TypeMapGenerator.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/TypeMapGenerator.cs @@ -40,18 +40,9 @@ public int Compare (ModuleReleaseData left, ModuleReleaseData right) } } - internal sealed class TypeMapEntryArrayComparer : IComparer - { - public int Compare (TypeMapReleaseEntry left, TypeMapReleaseEntry right) - { - return String.CompareOrdinal (left.JavaName, right.JavaName); - } - } - internal sealed class TypeMapReleaseEntry { public string JavaName; - public int JavaNameLength; public string ManagedTypeName; public uint Token; public int AssemblyNameIndex = -1; @@ -67,7 +58,6 @@ internal sealed class ModuleReleaseData public TypeMapReleaseEntry[] Types; public Dictionary DuplicateTypes; public string AssemblyName; - public string AssemblyNameLabel; public string OutputFilePath; public Dictionary TypesScratch; @@ -343,7 +333,6 @@ string GetManagedTypeName (TypeDefinition td) bool GenerateRelease (bool skipJniAddNativeMethodRegistrationAttributeScan, List javaTypes, string outputDirectory, ApplicationConfigTaskState appConfState) { int assemblyId = 0; - int maxJavaNameLength = 0; var knownAssemblies = new Dictionary (StringComparer.Ordinal); var tempModules = new Dictionary (); Dictionary moduleCounter = null; @@ -392,16 +381,12 @@ bool GenerateRelease (bool skipJniAddNativeMethodRegistrationAttributeScan, List // a Java type name to a managed type. This fixes https://github.com/xamarin/xamarin-android/issues/4660 var entry = new TypeMapReleaseEntry { JavaName = javaName, - JavaNameLength = outputEncoding.GetByteCount (javaName), ManagedTypeName = td.FullName, Token = td.MetadataToken.ToUInt32 (), AssemblyNameIndex = knownAssemblies [assemblyName], SkipInJavaToManaged = ShouldSkipInJavaToManaged (td), }; - if (entry.JavaNameLength > maxJavaNameLength) - maxJavaNameLength = entry.JavaNameLength; - if (moduleData.TypesScratch.ContainsKey (entry.JavaName)) { // This is disabled because it costs a lot of time (around 150ms per standard XF Integration app // build) and has no value for the end user. The message is left here because it may be useful to us @@ -415,19 +400,19 @@ bool GenerateRelease (bool skipJniAddNativeMethodRegistrationAttributeScan, List var modules = tempModules.Values.ToArray (); Array.Sort (modules, new ModuleUUIDArrayComparer ()); - var typeMapEntryComparer = new TypeMapEntryArrayComparer (); foreach (ModuleReleaseData module in modules) { if (module.TypesScratch.Count == 0) { module.Types = Array.Empty (); continue; } + // No need to sort here, the LLVM IR generator will compute hashes and sort + // the array on write. module.Types = module.TypesScratch.Values.ToArray (); - Array.Sort (module.Types, typeMapEntryComparer); } NativeTypeMappingData data; - data = new NativeTypeMappingData (logger, modules, maxJavaNameLength + 1); + data = new NativeTypeMappingData (logger, modules); var generator = new TypeMappingReleaseNativeAssemblyGenerator (data); generator.Init (); diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingReleaseNativeAssemblyGenerator.cs b/src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingReleaseNativeAssemblyGenerator.cs index ecc42af1eb5..7804dfba5a7 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingReleaseNativeAssemblyGenerator.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingReleaseNativeAssemblyGenerator.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Text; +using K4os.Hash.xxHash; using Xamarin.Android.Tasks.LLVMIR; namespace Xamarin.Android.Tasks @@ -57,18 +58,11 @@ public override ulong GetBufferSize (object data, string fieldName) } } - sealed class TypeMapJavaContextDataProvider : NativeAssemblerStructContextDataProvider + sealed class JavaNameHashComparer : IComparer> { - public override uint GetMaxInlineWidth (object data, string fieldName) + public int Compare (StructureInstance a, StructureInstance b) { - if (String.Compare ("java_name", fieldName, StringComparison.Ordinal) == 0) { - // Using a static field for this is **very** clunky, but it works in our case since we will - // set that field only once per build session and it allows us to query the array size while - // generating the structure declarations (as required by LLVM IR) - return TypeMapJava.MaxJavaNameLength; - } - - return 0; + return a.Obj.JavaNameHash.CompareTo (b.Obj.JavaNameHash); } } @@ -98,6 +92,9 @@ sealed class TypeMapModule [NativeAssembler (Ignore = true)] public string? DuplicateMapSymbolName; + [NativeAssembler (Ignore = true)] + public TypeMapGenerator.ModuleReleaseData Data; + [NativeAssembler (UsesDataProvider = true, InlineArray = true, InlineArraySize = 16)] public byte[] module_uuid; public uint entry_count; @@ -122,17 +119,17 @@ sealed class TypeMapModule // Order of fields and their type must correspond *exactly* to that in // src/monodroid/jni/xamarin-app.hh TypeMapJava structure - [NativeAssemblerStructContextDataProvider (typeof (TypeMapJavaContextDataProvider))] sealed class TypeMapJava { [NativeAssembler (Ignore = true)] - public static uint MaxJavaNameLength; + public string JavaName; + + [NativeAssembler (Ignore = true)] + public ulong JavaNameHash; public uint module_index; public uint type_token_id; - - [NativeAssembler (UsesDataProvider = true, InlineArray = true, NeedsPadding = true)] - public byte[] java_name; + public uint java_name_index; } sealed class ModuleMapData @@ -140,10 +137,10 @@ sealed class ModuleMapData public string SymbolLabel { get; } public List> Entries { get; } - public ModuleMapData (string symbolLabel) + public ModuleMapData (string symbolLabel, List> entries) { SymbolLabel = symbolLabel; - Entries = new List> (); + Entries = entries; } } @@ -151,37 +148,45 @@ public ModuleMapData (string symbolLabel) StructureInfo typeMapJavaStructureInfo; StructureInfo typeMapModuleStructureInfo; StructureInfo typeMapModuleEntryStructureInfo; - List mapModulesData; List> mapModules; List> javaMap; + Dictionary javaTypesByName; + List javaNames; + JavaNameHashComparer javaNameHashComparer; ulong moduleCounter = 0; public TypeMappingReleaseNativeAssemblyGenerator (NativeTypeMappingData mappingData) { this.mappingData = mappingData ?? throw new ArgumentNullException (nameof (mappingData)); - mapModulesData = new List (); mapModules = new List> (); javaMap = new List> (); + javaTypesByName = new Dictionary (StringComparer.Ordinal); + javaNameHashComparer = new JavaNameHashComparer (); + javaNames = new List (); } public override void Init () { - TypeMapJava.MaxJavaNameLength = mappingData.JavaNameWidth; InitMapModules (); InitJavaMap (); } void InitJavaMap () { + TypeMapJava map_entry; foreach (TypeMapGenerator.TypeMapReleaseEntry entry in mappingData.JavaTypes) { - var map_entry = new TypeMapJava { - module_index = (uint)entry.ModuleIndex, + javaNames.Add (entry.JavaName); + + map_entry = new TypeMapJava { + module_index = (uint)entry.ModuleIndex, // UInt32.MaxValue, type_token_id = entry.SkipInJavaToManaged ? 0 : entry.Token, - java_name = Encoding.UTF8.GetBytes (entry.JavaName), + java_name_index = (uint)(javaNames.Count - 1), + JavaName = entry.JavaName, }; javaMap.Add (new StructureInstance (map_entry)); + javaTypesByName.Add (map_entry.JavaName, map_entry); } } @@ -200,6 +205,7 @@ void InitMapModules () MVID = data.Mvid, MapSymbolName = mapName, DuplicateMapSymbolName = duplicateMapName.Length == 0 ? null : duplicateMapName, + Data = data, module_uuid = data.MvidBytes, entry_count = (uint)data.Types.Length, @@ -208,73 +214,153 @@ void InitMapModules () java_name_width = 0, }; - InitMapModuleData (mapName, data.Types, mapModulesData); - if (data.DuplicateTypes.Count > 0) { - InitMapModuleData (duplicateMapName, data.DuplicateTypes.Values, mapModulesData); - } - mapModules.Add (new StructureInstance (map_module)); } } - void InitMapModuleData (string moduleDataSymbolLabel, IEnumerable moduleEntries, List allModulesData) + protected override void MapStructures (LlvmIrGenerator generator) { - var tokens = new Dictionary (); - foreach (TypeMapGenerator.TypeMapReleaseEntry entry in moduleEntries) { - int idx = Array.BinarySearch (mappingData.JavaTypeNames, entry.JavaName, StringComparer.Ordinal); - if (idx < 0) - throw new InvalidOperationException ($"Could not map entry '{entry.JavaName}' to array index"); + generator.MapStructure (); + typeMapJavaStructureInfo = generator.MapStructure (); + typeMapModuleStructureInfo = generator.MapStructure (); + typeMapModuleEntryStructureInfo = generator.MapStructure (); + } - tokens[entry.Token] = (uint)idx; + // Prepare module map entries by sorting them on the managed token, and then mapping each entry to its corresponding Java type map index. + // Requires that `javaMap` is sorted on the type name hash. + void PrepareMapModuleData (string moduleDataSymbolLabel, IEnumerable moduleEntries, List allModulesData) + { + var mapModuleEntries = new List> (); + foreach (TypeMapGenerator.TypeMapReleaseEntry entry in moduleEntries) { + var map_entry = new TypeMapModuleEntry { + type_token_id = entry.Token, + java_map_index = GetJavaEntryIndex (entry.JavaName), + }; + mapModuleEntries.Add (new StructureInstance (map_entry)); } - var sortedTokens = tokens.Keys.ToArray (); - Array.Sort (sortedTokens); + mapModuleEntries.Sort ((StructureInstance a, StructureInstance b) => a.Obj.type_token_id.CompareTo (b.Obj.type_token_id)); + allModulesData.Add (new ModuleMapData (moduleDataSymbolLabel, mapModuleEntries)); - var moduleData = new ModuleMapData (moduleDataSymbolLabel); - foreach (uint token in sortedTokens) { - var map_entry = new TypeMapModuleEntry { - type_token_id = token, - java_map_index = tokens[token], - }; + uint GetJavaEntryIndex (string javaTypeName) + { + if (!javaTypesByName.TryGetValue (javaTypeName, out TypeMapJava javaType)) { + throw new InvalidOperationException ($"INTERNAL ERROR: Java type '{javaTypeName}' not found in cache"); + } - moduleData.Entries.Add (new StructureInstance (map_entry)); - } + var key = new StructureInstance (javaType); + int idx = javaMap.BinarySearch (key, javaNameHashComparer); + if (idx < 0) { + throw new InvalidOperationException ($"Could not map entry '{javaTypeName}' to array index"); + } - allModulesData.Add (moduleData); + return (uint)idx; + } } - protected override void MapStructures (LlvmIrGenerator generator) + // Generate hashes for all Java type names, then sort javaMap on the name hash. This has to be done in the writing phase because hashes + // will depend on architecture (or, actually, on its bitness) and may differ between architectures (they will be the same for all architectures + // with the same bitness) + (List allMapModulesData, List javaMapHashes) PrepareMapsForWriting (LlvmIrGenerator generator) { - generator.MapStructure (); - typeMapJavaStructureInfo = generator.MapStructure (); - typeMapModuleStructureInfo = generator.MapStructure (); - typeMapModuleEntryStructureInfo = generator.MapStructure (); + bool is64Bit = generator.Is64Bit; + + // Generate Java type name hashes... + for (int i = 0; i < javaMap.Count; i++) { + TypeMapJava entry = javaMap[i].Obj; + entry.JavaNameHash = HashName (entry.JavaName); + } + + // ...sort them... + javaMap.Sort ((StructureInstance a, StructureInstance b) => a.Obj.JavaNameHash.CompareTo (b.Obj.JavaNameHash)); + + var allMapModulesData = new List (); + + // ...and match managed types to Java... + foreach (StructureInstance moduleInstance in mapModules) { + TypeMapModule module = moduleInstance.Obj; + PrepareMapModuleData (module.MapSymbolName, module.Data.Types, allMapModulesData); + if (module.Data.DuplicateTypes.Count > 0) { + PrepareMapModuleData (module.DuplicateMapSymbolName, module.Data.DuplicateTypes.Values, allMapModulesData); + } + } + + var javaMapHashes = new HashSet (); + foreach (StructureInstance entry in javaMap) { + javaMapHashes.Add (entry.Obj.JavaNameHash); + } + + return (allMapModulesData, javaMapHashes.ToList ()); + + ulong HashName (string name) + { + if (name.Length == 0) { + return UInt64.MaxValue; + } + + // Native code (EmbeddedAssemblies::typemap_java_to_managed in embedded-assemblies.cc) will operate on wchar_t cast to a byte array, we need to do + // the same + return HashBytes (Encoding.Unicode.GetBytes (name)); + } + + ulong HashBytes (byte[] bytes) + { + if (is64Bit) { + return XXH64.DigestOf (bytes, 0, bytes.Length); + } + + return (ulong)XXH32.DigestOf (bytes, 0, bytes.Length); + } } protected override void Write (LlvmIrGenerator generator) { generator.WriteVariable ("map_module_count", mappingData.MapModuleCount); - generator.WriteVariable ("java_type_count", mappingData.JavaTypeCount); - generator.WriteVariable ("java_name_width", mappingData.JavaNameWidth); + generator.WriteVariable ("java_type_count", javaMap.Count); // must include the padding item, if any - WriteMapModules (generator); - WriteJavaMap (generator); + (List allMapModulesData, List javaMapHashes) = PrepareMapsForWriting (generator); + WriteMapModules (generator, allMapModulesData); + WriteJavaMap (generator, javaMapHashes); } - void WriteJavaMap (LlvmIrGenerator generator) + void WriteJavaMap (LlvmIrGenerator generator, List javaMapHashes) { generator.WriteEOL (); generator.WriteEOL ("Java to managed map"); - generator.WritePackedStructureArray ( + + generator.WriteStructureArray ( typeMapJavaStructureInfo, javaMap, LlvmIrVariableOptions.GlobalConstant, "map_java" ); + + if (generator.Is64Bit) { + WriteHashes (javaMapHashes); + } else { + // A bit ugly, but simple. We know that hashes are really 32-bit, so we can cast without + // worrying. + var hashes = new List (javaMapHashes.Count); + foreach (ulong hash in javaMapHashes) { + hashes.Add ((uint)hash); + } + WriteHashes (hashes); + } + + generator.WriteArray (javaNames, "java_type_names"); + + void WriteHashes (List hashes) where T: struct + { + generator.WriteArray ( + hashes, + LlvmIrVariableOptions.GlobalConstant, + "map_java_hashes", + (int idx, T value) => $"{idx}: 0x{value:x} => {javaMap[idx].Obj.JavaName}" + ); + } } - void WriteMapModules (LlvmIrGenerator generator) + void WriteMapModules (LlvmIrGenerator generator, List mapModulesData) { if (mapModules.Count == 0) { return; diff --git a/src/monodroid/jni/application_dso_stub.cc b/src/monodroid/jni/application_dso_stub.cc index 64dff21b874..0fc78f49b46 100644 --- a/src/monodroid/jni/application_dso_stub.cc +++ b/src/monodroid/jni/application_dso_stub.cc @@ -24,15 +24,16 @@ const TypeMap type_map = { #else const uint32_t map_module_count = 0; const uint32_t java_type_count = 0; -const uint32_t java_name_width = 0; +const char* const java_type_names[] = {}; -const TypeMapModule map_modules[] = {}; +TypeMapModule map_modules[] = {}; const TypeMapJava map_java[] = {}; +const xamarin::android::hash_t map_java_hashes[] = {}; #endif CompressedAssemblies compressed_assemblies = { - /*.count = */ 0, - /*.descriptors = */ nullptr, + .count = 0, + .descriptors = nullptr, }; // diff --git a/src/monodroid/jni/embedded-assemblies.cc b/src/monodroid/jni/embedded-assemblies.cc index ee39aee5a7b..cd842cd54ac 100644 --- a/src/monodroid/jni/embedded-assemblies.cc +++ b/src/monodroid/jni/embedded-assemblies.cc @@ -548,8 +548,8 @@ EmbeddedAssemblies::install_preload_hooks_for_alc () #endif // def NET6 template -const Entry* -EmbeddedAssemblies::binary_search (const Key *key, const Entry *base, size_t nmemb, [[maybe_unused]] size_t precalculated_size) +force_inline const Entry* +EmbeddedAssemblies::binary_search (const Key *key, const Entry *base, size_t nmemb, [[maybe_unused]] size_t precalculated_size) noexcept { static_assert (compare != nullptr, "compare is a required template parameter"); @@ -599,9 +599,65 @@ EmbeddedAssemblies::binary_search (const Key *key, const Entry *base, size_t nme return nullptr; } +force_inline ssize_t +EmbeddedAssemblies::binary_search (hash_t key, const hash_t *arr, size_t n) noexcept +{ + ssize_t left = -1; + ssize_t right = static_cast(n); + + while (right - left > 1) { + ssize_t middle = (left + right) >> 1; + if (arr[middle] < key) { + left = middle; + } else { + right = middle; + } + } + + return arr[right] == key ? right : -1; +} + +force_inline ptrdiff_t +EmbeddedAssemblies::binary_search_branchless (hash_t x, const hash_t *arr, uint32_t len) noexcept +{ + const hash_t *base = arr; + while (len > 1) { + uint32_t half = len >> 1; + // __builtin_prefetch(&base[(len - half) / 2]); + // __builtin_prefetch(&base[half + (len - half) / 2]); + base = (base[half] < x ? &base[half] : base); + len -= half; + } + + //return *(base + (*base < x)); + ptrdiff_t ret = (base + (*base < x)) - arr; + return arr[ret] == x ? ret : -1; +} + +#if defined (RELEASE) && defined (ANDROID) +force_inline const TypeMapModuleEntry* +EmbeddedAssemblies::binary_search (uint32_t key, const TypeMapModuleEntry *arr, uint32_t n) noexcept +{ + ssize_t left = -1; + ssize_t right = static_cast(n); + ssize_t middle; + + while (right - left > 1) { + middle = (left + right) >> 1; + if (arr[middle].type_token_id < key) { + left = middle; + } else { + right = middle; + } + } + + return arr[right].type_token_id == key ? &arr[right] : nullptr; +} +#endif // def RELEASE && def ANDROID + #if defined (DEBUG) || !defined (ANDROID) -int -EmbeddedAssemblies::compare_type_name (const char *type_name, const TypeMapEntry *entry) +force_inline int +EmbeddedAssemblies::compare_type_name (const char *type_name, const TypeMapEntry *entry) noexcept { if (entry == nullptr) return 1; @@ -609,38 +665,39 @@ EmbeddedAssemblies::compare_type_name (const char *type_name, const TypeMapEntry return strcmp (type_name, entry->from); } -MonoReflectionType* -EmbeddedAssemblies::typemap_java_to_managed (const char *java_type_name) +force_inline MonoReflectionType* +EmbeddedAssemblies::typemap_java_to_managed ([[maybe_unused]] hash_t hash, const MonoString *java_type) noexcept { + c_unique_ptr java_type_name {mono_string_to_utf8 (const_cast(java_type))}; const TypeMapEntry *entry = nullptr; if (application_config.instant_run_enabled) { TypeMap *module; for (size_t i = 0; i < type_map_count; i++) { module = &type_maps[i]; - entry = binary_search (java_type_name, module->java_to_managed, module->entry_count); + entry = binary_search (java_type_name.get (), module->java_to_managed, module->entry_count); if (entry != nullptr) break; } } else { - entry = binary_search (java_type_name, type_map.java_to_managed, type_map.entry_count); + entry = binary_search (java_type_name.get (), type_map.java_to_managed, type_map.entry_count); } if (XA_UNLIKELY (entry == nullptr)) { - log_info (LOG_ASSEMBLY, "typemap: unable to find mapping to a managed type from Java type '%s'", java_type_name); + log_info (LOG_ASSEMBLY, "typemap: unable to find mapping to a managed type from Java type '%s'", java_type_name.get ()); return nullptr; } const char *managed_type_name = entry->to; if (managed_type_name == nullptr) { - log_debug (LOG_ASSEMBLY, "typemap: Java type '%s' maps either to an open generic type or an interface type.", java_type_name); + log_debug (LOG_ASSEMBLY, "typemap: Java type '%s' maps either to an open generic type or an interface type.", java_type_name.get ()); return nullptr; } - log_debug (LOG_DEFAULT, "typemap: Java type '%s' corresponds to managed type '%s'", java_type_name, managed_type_name); + log_debug (LOG_DEFAULT, "typemap: Java type '%s' corresponds to managed type '%s'", java_type_name.get (), managed_type_name); MonoType *type = mono_reflection_type_from_name (const_cast(managed_type_name), nullptr); if (XA_UNLIKELY (type == nullptr)) { - log_info (LOG_ASSEMBLY, "typemap: managed type '%s' (mapped from Java type '%s') could not be loaded", managed_type_name, java_type_name); + log_info (LOG_ASSEMBLY, "typemap: managed type '%s' (mapped from Java type '%s') could not be loaded", managed_type_name, java_type_name.get ()); return nullptr; } @@ -653,46 +710,44 @@ EmbeddedAssemblies::typemap_java_to_managed (const char *java_type_name) return ret; } #else -MonoReflectionType* -EmbeddedAssemblies::typemap_java_to_managed (const char *java_type_name) +force_inline MonoReflectionType* +EmbeddedAssemblies::typemap_java_to_managed (hash_t hash, const MonoString *java_type_name) noexcept { - TypeMapModule *module; - const TypeMapJava *java_entry = binary_search (java_type_name, map_java, java_type_count, type_map_java_struct_size); - if (java_entry == nullptr) { - log_info (LOG_ASSEMBLY, "typemap: unable to find mapping to a managed type from Java type '%s'", java_type_name); - return nullptr; - } - - if (java_entry->module_index >= map_module_count) { - log_warn (LOG_ASSEMBLY, "typemap: mapping from Java type '%s' to managed type has invalid module index", java_type_name); + // In microbrenchmarks, `binary_search_branchless` is faster than `binary_search` but in "real" application tests, + // the simple version appears to yield faster startup... Leaving both for now, for further investigation and + // potential optimizations + ssize_t idx = binary_search (hash, map_java_hashes, java_type_count); + //ptrdiff_t idx = binary_search_branchless (hash, map_java_hashes, java_type_count); + + TypeMapJava const* java_entry = idx >= 0 ? &map_java[idx] : nullptr; + TypeMapModule *module = java_entry != nullptr && java_entry->module_index < map_module_count ? &map_modules[java_entry->module_index] : nullptr; + if (module == nullptr) { + if (java_entry == nullptr) { + log_info (LOG_ASSEMBLY, "typemap: unable to find mapping to a managed type from Java type '%s' (hash 0x%zx)", to_utf8 (java_type_name).get (), hash); + } else { + log_warn (LOG_ASSEMBLY, "typemap: mapping from Java type '%s' to managed type has invalid module index %u", to_utf8 (java_type_name).get (), java_entry->module_index); + } return nullptr; } - module = const_cast(&map_modules[java_entry->module_index]); - const TypeMapModuleEntry *entry = binary_search (&java_entry->type_token_id, module->map, module->entry_count); + const TypeMapModuleEntry *entry = binary_search (java_entry->type_token_id, module->map, module->entry_count); if (entry == nullptr) { - log_info (LOG_ASSEMBLY, "typemap: unable to find mapping from Java type '%s' to managed type with token ID %u in module [%s]", java_type_name, java_entry->type_token_id, MonoGuidString (module->module_uuid).get ()); + log_info (LOG_ASSEMBLY, "typemap: unable to find mapping from Java type '%s' to managed type with token ID %u in module [%s]", to_utf8 (java_type_name).get (), java_entry->type_token_id, MonoGuidString (module->module_uuid).get ()); return nullptr; } - uint32_t type_token_id = java_entry->type_token_id; if (module->image == nullptr) { module->image = mono_image_loaded (module->assembly_name); if (module->image == nullptr) { - // TODO: load - log_error (LOG_ASSEMBLY, "typemap: assembly '%s' not loaded yet!", module->assembly_name); - } - - if (module->image == nullptr) { - log_error (LOG_ASSEMBLY, "typemap: unable to load assembly '%s' when looking up managed type corresponding to Java type '%s'", module->assembly_name, java_type_name); + log_error (LOG_ASSEMBLY, "typemap: unable to load assembly '%s' when looking up managed type corresponding to Java type '%s'", module->assembly_name, to_utf8 (java_type_name).get ()); return nullptr; } } - log_debug (LOG_ASSEMBLY, "typemap: java type '%s' corresponds to managed token id %u (0x%x)", java_type_name, type_token_id, type_token_id); - MonoClass *klass = mono_class_get (module->image, static_cast(type_token_id)); - if (klass == nullptr) { - log_error (LOG_ASSEMBLY, "typemap: unable to find managed type with token ID %u in assembly '%s', corresponding to Java type '%s'", type_token_id, module->assembly_name, java_type_name); + log_debug (LOG_ASSEMBLY, "typemap: java type '%s' corresponds to managed token id %u (0x%x)", to_utf8 (java_type_name).get (), java_entry->type_token_id, java_entry->type_token_id); + MonoClass *klass = mono_class_get (module->image, java_entry->type_token_id); + if (klass == nullptr) [[unlikely]] { + log_error (LOG_ASSEMBLY, "typemap: unable to find managed type with token ID %u in assembly '%s', corresponding to Java type '%s'", java_entry->type_token_id, module->assembly_name, to_utf8 (java_type_name).get ()); return nullptr; } @@ -706,26 +761,16 @@ EmbeddedAssemblies::typemap_java_to_managed (const char *java_type_name) #endif MonoReflectionType *ret = mono_type_get_object (domain, mono_class_get_type (klass)); if (ret == nullptr) { - log_warn (LOG_ASSEMBLY, "typemap: unable to instantiate managed type with token ID %u in assembly '%s', corresponding to Java type '%s'", type_token_id, module->assembly_name, java_type_name); + log_warn (LOG_ASSEMBLY, "typemap: unable to instantiate managed type with token ID %u in assembly '%s', corresponding to Java type '%s'", java_entry->type_token_id, module->assembly_name, to_utf8 (java_type_name).get ()); return nullptr; } return ret; } - -int -EmbeddedAssemblies::compare_java_name (const char *java_name, const TypeMapJava *entry) -{ - if (entry == nullptr || entry->java_name[0] == '\0') { - return -1; - } - - return strcmp (java_name, reinterpret_cast(entry->java_name)); -} #endif MonoReflectionType* -EmbeddedAssemblies::typemap_java_to_managed (MonoString *java_type) +EmbeddedAssemblies::typemap_java_to_managed (MonoString *java_type) noexcept { timing_period total_time; if (XA_UNLIKELY (utils.should_log (LOG_TIMING))) { @@ -738,13 +783,17 @@ EmbeddedAssemblies::typemap_java_to_managed (MonoString *java_type) return nullptr; } - c_unique_ptr java_type_name {mono_string_to_utf8 (java_type)}; - if (XA_UNLIKELY (!java_type_name || *java_type_name == '\0')) { - log_warn (LOG_ASSEMBLY, "typemap: empty Java type name passed to 'typemap_java_to_managed'"); + // We need to generate hash for all the bytes, and since MonoString is Unicode, we double the length to get the + // number of bytes. + int name_len = mono_string_length (java_type) << 1; + if (XA_UNLIKELY (name_len <= 0)) { + log_warn (LOG_ASSEMBLY, "typemap: empty 'java_type' passed to 'typemap_java_to_managed'"); return nullptr; } - MonoReflectionType *ret = typemap_java_to_managed (java_type_name.get ()); + const mono_unichar2 *type_chars = mono_string_chars (java_type); + hash_t hash = xxhash::hash (reinterpret_cast(type_chars), static_cast(name_len)); + MonoReflectionType *ret = typemap_java_to_managed (hash, java_type); if (XA_UNLIKELY (utils.should_log (LOG_TIMING))) { total_time.mark_end (); @@ -756,8 +805,8 @@ EmbeddedAssemblies::typemap_java_to_managed (MonoString *java_type) } #if defined (DEBUG) || !defined (ANDROID) -inline const TypeMapEntry* -EmbeddedAssemblies::typemap_managed_to_java (const char *managed_type_name) +force_inline const TypeMapEntry* +EmbeddedAssemblies::typemap_managed_to_java (const char *managed_type_name) noexcept { const TypeMapEntry *entry = nullptr; @@ -776,8 +825,8 @@ EmbeddedAssemblies::typemap_managed_to_java (const char *managed_type_name) return entry; } -inline const char* -EmbeddedAssemblies::typemap_managed_to_java ([[maybe_unused]] MonoType *type, MonoClass *klass, [[maybe_unused]] const uint8_t *mvid) +force_inline const char* +EmbeddedAssemblies::typemap_managed_to_java ([[maybe_unused]] MonoType *type, MonoClass *klass, [[maybe_unused]] const uint8_t *mvid) noexcept { c_unique_ptr type_name {mono_type_get_name_full (type, MONO_TYPE_NAME_FORMAT_FULL_NAME)}; MonoImage *image = mono_class_get_image (klass); @@ -800,59 +849,38 @@ EmbeddedAssemblies::typemap_managed_to_java ([[maybe_unused]] MonoType *type, Mo return entry->to; } #else -inline int -EmbeddedAssemblies::compare_type_token (const uint32_t *token, const TypeMapModuleEntry *entry) -{ - if (entry == nullptr) { - log_fatal (LOG_ASSEMBLY, "typemap: compare_type_token: entry is nullptr"); - exit (FATAL_EXIT_MISSING_ASSEMBLY); - } - - if (*token < entry->type_token_id) - return -1; - if (*token > entry->type_token_id) - return 1; - return 0; -} - -inline int -EmbeddedAssemblies::compare_mvid (const uint8_t *mvid, const TypeMapModule *module) +force_inline int +EmbeddedAssemblies::compare_mvid (const uint8_t *mvid, const TypeMapModule *module) noexcept { return memcmp (mvid, module->module_uuid, sizeof(module->module_uuid)); } -inline const char* -EmbeddedAssemblies::typemap_managed_to_java ([[maybe_unused]] MonoType *type, MonoClass *klass, const uint8_t *mvid) +force_inline const char* +EmbeddedAssemblies::typemap_managed_to_java ([[maybe_unused]] MonoType *type, MonoClass *klass, const uint8_t *mvid) noexcept { - if (mvid == nullptr) { - log_warn (LOG_ASSEMBLY, "typemap: no mvid specified in call to typemap_managed_to_java"); - return nullptr; - } - - const TypeMapModule *map; - size_t map_entry_count; - map = map_modules; - map_entry_count = map_module_count; - - const TypeMapModule *match = binary_search (mvid, map, map_entry_count); + const TypeMapModule *match = mvid != nullptr ? binary_search (mvid, map_modules, map_module_count) : nullptr; if (match == nullptr) { - log_info (LOG_ASSEMBLY, "typemap: module matching MVID [%s] not found.", MonoGuidString (mvid).get ()); - return nullptr; - } - - if (match->map == nullptr) { - log_warn (LOG_ASSEMBLY, "typemap: module with MVID [%s] has no associated type map.", MonoGuidString (mvid).get ()); + if (mvid == nullptr) { + log_warn (LOG_ASSEMBLY, "typemap: no mvid specified in call to typemap_managed_to_java"); + } else { + log_info (LOG_ASSEMBLY, "typemap: module matching MVID [%s] not found.", MonoGuidString (mvid).get ()); + } return nullptr; } uint32_t token = mono_class_get_type_token (klass); log_debug (LOG_ASSEMBLY, "typemap: MVID [%s] maps to assembly %s, looking for token %d (0x%x), table index %d", MonoGuidString (mvid).get (), match->assembly_name, token, token, token & 0x00FFFFFF); // Each map entry is a pair of 32-bit integers: [TypeTokenID][JavaMapArrayIndex] - const TypeMapModuleEntry *entry = binary_search (&token, match->map, match->entry_count); + const TypeMapModuleEntry *entry = match->map != nullptr ? binary_search (token, match->map, match->entry_count) : nullptr; if (entry == nullptr) { + if (match->map == nullptr) { + log_warn (LOG_ASSEMBLY, "typemap: module with mvid [%s] has no associated type map.", MonoGuidString (mvid).get ()); + return nullptr; + } + if (match->duplicate_count > 0 && match->duplicate_map != nullptr) { log_debug (LOG_ASSEMBLY, "typemap: searching module [%s] duplicate map for token %u (0x%x)", MonoGuidString (mvid).get (), token, token); - entry = binary_search (&token, match->duplicate_map, match->duplicate_count); + entry = binary_search (token, match->duplicate_map, match->duplicate_count); } if (entry == nullptr) { @@ -861,15 +889,17 @@ EmbeddedAssemblies::typemap_managed_to_java ([[maybe_unused]] MonoType *type, Mo } } - uint32_t java_entry_count; - java_entry_count = java_type_count; - if (entry->java_map_index >= java_entry_count) { + if (XA_UNLIKELY (entry->java_map_index >= java_type_count)) { log_warn (LOG_ASSEMBLY, "typemap: type with token %d (0x%x) in module {%s} (%s) has invalid Java type index %u", token, token, MonoGuidString (mvid).get (), match->assembly_name, entry->java_map_index); return nullptr; } - const TypeMapJava *java_entry = reinterpret_cast (reinterpret_cast(map_java) + (type_map_java_struct_size * entry->java_map_index)); - const char *ret = reinterpret_cast(java_entry->java_name); + TypeMapJava const& java_entry = map_java[entry->java_map_index]; + if (XA_UNLIKELY (java_entry.java_name_index >= java_type_count)) { + log_warn (LOG_ASSEMBLY, "typemap: type with token %d (0x%x) in module {%s} (%s) points to invalid Java type at index %u (invalid type name index %u)", token, token, MonoGuidString (mvid).get (), match->assembly_name, entry->java_map_index, java_entry.java_name_index); + return nullptr; + } + const char *ret = java_type_names[java_entry.java_name_index]; if (XA_UNLIKELY (ret == nullptr)) { log_warn (LOG_ASSEMBLY, "typemap: empty Java type name returned for entry at index %u", entry->java_map_index); @@ -890,7 +920,7 @@ EmbeddedAssemblies::typemap_managed_to_java ([[maybe_unused]] MonoType *type, Mo #endif const char* -EmbeddedAssemblies::typemap_managed_to_java (MonoReflectionType *reflection_type, const uint8_t *mvid) +EmbeddedAssemblies::typemap_managed_to_java (MonoReflectionType *reflection_type, const uint8_t *mvid) noexcept { timing_period total_time; if (XA_UNLIKELY (utils.should_log (LOG_TIMING))) { diff --git a/src/monodroid/jni/embedded-assemblies.hh b/src/monodroid/jni/embedded-assemblies.hh index 0e46156edba..d27779930bb 100644 --- a/src/monodroid/jni/embedded-assemblies.hh +++ b/src/monodroid/jni/embedded-assemblies.hh @@ -39,6 +39,12 @@ namespace xamarin::android::internal { struct TypeMappingInfo; #endif +#if defined (RELEASE) && defined (ANDROID) +#define STATIC_IN_ANDROID_RELEASE static +#else +#define STATIC_IN_ANDROID_RELEASE +#endif // def RELEASE && def ANDROID + #if defined (HAVE_CONCEPTS) template concept ByteArrayContainer = requires (T a) { @@ -97,20 +103,19 @@ namespace xamarin::android::internal { public: #if defined (RELEASE) && defined (ANDROID) EmbeddedAssemblies () noexcept - : type_map_java_struct_size (calc_type_map_java_struct_size ()) {} #endif // def RELEASE && def ANDROID #if defined (DEBUG) || !defined (ANDROID) void try_load_typemaps_from_directory (const char *path); #endif - const char* typemap_managed_to_java (MonoReflectionType *type, const uint8_t *mvid); + STATIC_IN_ANDROID_RELEASE const char* typemap_managed_to_java (MonoReflectionType *type, const uint8_t *mvid) noexcept; void install_preload_hooks_for_appdomains (); #if defined (NET6) void install_preload_hooks_for_alc (); #endif // def NET6 - MonoReflectionType* typemap_java_to_managed (MonoString *java_type); + STATIC_IN_ANDROID_RELEASE MonoReflectionType* typemap_java_to_managed (MonoString *java_type) noexcept; /* returns current number of *all* assemblies found from all invocations */ template @@ -162,21 +167,8 @@ namespace xamarin::android::internal { } private: -#if defined (RELEASE) && defined (ANDROID) - static size_t calc_type_map_java_struct_size () - { - size_t struct_size = sizeof(TypeMapJava) + java_name_width; - size_t padding = struct_size % alignof(TypeMapJava); - if (padding > 0) { - struct_size += alignof(TypeMapJava) - padding; - } - - return struct_size; - } -#endif // def RELEASE && def ANDROID - - const char* typemap_managed_to_java (MonoType *type, MonoClass *klass, const uint8_t *mvid); - MonoReflectionType* typemap_java_to_managed (const char *java_type_name); + STATIC_IN_ANDROID_RELEASE const char* typemap_managed_to_java (MonoType *type, MonoClass *klass, const uint8_t *mvid) noexcept; + STATIC_IN_ANDROID_RELEASE MonoReflectionType* typemap_java_to_managed (hash_t hash, const MonoString *java_type_name) noexcept; size_t register_from (const char *apk_file, monodroid_should_register should_register); void gather_bundled_assemblies_from_apk (const char* apk, monodroid_should_register should_register); #if defined (NET6) @@ -206,7 +198,7 @@ namespace xamarin::android::internal { bool typemap_load_file (int dir_fd, const char *dir_path, const char *file_path, TypeMap &module); bool typemap_load_file (BinaryTypeMapHeader &header, const char *dir_path, const char *file_path, int file_fd, TypeMap &module); static ssize_t do_read (int fd, void *buf, size_t count); - const TypeMapEntry *typemap_managed_to_java (const char *managed_type_name); + const TypeMapEntry *typemap_managed_to_java (const char *managed_type_name) noexcept; #endif // DEBUG || !ANDROID static md_mmap_info md_mmap_apk_file (int fd, uint32_t offset, size_t size, const char* filename); @@ -278,17 +270,23 @@ namespace xamarin::android::internal { ; } + static force_inline c_unique_ptr to_utf8 (const MonoString *s) noexcept + { + return c_unique_ptr (mono_string_to_utf8 (const_cast(s))); + } + bool is_debug_file (dynamic_local_string const& name) noexcept; template - const Entry* binary_search (const Key *key, const Entry *base, size_t nmemb, size_t extra_size = 0); + static const Entry* binary_search (const Key *key, const Entry *base, size_t nmemb, size_t extra_size = 0) noexcept; + static ssize_t binary_search (hash_t key, const hash_t *arr, size_t n) noexcept; + static ptrdiff_t binary_search_branchless (hash_t x, const hash_t *arr, uint32_t len) noexcept; #if defined (DEBUG) || !defined (ANDROID) - static int compare_type_name (const char *type_name, const TypeMapEntry *entry); + static int compare_type_name (const char *type_name, const TypeMapEntry *entry) noexcept; #else - static int compare_mvid (const uint8_t *mvid, const TypeMapModule *module); - static int compare_type_token (const uint32_t *token, const TypeMapModuleEntry *entry); - static int compare_java_name (const char *java_name, const TypeMapJava *entry); + static int compare_mvid (const uint8_t *mvid, const TypeMapModule *module) noexcept; + static const TypeMapModuleEntry* binary_search (uint32_t key, const TypeMapModuleEntry *arr, uint32_t n) noexcept; #endif template void set_entry_data (XamarinAndroidBundledAssembly &entry, int apk_fd, uint32_t data_offset, uint32_t data_size, uint32_t prefix_len, uint32_t max_name_size, dynamic_local_string const& entry_name) noexcept; @@ -306,10 +304,6 @@ namespace xamarin::android::internal { size_t bundled_assembly_index = 0; size_t number_of_found_assemblies = 0; -#if defined (RELEASE) && defined (ANDROID) - const size_t type_map_java_struct_size; -#endif // def RELEASE && def ANDROID - #if defined (DEBUG) || !defined (ANDROID) TypeMappingInfo *java_to_managed_maps; TypeMappingInfo *managed_to_java_maps; diff --git a/src/monodroid/jni/monodroid-glue-internal.hh b/src/monodroid/jni/monodroid-glue-internal.hh index 2a6c6775163..8fbb100138f 100644 --- a/src/monodroid/jni/monodroid-glue-internal.hh +++ b/src/monodroid/jni/monodroid-glue-internal.hh @@ -128,6 +128,7 @@ namespace xamarin::android::internal #if defined (NET6) using jnienv_initialize_fn = void (*) (JnienvInitializeArgs*); + using jnienv_register_jni_natives_fn = void (*)(const jchar *typeName_ptr, int32_t typeName_len, jclass jniClass, const jchar *methods_ptr, int32_t methods_len); #endif private: @@ -332,9 +333,11 @@ namespace xamarin::android::internal static void jit_done (MonoProfiler *prof, MonoMethod *method, MonoJitInfo* jinfo); static void thread_start (MonoProfiler *prof, uintptr_t tid); static void thread_end (MonoProfiler *prof, uintptr_t tid); - static MonoReflectionType* typemap_java_to_managed (MonoString *java_type_name); +#if !defined (RELEASE) || !defined (ANDROID) + static MonoReflectionType* typemap_java_to_managed (MonoString *java_type_name) noexcept; + static const char* typemap_managed_to_java (MonoReflectionType *type, const uint8_t *mvid) noexcept; +#endif // !def RELEASE || !def ANDROID - static const char* typemap_managed_to_java (MonoReflectionType *type, const uint8_t *mvid); #if defined (NET6) static void monodroid_debugger_unhandled_exception (MonoException *ex); #endif @@ -382,6 +385,9 @@ namespace xamarin::android::internal static bool startup_in_progress; #if defined (NET6) +#if defined (ANDROID) + jnienv_register_jni_natives_fn jnienv_register_jni_natives = nullptr; +#endif MonoAssemblyLoadContextGCHandle default_alc = nullptr; static std::mutex pinvoke_map_write_lock; diff --git a/src/monodroid/jni/monodroid-glue.cc b/src/monodroid/jni/monodroid-glue.cc index 79a8e24c624..f6f12407667 100644 --- a/src/monodroid/jni/monodroid-glue.cc +++ b/src/monodroid/jni/monodroid-glue.cc @@ -1043,8 +1043,22 @@ MonodroidRuntime::init_android_runtime ( #endif // ndef NET6 JNIEnv *env, jclass runtimeClass, jobject loader) { - mono_add_internal_call ("Java.Interop.TypeManager::monodroid_typemap_java_to_managed", reinterpret_cast(typemap_java_to_managed)); - mono_add_internal_call ("Android.Runtime.JNIEnv::monodroid_typemap_managed_to_java", reinterpret_cast(typemap_managed_to_java)); + constexpr char icall_typemap_java_to_managed[] = "Java.Interop.TypeManager::monodroid_typemap_java_to_managed"; + constexpr char icall_typemap_managed_to_java[] = "Android.Runtime.JNIEnv::monodroid_typemap_managed_to_java"; + +#if defined (RELEASE) && defined (ANDROID) + // The reason for these using is that otherwise the compiler will complain about not being + // able to cast overloaded methods to const void* pointers. + using j2mFn = MonoReflectionType* (*)(MonoString *java_type); + using m2jFn = const char* (*)(MonoReflectionType *type, const uint8_t *mvid); + + mono_add_internal_call (icall_typemap_java_to_managed, reinterpret_cast(static_cast(EmbeddedAssemblies::typemap_java_to_managed))); + mono_add_internal_call (icall_typemap_managed_to_java, reinterpret_cast(static_cast(EmbeddedAssemblies::typemap_managed_to_java))); +#else + mono_add_internal_call (icall_typemap_java_to_managed, reinterpret_cast(typemap_java_to_managed)); + mono_add_internal_call (icall_typemap_managed_to_java, reinterpret_cast(typemap_managed_to_java)); +#endif // def RELEASE && def ANDROID + #if defined (NET6) mono_add_internal_call ("Android.Runtime.JNIEnv::monodroid_debugger_unhandled_exception", reinterpret_cast (monodroid_debugger_unhandled_exception)); mono_add_internal_call ("Android.Runtime.JNIEnv::monodroid_unhandled_exception", reinterpret_cast(monodroid_unhandled_exception)); @@ -1138,6 +1152,10 @@ MonodroidRuntime::init_android_runtime ( registerType = mono_class_get_method_from_name (runtime, "RegisterJniNatives", 5); } else { registerType = mono_get_method (image, application_config.jnienv_registerjninatives_method_token, runtime); +#if defined (NET6) && defined (ANDROID) + MonoError error; + jnienv_register_jni_natives = reinterpret_cast(mono_method_get_unmanaged_callers_only_ftnptr (registerType, &error)); +#endif // def NET6 && def ANDROID } } abort_unless (registerType != nullptr, "INTERNAL ERROR: Unable to find Android.Runtime.JNIEnv.RegisterJniNatives!"); @@ -1960,17 +1978,19 @@ MonodroidRuntime::monodroid_unhandled_exception (MonoObject *java_exception) } #endif // def NET6 +#if !defined (RELEASE) || !defined (ANDROID) MonoReflectionType* -MonodroidRuntime::typemap_java_to_managed (MonoString *java_type_name) +MonodroidRuntime::typemap_java_to_managed (MonoString *java_type_name) noexcept { return embeddedAssemblies.typemap_java_to_managed (java_type_name); } const char* -MonodroidRuntime::typemap_managed_to_java (MonoReflectionType *type, const uint8_t *mvid) +MonodroidRuntime::typemap_managed_to_java (MonoReflectionType *type, const uint8_t *mvid) noexcept { return embeddedAssemblies.typemap_managed_to_java (type, mvid); } +#endif // !def RELEASE || !def ANDROID #if defined (WINDOWS) const char* @@ -2429,7 +2449,7 @@ Java_mono_android_Runtime_initInternal (JNIEnv *env, jclass klass, jstring lang, ); } -inline void +force_inline void MonodroidRuntime::Java_mono_android_Runtime_register (JNIEnv *env, jstring managedType, jclass nativeClass, jstring methods) { timing_period total_time; @@ -2439,20 +2459,13 @@ MonodroidRuntime::Java_mono_android_Runtime_register (JNIEnv *env, jstring manag if (XA_UNLIKELY (utils.should_log (LOG_TIMING))) total_time.mark_start (); - int managedType_len = env->GetStringLength (managedType); + jsize managedType_len = env->GetStringLength (managedType); const jchar *managedType_ptr = env->GetStringChars (managedType, nullptr); - if (XA_UNLIKELY (utils.should_log (LOG_TIMING))) { - const char *mt_ptr = env->GetStringUTFChars (managedType, nullptr); - type.assign (mt_ptr, strlen (mt_ptr)); - env->ReleaseStringUTFChars (managedType, mt_ptr); - - log_info_nocheck (LOG_TIMING, "Runtime.register: registering type `%s`", type.get ()); - } - - int methods_len = env->GetStringLength (methods); + jsize methods_len = env->GetStringLength (methods); const jchar *methods_ptr = env->GetStringChars (methods, nullptr); +#if !defined (NET6) || !defined (ANDROID) void *args[] = { &managedType_ptr, &managedType_len, @@ -2460,8 +2473,9 @@ MonodroidRuntime::Java_mono_android_Runtime_register (JNIEnv *env, jstring manag &methods_ptr, &methods_len, }; - MonoMethod *register_jni_natives = registerType; +#endif // ndef NET6 || ndef ANDROID + #if !defined (NET6) MonoDomain *domain = utils.get_current_domain (/* attach_thread_if_needed */ false); mono_jit_thread_attach (domain); @@ -2475,7 +2489,11 @@ MonodroidRuntime::Java_mono_android_Runtime_register (JNIEnv *env, jstring manag utils.monodroid_runtime_invoke (domain, register_jni_natives, nullptr, args, nullptr); #else // ndef NET6 +#if !defined (ANDROID) mono_runtime_invoke (register_jni_natives, nullptr, args, nullptr); +#else + jnienv_register_jni_natives (managedType_ptr, managedType_len, nativeClass, methods_ptr, methods_len); +#endif // ndef ANDROID #endif // def NET6 env->ReleaseStringChars (methods, methods_ptr); @@ -2484,6 +2502,12 @@ MonodroidRuntime::Java_mono_android_Runtime_register (JNIEnv *env, jstring manag if (XA_UNLIKELY (utils.should_log (LOG_TIMING))) { total_time.mark_end (); + const char *mt_ptr = env->GetStringUTFChars (managedType, nullptr); + type.assign (mt_ptr, strlen (mt_ptr)); + env->ReleaseStringUTFChars (managedType, mt_ptr); + + log_info_nocheck (LOG_TIMING, "Runtime.register: registering type `%s`", type.get ()); + Timing::info (total_time, "Runtime.register: end time"); #if !defined (NET6) dump_counters ("## Runtime.register: type=%s\n", type.get ()); diff --git a/src/monodroid/jni/platform-compat.hh b/src/monodroid/jni/platform-compat.hh index b47f84e555d..62502bbcafa 100644 --- a/src/monodroid/jni/platform-compat.hh +++ b/src/monodroid/jni/platform-compat.hh @@ -37,12 +37,17 @@ typedef struct dirent monodroid_dirent_t; #endif #define force_inline inline __attribute__((always_inline)) +#define never_inline __attribute__((noinline)) #endif // _MSV_VER #ifndef force_inline #define force_inline inline #endif +#ifndef never_inline +#define never_inline +#endif + #ifndef inline #define inline inline #endif diff --git a/src/monodroid/jni/util.hh b/src/monodroid/jni/util.hh index f5055862aa9..82ab57a150e 100644 --- a/src/monodroid/jni/util.hh +++ b/src/monodroid/jni/util.hh @@ -106,7 +106,7 @@ namespace xamarin::android ssize_t recv_uninterrupted (int fd, void *buf, size_t len); jclass get_class_from_runtime_field (JNIEnv *env, jclass runtime, const char *name, bool make_gref = false); - bool should_log (LogCategories category) const + static bool should_log (LogCategories category) noexcept { return (log_categories & category) != 0; } diff --git a/src/monodroid/jni/xamarin-app.hh b/src/monodroid/jni/xamarin-app.hh index 734464ed502..818f3e83c6a 100644 --- a/src/monodroid/jni/xamarin-app.hh +++ b/src/monodroid/jni/xamarin-app.hh @@ -7,6 +7,7 @@ #include #include "monodroid.h" +#include "xxhash.hh" static constexpr uint64_t FORMAT_TAG = 0x015E6972616D58; static constexpr uint32_t COMPRESSED_DATA_MAGIC = 0x5A4C4158; // 'XALZ', little-endian @@ -75,7 +76,7 @@ struct TypeMapJava { uint32_t module_index; uint32_t type_token_id; - uint8_t java_name[]; + uint32_t java_name_index; }; #endif @@ -238,9 +239,10 @@ MONO_API MONO_API_EXPORT const TypeMap type_map; // MUST match src/Xamarin.Andro #else MONO_API MONO_API_EXPORT const uint32_t map_module_count; MONO_API MONO_API_EXPORT const uint32_t java_type_count; -MONO_API MONO_API_EXPORT const uint32_t java_name_width; -MONO_API MONO_API_EXPORT const TypeMapModule map_modules[]; +MONO_API MONO_API_EXPORT const char* const java_type_names[]; +MONO_API MONO_API_EXPORT TypeMapModule map_modules[]; MONO_API MONO_API_EXPORT const TypeMapJava map_java[]; +MONO_API MONO_API_EXPORT const xamarin::android::hash_t map_java_hashes[]; #endif MONO_API MONO_API_EXPORT CompressedAssemblies compressed_assemblies;