diff --git a/src/Mono.Android/Android.Runtime/JNIEnv.cs b/src/Mono.Android/Android.Runtime/JNIEnv.cs index 5bf56887c42..e7fc693a90b 100644 --- a/src/Mono.Android/Android.Runtime/JNIEnv.cs +++ b/src/Mono.Android/Android.Runtime/JNIEnv.cs @@ -670,8 +670,9 @@ public static string GetClassNameFromInstance (IntPtr jobject) } } - [DllImport ("__Internal", CallingConvention = CallingConvention.Cdecl)] - static extern IntPtr monodroid_typemap_managed_to_java (byte[] mvid, int token); + // TODO: `type_name` is temporary, until https://github.com/mono/mono/issues/19377 is fixed + [MethodImplAttribute(MethodImplOptions.InternalCall)] + static extern unsafe IntPtr monodroid_typemap_managed_to_java (Type type, byte* mvid, string type_name); internal static void LogTypemapTrace (StackTrace st) { @@ -685,19 +686,24 @@ internal static void LogTypemapTrace (StackTrace st) } } - internal static string TypemapManagedToJava (Type type) + internal static unsafe string TypemapManagedToJava (Type type) { if (mvid_bytes == null) mvid_bytes = new byte[16]; - Span mvid = new Span(mvid_bytes); - byte[] mvid_slow = null; + var mvid = new Span(mvid_bytes); + byte[] mvid_data = null; if (!type.Module.ModuleVersionId.TryWriteBytes (mvid)) { monodroid_log (LogLevel.Warn, LogCategories.Default, $"Failed to obtain module MVID using the fast method, falling back to the slow one"); - mvid_slow = type.Module.ModuleVersionId.ToByteArray (); + mvid_data = type.Module.ModuleVersionId.ToByteArray (); + } else { + mvid_data = mvid_bytes; } - IntPtr ret = monodroid_typemap_managed_to_java (mvid_slow == null ? mvid_bytes : mvid_slow, type.MetadataToken); + IntPtr ret; + fixed (byte* mvidptr = mvid_data) { + ret = monodroid_typemap_managed_to_java (type, mvidptr, type.FullName); + } if (ret == IntPtr.Zero) { if (LogTypemapMissStackTrace) { diff --git a/src/Mono.Android/Test/Java.Interop/JnienvTest.cs b/src/Mono.Android/Test/Java.Interop/JnienvTest.cs index d6b54c45c97..75bdedac0e5 100644 --- a/src/Mono.Android/Test/Java.Interop/JnienvTest.cs +++ b/src/Mono.Android/Test/Java.Interop/JnienvTest.cs @@ -383,19 +383,16 @@ public void JavaToManagedTypeMapping () Assert.AreEqual (null, m); } - [DllImport ("__Internal", CallingConvention = CallingConvention.Cdecl)] - static extern IntPtr monodroid_typemap_managed_to_java (byte[] mvid, int token); - [Test] public void ManagedToJavaTypeMapping () { Type type = typeof(Activity); - var m = monodroid_typemap_managed_to_java (type.Module.ModuleVersionId.ToByteArray (), type.MetadataToken); - Assert.AreNotEqual (IntPtr.Zero, m, "`Activity` subclasses Java.Lang.Object, it should be in the typemap!"); + string m = JNIEnv.TypemapManagedToJava (type); + Assert.AreNotEqual (null, m, "`Activity` subclasses Java.Lang.Object, it should be in the typemap!"); type = typeof (JnienvTest); - m = monodroid_typemap_managed_to_java (type.Module.ModuleVersionId.ToByteArray (), type.MetadataToken); - Assert.AreEqual (IntPtr.Zero, m, "`JnienvTest` does *not* subclass Java.Lang.Object, it should *not* be in the typemap!"); + m = JNIEnv.TypemapManagedToJava (type); + Assert.AreEqual (null, m, "`JnienvTest` does *not* subclass Java.Lang.Object, it should *not* be in the typemap!"); } [Test] diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs index 2570de86a1d..a738f99f28e 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs @@ -402,7 +402,7 @@ void SaveResource (string resource, string filename, string destDir, Func types) { var tmg = new TypeMapGenerator ((string message) => Log.LogDebugMessage (message), SupportedAbis); - if (!tmg.Generate (SkipJniAddNativeMethodRegistrationAttributeScan, types, TypemapOutputDirectory, GenerateNativeAssembly, out ApplicationConfigTaskState appConfState)) + if (!tmg.Generate (Debug, SkipJniAddNativeMethodRegistrationAttributeScan, types, TypemapOutputDirectory, GenerateNativeAssembly, out ApplicationConfigTaskState appConfState)) throw new XamarinAndroidException (4308, Properties.Resources.XA4308); GeneratedBinaryTypeMaps = tmg.GeneratedBinaryTypeMaps.ToArray (); BuildEngine4.RegisterTaskObject (ApplicationConfigTaskState.RegisterTaskObjectKey, appConfState, RegisteredTaskObjectLifetime.Build, allowEarlyCollection: false); diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/IncrementalBuildTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/IncrementalBuildTest.cs index 855fa7018cc..c1d6353aac5 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/IncrementalBuildTest.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/IncrementalBuildTest.cs @@ -832,7 +832,6 @@ public void GenerateJavaStubsAndAssembly ([Values (true, false)] bool isRelease) readonly string [] ExpectedAssemblyFiles = new [] { Path.Combine ("android", "environment.armeabi-v7a.o"), Path.Combine ("android", "environment.armeabi-v7a.s"), - Path.Combine ("android", "typemaps.armeabi-v7a-managed.inc"), Path.Combine ("android", "typemaps.armeabi-v7a-shared.inc"), Path.Combine ("android", "typemaps.armeabi-v7a.o"), Path.Combine ("android", "typemaps.armeabi-v7a.s"), diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/ARMNativeAssemblerTargetProvider.cs b/src/Xamarin.Android.Build.Tasks/Utilities/ARMNativeAssemblerTargetProvider.cs index e563f64ffe6..755e63fcd88 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/ARMNativeAssemblerTargetProvider.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/ARMNativeAssemblerTargetProvider.cs @@ -14,6 +14,7 @@ class ARMNativeAssemblerTargetProvider : NativeAssemblerTargetProvider public override string AbiName => Is64Bit ? ARMV8a : ARMV7a; public override uint MapModulesAlignBits => Is64Bit ? 3u : 2u; public override uint MapJavaAlignBits { get; } = 2; + public override uint DebugTypeMapAlignBits => Is64Bit ? 3u : 2u; public ARMNativeAssemblerTargetProvider (bool is64Bit) { diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/NativeAssemblerTargetProvider.cs b/src/Xamarin.Android.Build.Tasks/Utilities/NativeAssemblerTargetProvider.cs index 6b379232078..8d5795e3b60 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/NativeAssemblerTargetProvider.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/NativeAssemblerTargetProvider.cs @@ -11,6 +11,7 @@ abstract class NativeAssemblerTargetProvider public abstract string AbiName { get; } public abstract uint MapModulesAlignBits { get; } public abstract uint MapJavaAlignBits { get; } + public abstract uint DebugTypeMapAlignBits { get; } public virtual string MapType () { diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/NativeTypeMappingData.cs b/src/Xamarin.Android.Build.Tasks/Utilities/NativeTypeMappingData.cs index c915d6d46a8..b5b57c23ec1 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/NativeTypeMappingData.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/NativeTypeMappingData.cs @@ -6,16 +6,16 @@ namespace Xamarin.Android.Tasks { class NativeTypeMappingData { - public TypeMapGenerator.ModuleData[] Modules { get; } + public TypeMapGenerator.ModuleReleaseData[] Modules { get; } public IDictionary AssemblyNames { get; } public string[] JavaTypeNames { get; } - public TypeMapGenerator.TypeMapEntry[] JavaTypes { get; } + public TypeMapGenerator.TypeMapReleaseEntry[] JavaTypes { get; } public uint MapModuleCount { get; } public uint JavaTypeCount { get; } public uint JavaNameWidth { get; } - public NativeTypeMappingData (Action logger, TypeMapGenerator.ModuleData[] modules, int javaNameWidth) + public NativeTypeMappingData (Action logger, TypeMapGenerator.ModuleReleaseData[] modules, int javaNameWidth) { Modules = modules ?? throw new ArgumentNullException (nameof (modules)); @@ -24,11 +24,11 @@ public NativeTypeMappingData (Action logger, TypeMapGenerator.ModuleData AssemblyNames = new Dictionary (StringComparer.Ordinal); - var tempJavaTypes = new Dictionary (StringComparer.Ordinal); + var tempJavaTypes = new Dictionary (StringComparer.Ordinal); int managedStringCounter = 0; var moduleComparer = new TypeMapGenerator.ModuleUUIDArrayComparer (); - foreach (TypeMapGenerator.ModuleData data in modules) { + foreach (TypeMapGenerator.ModuleReleaseData data in modules) { data.AssemblyNameLabel = $"map_aname.{managedStringCounter++}"; AssemblyNames.Add (data.AssemblyNameLabel, data.AssemblyName); @@ -36,7 +36,7 @@ public NativeTypeMappingData (Action logger, TypeMapGenerator.ModuleData if (moduleIndex < 0) throw new InvalidOperationException ($"Unable to map module with MVID {data.Mvid} to array index"); - foreach (TypeMapGenerator.TypeMapEntry entry in data.Types) { + foreach (TypeMapGenerator.TypeMapReleaseEntry entry in data.Types) { entry.ModuleIndex = moduleIndex; if (tempJavaTypes.ContainsKey (entry.JavaName)) continue; @@ -47,7 +47,7 @@ public NativeTypeMappingData (Action logger, TypeMapGenerator.ModuleData var javaNames = tempJavaTypes.Keys.ToArray (); Array.Sort (javaNames, StringComparer.Ordinal); - var javaTypes = new TypeMapGenerator.TypeMapEntry[javaNames.Length]; + var javaTypes = new TypeMapGenerator.TypeMapReleaseEntry[javaNames.Length]; for (int i = 0; i < javaNames.Length; i++) { javaTypes[i] = tempJavaTypes[javaNames[i]]; } diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/TypeMapGenerator.cs b/src/Xamarin.Android.Build.Tasks/Utilities/TypeMapGenerator.cs index 8e5c35f8d75..1c4c168187e 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/TypeMapGenerator.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/TypeMapGenerator.cs @@ -10,11 +10,12 @@ namespace Xamarin.Android.Tasks { class TypeMapGenerator { - const string TypeMapMagicString = "XATM"; // Xamarin Android TypeMap + const string TypeMapMagicString = "XATS"; // Xamarin Android TypeMap const string TypeMapIndexMagicString = "XATI"; // Xamarin Android Typemap Index - const uint TypeMapFormatVersion = 1; // Keep in sync with the value in src/monodroid/jni/xamarin-app.hh + const uint TypeMapFormatVersion = 2; // Keep in sync with the value in src/monodroid/jni/xamarin-app.hh + const string TypemapExtension = ".typemap"; - internal sealed class ModuleUUIDArrayComparer : IComparer + internal sealed class ModuleUUIDArrayComparer : IComparer { int Compare (byte[] left, byte[] right) { @@ -29,21 +30,21 @@ int Compare (byte[] left, byte[] right) return 0; } - public int Compare (ModuleData left, ModuleData right) + public int Compare (ModuleReleaseData left, ModuleReleaseData right) { return Compare (left.MvidBytes, right.MvidBytes); } } - internal sealed class TypeMapEntryArrayComparer : IComparer + internal sealed class TypeMapEntryArrayComparer : IComparer { - public int Compare (TypeMapEntry left, TypeMapEntry right) + public int Compare (TypeMapReleaseEntry left, TypeMapReleaseEntry right) { return String.CompareOrdinal (left.JavaName, right.JavaName); } } - internal sealed class TypeMapEntry + internal sealed class TypeMapReleaseEntry { public string JavaName; public int JavaNameLength; @@ -53,18 +54,39 @@ internal sealed class TypeMapEntry public int ModuleIndex = -1; } - internal sealed class ModuleData + internal sealed class ModuleReleaseData { public Guid Mvid; public byte[] MvidBytes; public AssemblyDefinition Assembly; - public TypeMapEntry[] Types; - public Dictionary DuplicateTypes; + public TypeMapReleaseEntry[] Types; + public Dictionary DuplicateTypes; public string AssemblyName; public string AssemblyNameLabel; public string OutputFilePath; - public Dictionary TypesScratch; + public Dictionary TypesScratch; + } + + internal sealed class TypeMapDebugEntry + { + public string JavaName; + public string JavaLabel; + public string ManagedName; + public string ManagedLabel; + public int JavaIndex; + public int ManagedIndex; + } + + // Widths include the terminating nul character nor the padding! + internal sealed class ModuleDebugData + { + public uint EntryCount; + public uint JavaNameWidth; + public uint ManagedNameWidth; + public List JavaToManagedMap; + public List ManagedToJavaMap; + public string OutputFilePath; } Action logger; @@ -102,7 +124,7 @@ void UpdateApplicationConfig (TypeDefinition javaType, ApplicationConfigTaskStat } } - public bool Generate (bool skipJniAddNativeMethodRegistrationAttributeScan, List javaTypes, string outputDirectory, bool generateNativeAssembly, out ApplicationConfigTaskState appConfState) + public bool Generate (bool debugBuild, bool skipJniAddNativeMethodRegistrationAttributeScan, List javaTypes, string outputDirectory, bool generateNativeAssembly, out ApplicationConfigTaskState appConfState) { if (String.IsNullOrEmpty (outputDirectory)) throw new ArgumentException ("must not be null or empty", nameof (outputDirectory)); @@ -110,16 +132,164 @@ public bool Generate (bool skipJniAddNativeMethodRegistrationAttributeScan, List if (!Directory.Exists (outputDirectory)) Directory.CreateDirectory (outputDirectory); + appConfState = new ApplicationConfigTaskState { + JniAddNativeMethodRegistrationAttributePresent = skipJniAddNativeMethodRegistrationAttributeScan + }; + + string typemapsOutputDirectory = Path.Combine (outputDirectory, "typemaps"); + + if (debugBuild) { + return GenerateDebug (skipJniAddNativeMethodRegistrationAttributeScan, javaTypes, typemapsOutputDirectory, generateNativeAssembly, appConfState); + } + + return GenerateRelease (skipJniAddNativeMethodRegistrationAttributeScan, javaTypes, typemapsOutputDirectory, appConfState); + } + + bool GenerateDebug (bool skipJniAddNativeMethodRegistrationAttributeScan, List javaTypes, string outputDirectory, bool generateNativeAssembly, ApplicationConfigTaskState appConfState) + { + if (generateNativeAssembly) + return GenerateDebugNativeAssembly (skipJniAddNativeMethodRegistrationAttributeScan, javaTypes, outputDirectory, appConfState); + return GenerateDebugFiles (skipJniAddNativeMethodRegistrationAttributeScan, javaTypes, outputDirectory, appConfState); + } + + bool GenerateDebugFiles (bool skipJniAddNativeMethodRegistrationAttributeScan, List javaTypes, string outputDirectory, ApplicationConfigTaskState appConfState) + { + var modules = new Dictionary (StringComparer.Ordinal); + int maxModuleFileNameWidth = 0; + int maxModuleNameWidth = 0; + + foreach (TypeDefinition td in javaTypes) { + UpdateApplicationConfig (td, appConfState); + string moduleName = td.Module.Assembly.Name.Name; + ModuleDebugData module; + + if (!modules.TryGetValue (moduleName, out module)) { + string outputFileName = $"{moduleName}{TypemapExtension}"; + module = new ModuleDebugData { + EntryCount = 0, + JavaNameWidth = 0, + ManagedNameWidth = 0, + JavaToManagedMap = new List (), + ManagedToJavaMap = new List (), + OutputFilePath = Path.Combine (outputDirectory, outputFileName), + }; + + if (moduleName.Length > maxModuleNameWidth) + maxModuleNameWidth = moduleName.Length; + + if (outputFileName.Length > maxModuleFileNameWidth) + maxModuleFileNameWidth = outputFileName.Length; + + modules.Add (moduleName, module); + } + + TypeMapDebugEntry entry = GetDebugEntry (td); + if (entry.JavaName.Length > module.JavaNameWidth) + module.JavaNameWidth = (uint)entry.JavaName.Length + 1; + + if (entry.ManagedName.Length > module.ManagedNameWidth) + module.ManagedNameWidth = (uint)entry.ManagedName.Length + 1; + + module.JavaToManagedMap.Add (entry); + module.ManagedToJavaMap.Add (entry); + } + + foreach (ModuleDebugData module in modules.Values) { + PrepareDebugMaps (module); + } + + string typeMapIndexPath = Path.Combine (outputDirectory, "typemap.index"); + using (var indexWriter = MemoryStreamPool.Shared.CreateBinaryWriter ()) { + OutputModules (modules, indexWriter, maxModuleFileNameWidth + 1); + indexWriter.Flush (); + MonoAndroidHelper.CopyIfStreamChanged (indexWriter.BaseStream, typeMapIndexPath); + } + GeneratedBinaryTypeMaps.Add (typeMapIndexPath); + + GenerateNativeAssembly ( + (NativeAssemblerTargetProvider asmTargetProvider, bool sharedBitsWritten, bool sharedIncludeUsesAbiPrefix) => { + return new TypeMappingDebugNativeAssemblyGenerator (asmTargetProvider, new ModuleDebugData (), outputDirectory, sharedBitsWritten); + } + ); + + return true; + } + + bool GenerateDebugNativeAssembly (bool skipJniAddNativeMethodRegistrationAttributeScan, List javaTypes, string outputDirectory, ApplicationConfigTaskState appConfState) + { + var javaToManaged = new List (); + var managedToJava = new List (); + + foreach (TypeDefinition td in javaTypes) { + UpdateApplicationConfig (td, appConfState); + + TypeMapDebugEntry entry = GetDebugEntry (td); + javaToManaged.Add (entry); + managedToJava.Add (entry); + } + + var data = new ModuleDebugData { + EntryCount = (uint)javaToManaged.Count, + JavaToManagedMap = javaToManaged, + ManagedToJavaMap = managedToJava, + }; + + PrepareDebugMaps (data); + GenerateNativeAssembly ( + (NativeAssemblerTargetProvider asmTargetProvider, bool sharedBitsWritten, bool sharedIncludeUsesAbiPrefix) => { + return new TypeMappingDebugNativeAssemblyGenerator (asmTargetProvider, data, outputDirectory, sharedBitsWritten, sharedIncludeUsesAbiPrefix); + } + ); + + return true; + } + + void PrepareDebugMaps (ModuleDebugData module) + { + module.JavaToManagedMap.Sort ((TypeMapDebugEntry a, TypeMapDebugEntry b) => String.Compare (a.JavaName, b.JavaName, StringComparison.Ordinal)); + module.ManagedToJavaMap.Sort ((TypeMapDebugEntry a, TypeMapDebugEntry b) => String.Compare (a.ManagedName, b.ManagedName, StringComparison.Ordinal)); + + for (int i = 0; i < module.JavaToManagedMap.Count; i++) { + module.JavaToManagedMap[i].JavaIndex = i; + } + + for (int i = 0; i < module.ManagedToJavaMap.Count; i++) { + module.ManagedToJavaMap[i].ManagedIndex = i; + } + } + + TypeMapDebugEntry GetDebugEntry (TypeDefinition td) + { + // This is necessary because Mono runtime will return to us type name with a `.` for nested types (not a + // `/` or a `+`. So, for instance, a type named `DefaultRenderer` found in the + // `Xamarin.Forms.Platform.Android.Platform` class in the `Xamarin.Forms.Platform.Android` assembly will + // be seen here as + // + // Xamarin.Forms.Platform.Android.Platform/DefaultRenderer + // + // The managed land name for the type will be rendered as + // + // Xamarin.Forms.Platform.Android.Platform+DefaultRenderer + // + // And this is the form that we need in the map file + // + string managedTypeName = td.FullName.Replace ('/', '+'); + + return new TypeMapDebugEntry { + JavaName = Java.Interop.Tools.TypeNameMappings.JavaNativeTypeManager.ToJniName (td), + ManagedName = $"{managedTypeName}, {td.Module.Assembly.Name.Name}", + }; + } + + bool GenerateRelease (bool skipJniAddNativeMethodRegistrationAttributeScan, List javaTypes, string outputDirectory, ApplicationConfigTaskState appConfState) + { int assemblyId = 0; int maxJavaNameLength = 0; int maxModuleFileNameLength = 0; var knownAssemblies = new Dictionary (StringComparer.Ordinal); - var tempModules = new Dictionary (); + var tempModules = new Dictionary (); Dictionary moduleCounter = null; var mvidCache = new Dictionary (); - appConfState = new ApplicationConfigTaskState { - JniAddNativeMethodRegistrationAttributePresent = skipJniAddNativeMethodRegistrationAttributeScan - }; foreach (TypeDefinition td in javaTypes) { UpdateApplicationConfig (td, appConfState); @@ -140,40 +310,24 @@ public bool Generate (bool skipJniAddNativeMethodRegistrationAttributeScan, List mvidCache.Add (td.Module.Mvid, moduleUUID); } - ModuleData moduleData; + ModuleReleaseData moduleData; if (!tempModules.TryGetValue (moduleUUID, out moduleData)) { if (moduleCounter == null) moduleCounter = new Dictionary (); - moduleData = new ModuleData { + moduleData = new ModuleReleaseData { Mvid = td.Module.Mvid, MvidBytes = moduleUUID, Assembly = td.Module.Assembly, AssemblyName = td.Module.Assembly.Name.Name, - TypesScratch = new Dictionary (StringComparer.Ordinal), - DuplicateTypes = new Dictionary (), + TypesScratch = new Dictionary (StringComparer.Ordinal), + DuplicateTypes = new Dictionary (), }; tempModules.Add (moduleUUID, moduleData); - - if (!generateNativeAssembly) { - int moduleNum; - if (!moduleCounter.TryGetValue (moduleData.Assembly, out moduleNum)) { - moduleNum = 0; - moduleCounter [moduleData.Assembly] = 0; - } else { - moduleNum++; - moduleCounter [moduleData.Assembly] = moduleNum; - } - - string fileName = $"{moduleData.Assembly.Name.Name}.{moduleNum}.typemap"; - moduleData.OutputFilePath = Path.Combine (outputDirectory, fileName); - if (maxModuleFileNameLength < fileName.Length) - maxModuleFileNameLength = fileName.Length; - } } string javaName = Java.Interop.Tools.TypeNameMappings.JavaNativeTypeManager.ToJniName (td); - var entry = new TypeMapEntry { + var entry = new TypeMapReleaseEntry { JavaName = javaName, JavaNameLength = outputEncoding.GetByteCount (javaName), ManagedTypeName = td.FullName, @@ -181,10 +335,8 @@ public bool Generate (bool skipJniAddNativeMethodRegistrationAttributeScan, List AssemblyNameIndex = knownAssemblies [assemblyName] }; - if (generateNativeAssembly) { - if (entry.JavaNameLength > maxJavaNameLength) - maxJavaNameLength = entry.JavaNameLength; - } + 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 @@ -200,9 +352,9 @@ public bool Generate (bool skipJniAddNativeMethodRegistrationAttributeScan, List Array.Sort (modules, new ModuleUUIDArrayComparer ()); var typeMapEntryComparer = new TypeMapEntryArrayComparer (); - foreach (ModuleData module in modules) { + foreach (ModuleReleaseData module in modules) { if (module.TypesScratch.Count == 0) { - module.Types = new TypeMapEntry[0]; + module.Types = new TypeMapReleaseEntry[0]; continue; } @@ -211,20 +363,19 @@ public bool Generate (bool skipJniAddNativeMethodRegistrationAttributeScan, List } NativeTypeMappingData data; - if (!generateNativeAssembly) { - string typeMapIndexPath = Path.Combine (outputDirectory, "typemap.index"); - using (var indexWriter = MemoryStreamPool.Shared.CreateBinaryWriter ()) { - OutputModules (modules, indexWriter, maxModuleFileNameLength + 1); - indexWriter.Flush (); - MonoAndroidHelper.CopyIfStreamChanged (indexWriter.BaseStream, typeMapIndexPath); + data = new NativeTypeMappingData (logger, modules, maxJavaNameLength + 1); + + GenerateNativeAssembly ( + (NativeAssemblerTargetProvider asmTargetProvider, bool sharedBitsWritten, bool sharedIncludeUsesAbiPrefix) => { + return new TypeMappingReleaseNativeAssemblyGenerator (asmTargetProvider, data, outputDirectory, sharedBitsWritten, sharedIncludeUsesAbiPrefix); } - GeneratedBinaryTypeMaps.Add (typeMapIndexPath); + ); - data = new NativeTypeMappingData (logger, new ModuleData[0], 0); - } else { - data = new NativeTypeMappingData (logger, modules, maxJavaNameLength + 1); - } + return true; + } + void GenerateNativeAssembly (Func getGenerator) + { NativeAssemblerTargetProvider asmTargetProvider; bool sharedBitsWritten = false; bool sharedIncludeUsesAbiPrefix; @@ -253,7 +404,7 @@ public bool Generate (bool skipJniAddNativeMethodRegistrationAttributeScan, List throw new InvalidOperationException ($"Unknown ABI {abi}"); } - var generator = new TypeMappingNativeAssemblyGenerator (asmTargetProvider, data, Path.Combine (outputDirectory, "typemaps"), sharedBitsWritten, sharedIncludeUsesAbiPrefix); + NativeAssemblyGenerator generator = getGenerator (asmTargetProvider, sharedBitsWritten, sharedIncludeUsesAbiPrefix); using (var sw = MemoryStreamPool.Shared.CreateStreamWriter (outputEncoding)) { generator.Write (sw); @@ -263,7 +414,6 @@ public bool Generate (bool skipJniAddNativeMethodRegistrationAttributeScan, List sharedBitsWritten = true; } } - return true; } // Binary index file format, all data is little-endian: @@ -276,37 +426,38 @@ public bool Generate (bool skipJniAddNativeMethodRegistrationAttributeScan, List // // Index entry format: // - // [Module UUID][File name] + // [File name] // // Where: // - // [Module UUID] is 16 bytes long // [File name] is right-padded with characters to the [Module file name width] boundary. // - void OutputModules (ModuleData[] modules, BinaryWriter indexWriter, int moduleFileNameWidth) + void OutputModules (Dictionary modules, BinaryWriter indexWriter, int moduleFileNameWidth) { indexWriter.Write (typemapIndexMagicString); indexWriter.Write (TypeMapFormatVersion); - indexWriter.Write (modules.Length); + indexWriter.Write (modules.Count); indexWriter.Write (moduleFileNameWidth); - foreach (ModuleData data in modules) { - OutputModule (data.MvidBytes, data); - indexWriter.Write (data.MvidBytes); + foreach (var kvp in modules) { + string moduleName = kvp.Key; + ModuleDebugData module = kvp.Value; - string outputFilePath = Path.GetFileName (data.OutputFilePath); + OutputModule (moduleName, module); + + string outputFilePath = Path.GetFileName (module.OutputFilePath); indexWriter.Write (outputEncoding.GetBytes (outputFilePath)); PadField (indexWriter, outputFilePath.Length, moduleFileNameWidth); } } - void OutputModule (byte[] moduleUUID, ModuleData moduleData) + void OutputModule (string moduleName, ModuleDebugData moduleData) { - if (moduleData.Types.Length == 0) + if (moduleData.JavaToManagedMap.Count == 0) return; using (var bw = MemoryStreamPool.Shared.CreateBinaryWriter ()) { - OutputModule (bw, moduleUUID, moduleData); + OutputModule (bw, moduleName, moduleData); bw.Flush (); MonoAndroidHelper.CopyIfStreamChanged (bw.BaseStream, moduleData.OutputFilePath); } @@ -315,104 +466,50 @@ void OutputModule (byte[] moduleUUID, ModuleData moduleData) // Binary file format, all data is little-endian: // - // [Magic string] # XATM - // [Format version] # 32-bit integer, 4 bytes - // [Module UUID] # 16 bytes - // [Entry count] # unsigned 32-bit integer, 4 bytes - // [Duplicate count] # unsigned 32-bit integer, 4 bytes (might be 0) - // [Java type name width] # unsigned 32-bit integer, 4 bytes - // [Assembly name size] # unsigned 32-bit integer, 4 bytes + // [Magic string] # XATS + // [Format version] # 32-bit unsigned integer, 4 bytes + // [Entry count] # 32-bit unsigned integer, 4 bytes + // [Java type name width] # 32-bit unsigned integer, 4 bytes + // [Managed type name width] # 32-bit unsigned integer, 4 bytes + // [Assembly name size] # 32-bit unsigned integer, 4 bytes // [Assembly name] # Non-null terminated assembly name // [Java-to-managed map] # Format described below, [Entry count] entries // [Managed-to-java map] # Format described below, [Entry count] entries - // [Managed-to-java duplicates map] # Map of unique managed IDs which point to the same Java type name (might be empty) // // Java-to-managed map format: // - // [Java type name][Managed type token ID] + // [Java type name][Managed type table index] // // Each name is padded with to the width specified in the [Java type name width] field above. // Names are written without the size prefix, instead they are always terminated with a nul character // to make it easier and faster to handle by the native runtime. // - // Each token ID is an unsigned 32-bit integer, 4 bytes - // - // - // Managed-to-java map format: - // - // [Managed type token ID][Java type name table index] - // - // Both fields are unsigned 32-bit integers, to a total of 8 bytes per entry. Index points into the - // [Java-to-managed map] table above. + // Each [Managed type table index] is an unsigned 32-bit integer, 4 bytes // - // Managed-to-java duplicates map format: // - // Format is identical to [Managed-to-java] above. + // Managed-to-java map is identical to the [Java-to-managed] table above, with the exception of the index + // pointing to the Java name table. // - void OutputModule (BinaryWriter bw, byte[] moduleUUID, ModuleData moduleData) + void OutputModule (BinaryWriter bw, string moduleName, ModuleDebugData moduleData) { bw.Write (moduleMagicString); bw.Write (TypeMapFormatVersion); - bw.Write (moduleUUID); - - var javaNames = new Dictionary (StringComparer.Ordinal); - var managedTypes = new Dictionary (); - int maxJavaNameLength = 0; - - foreach (TypeMapEntry entry in moduleData.Types) { - javaNames.Add (entry.JavaName, entry.Token); - if (entry.JavaNameLength > maxJavaNameLength) - maxJavaNameLength = entry.JavaNameLength; - - managedTypes.Add (entry.Token, 0); + bw.Write (moduleData.JavaToManagedMap.Count); + bw.Write (moduleData.JavaNameWidth); + bw.Write (moduleData.ManagedNameWidth); + bw.Write (moduleName.Length); + bw.Write (outputEncoding.GetBytes (moduleName)); + + foreach (TypeMapDebugEntry entry in moduleData.JavaToManagedMap) { + bw.Write (outputEncoding.GetBytes (entry.JavaName)); + PadField (bw, entry.JavaName.Length, (int)moduleData.JavaNameWidth); + bw.Write (entry.ManagedIndex); } - var javaNameList = javaNames.Keys.ToList (); - foreach (TypeMapEntry entry in moduleData.Types) { - var javaIndex = (uint)javaNameList.IndexOf (entry.JavaName); - managedTypes[entry.Token] = javaIndex; - } - - bw.Write (javaNames.Count); - bw.Write (moduleData.DuplicateTypes.Count); - bw.Write (maxJavaNameLength + 1); - - string assemblyName = moduleData.Assembly.Name.Name; - bw.Write (assemblyName.Length); - bw.Write (outputEncoding.GetBytes (assemblyName)); - - var sortedJavaNames = javaNames.Keys.ToArray (); - Array.Sort (sortedJavaNames, StringComparer.Ordinal); - foreach (string typeName in sortedJavaNames) { - byte[] bytes = outputEncoding.GetBytes (typeName); - bw.Write (bytes); - PadField (bw, bytes.Length, maxJavaNameLength + 1); - bw.Write (javaNames[typeName]); - } - - WriteManagedTypes (managedTypes); - if (moduleData.DuplicateTypes.Count == 0) - return; - - var managedDuplicates = new Dictionary (); - foreach (var kvp in moduleData.DuplicateTypes) { - uint javaIndex = kvp.Key; - uint typeId = kvp.Value.Token; - - managedDuplicates.Add (javaIndex, typeId); - } - - WriteManagedTypes (managedDuplicates); - - void WriteManagedTypes (IDictionary types) - { - var sortedTokens = types.Keys.ToArray (); - Array.Sort (sortedTokens); - - foreach (uint token in sortedTokens) { - bw.Write (token); - bw.Write (types[token]); - } + foreach (TypeMapDebugEntry entry in moduleData.ManagedToJavaMap) { + bw.Write (outputEncoding.GetBytes (entry.ManagedName)); + PadField (bw, entry.ManagedName.Length, (int)moduleData.ManagedNameWidth); + bw.Write (entry.JavaIndex); } } diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingDebugNativeAssemblyGenerator.cs b/src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingDebugNativeAssemblyGenerator.cs new file mode 100644 index 00000000000..fb9d51d2218 --- /dev/null +++ b/src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingDebugNativeAssemblyGenerator.cs @@ -0,0 +1,140 @@ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace Xamarin.Android.Tasks +{ + class TypeMappingDebugNativeAssemblyGenerator : NativeAssemblyGenerator + { + const string JavaToManagedSymbol = "map_java_to_managed"; + const string ManagedToJavaSymbol = "map_managed_to_java"; + const string TypeMapSymbol = "type_map"; // MUST match src/monodroid/xamarin-app.hh + + readonly string baseFileName; + readonly bool sharedBitsWritten; + readonly TypeMapGenerator.ModuleDebugData data; + + public TypeMappingDebugNativeAssemblyGenerator (NativeAssemblerTargetProvider targetProvider, TypeMapGenerator.ModuleDebugData data, string baseFileName, bool sharedBitsWritten, bool sharedIncludeUsesAbiPrefix = false) + : base (targetProvider, baseFileName, sharedIncludeUsesAbiPrefix) + { + if (String.IsNullOrEmpty (baseFileName)) + throw new ArgumentException("must not be null or empty", nameof (baseFileName)); + this.data = data ?? throw new ArgumentNullException (nameof (data)); + + this.baseFileName = baseFileName; + this.sharedBitsWritten = sharedBitsWritten; + } + + protected override void WriteSymbols (StreamWriter output) + { + bool haveJavaToManaged = data.JavaToManagedMap != null && data.JavaToManagedMap.Count > 0; + bool haveManagedToJava = data.ManagedToJavaMap != null && data.ManagedToJavaMap.Count > 0; + + using (var sharedOutput = MemoryStreamPool.Shared.CreateStreamWriter (output.Encoding)) { + WriteSharedBits (sharedOutput, haveJavaToManaged, haveManagedToJava); + sharedOutput.Flush (); + MonoAndroidHelper.CopyIfStreamChanged (sharedOutput.BaseStream, SharedIncludeFile); + } + + if (haveJavaToManaged || haveManagedToJava) { + output.Write (Indent); + output.Write (".include"); + output.Write (Indent); + output.Write ('"'); + output.Write (Path.GetFileName (SharedIncludeFile)); + output.WriteLine ('"'); + + output.WriteLine (); + } + + uint size = 0; + WriteCommentLine (output, "Managed to java map: START", indent: false); + WriteSection (output, $".data.rel.{ManagedToJavaSymbol}", hasStrings: false, writable: true); + WriteStructureSymbol (output, ManagedToJavaSymbol, alignBits: TargetProvider.DebugTypeMapAlignBits, isGlobal: false); + if (haveManagedToJava) { + foreach (TypeMapGenerator.TypeMapDebugEntry entry in data.ManagedToJavaMap) { + size += WritePointer (output, entry.ManagedLabel); + size += WritePointer (output, entry.JavaLabel); + } + } + WriteStructureSize (output, ManagedToJavaSymbol, size, alwaysWriteSize: true); + WriteCommentLine (output, "Managed to java map: END", indent: false); + output.WriteLine (); + + size = 0; + WriteCommentLine (output, "Java to managed map: START", indent: false); + WriteSection (output, $".data.rel.{JavaToManagedSymbol}", hasStrings: false, writable: true); + WriteStructureSymbol (output, JavaToManagedSymbol, alignBits: TargetProvider.DebugTypeMapAlignBits, isGlobal: false); + if (haveJavaToManaged) { + foreach (TypeMapGenerator.TypeMapDebugEntry entry in data.JavaToManagedMap) { + size += WritePointer (output, entry.JavaLabel); + size += WritePointer (output, entry.ManagedLabel); + } + } + WriteStructureSize (output, JavaToManagedSymbol, size, alwaysWriteSize: true); + WriteCommentLine (output, "Java to managed map: END", indent: false); + output.WriteLine (); + + // MUST match src/monodroid/xamarin-app.hh + WriteCommentLine (output, "TypeMap structure"); + WriteSection (output, $".data.rel.ro.{TypeMapSymbol}", hasStrings: false, writable: true); + WriteStructureSymbol (output, TypeMapSymbol, alignBits: TargetProvider.DebugTypeMapAlignBits, isGlobal: true); + + size = WriteStructure (output, packed: false, structureWriter: () => WriteTypeMapStruct (output)); + + WriteStructureSize (output, TypeMapSymbol, size); + } + + // MUST match the TypeMap struct from src/monodroid/xamarin-app.hh + uint WriteTypeMapStruct (StreamWriter output) + { + uint size = 0; + + WriteCommentLine (output, "entry_count"); + size += WriteData (output, data.EntryCount); + + WriteCommentLine (output, "assembly_name (unused in this mode)"); + size += WritePointer (output); + + WriteCommentLine (output, "data (unused in this mode)"); + size += WritePointer (output); + + WriteCommentLine (output, "java_to_managed"); + size += WritePointer (output, JavaToManagedSymbol); + + WriteCommentLine (output, "managed_to_java"); + size += WritePointer (output, ManagedToJavaSymbol); + + return size; + } + + void WriteSharedBits (StreamWriter output, bool haveJavaToManaged, bool haveManagedToJava) + { + string label; + + if (haveJavaToManaged) { + WriteCommentLine (output, "Java type names: START"); + foreach (TypeMapGenerator.TypeMapDebugEntry entry in data.JavaToManagedMap) { + label = $"java_type_name.{entry.JavaIndex}"; + WriteData (output, entry.JavaName, label, isGlobal: false); + entry.JavaLabel = MakeLocalLabel (label); + output.WriteLine (); + } + WriteCommentLine (output, "Java type names: END"); + output.WriteLine (); + } + + if (haveManagedToJava) { + WriteCommentLine (output, "Managed type names: START"); + foreach (TypeMapGenerator.TypeMapDebugEntry entry in data.ManagedToJavaMap) { + label = $"managed_type_name.{entry.ManagedIndex}"; + WriteData (output, entry.ManagedName, label, isGlobal: false); + entry.ManagedLabel = MakeLocalLabel (label); + } + WriteCommentLine (output, "Managed type names: END"); + } + } + } +} diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingNativeAssemblyGenerator.cs b/src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingReleaseNativeAssemblyGenerator.cs similarity index 90% rename from src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingNativeAssemblyGenerator.cs rename to src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingReleaseNativeAssemblyGenerator.cs index db47f557ffc..efb6253177c 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingNativeAssemblyGenerator.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingReleaseNativeAssemblyGenerator.cs @@ -5,20 +5,16 @@ namespace Xamarin.Android.Tasks { - class TypeMappingNativeAssemblyGenerator : NativeAssemblyGenerator + class TypeMappingReleaseNativeAssemblyGenerator : NativeAssemblyGenerator { readonly string baseFileName; readonly NativeTypeMappingData mappingData; readonly bool sharedBitsWritten; - public TypeMappingNativeAssemblyGenerator (NativeAssemblerTargetProvider targetProvider, NativeTypeMappingData mappingData, string baseFileName, bool sharedBitsWritten, bool sharedIncludeUsesAbiPrefix = false) + public TypeMappingReleaseNativeAssemblyGenerator (NativeAssemblerTargetProvider targetProvider, NativeTypeMappingData mappingData, string baseFileName, bool sharedBitsWritten, bool sharedIncludeUsesAbiPrefix = false) : base (targetProvider, baseFileName, sharedIncludeUsesAbiPrefix) { this.mappingData = mappingData ?? throw new ArgumentNullException (nameof (mappingData)); - - if (String.IsNullOrEmpty (baseFileName)) - throw new ArgumentException("must not be null or empty", nameof (baseFileName)); - this.baseFileName = baseFileName; this.sharedBitsWritten = sharedIncludeUsesAbiPrefix ? false : sharedBitsWritten; } @@ -93,13 +89,13 @@ void WriteAssemblyNames (StreamWriter output) } } - void WriteManagedMaps (StreamWriter output, string moduleSymbolName, IEnumerable entries) + void WriteManagedMaps (StreamWriter output, string moduleSymbolName, IEnumerable entries) { if (entries == null) return; var tokens = new Dictionary (); - foreach (TypeMapGenerator.TypeMapEntry entry in entries) { + foreach (TypeMapGenerator.TypeMapReleaseEntry entry in entries) { 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"); @@ -138,7 +134,7 @@ void WriteMapModules (StreamWriter output, StreamWriter mapOutput, string symbol uint size = 0; int moduleCounter = 0; - foreach (TypeMapGenerator.ModuleData data in mappingData.Modules) { + foreach (TypeMapGenerator.ModuleReleaseData data in mappingData.Modules) { string mapName = $"module{moduleCounter++}_managed_to_java"; string duplicateMapName; @@ -160,7 +156,7 @@ void WriteMapModules (StreamWriter output, StreamWriter mapOutput, string symbol output.WriteLine (); } - uint WriteMapModule (StreamWriter output, string mapName, string duplicateMapName, TypeMapGenerator.ModuleData data) + uint WriteMapModule (StreamWriter output, string mapName, string duplicateMapName, TypeMapGenerator.ModuleReleaseData data) { uint size = 0; WriteCommentLine (output, $"module_uuid: {data.Mvid}"); @@ -205,7 +201,7 @@ void WriteJavaMap (StreamWriter output, string symbolName) uint size = 0; int entryCount = 0; - foreach (TypeMapGenerator.TypeMapEntry entry in mappingData.JavaTypes) { + foreach (TypeMapGenerator.TypeMapReleaseEntry entry in mappingData.JavaTypes) { size += WriteJavaMapEntry (output, entry, entryCount++); } @@ -214,7 +210,7 @@ void WriteJavaMap (StreamWriter output, string symbolName) output.WriteLine (); } - uint WriteJavaMapEntry (StreamWriter output, TypeMapGenerator.TypeMapEntry entry, int entryIndex) + uint WriteJavaMapEntry (StreamWriter output, TypeMapGenerator.TypeMapReleaseEntry entry, int entryIndex) { uint size = 0; diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/X86NativeAssemblerTargetProvider.cs b/src/Xamarin.Android.Build.Tasks/Utilities/X86NativeAssemblerTargetProvider.cs index bb36f000526..898dd3116d4 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/X86NativeAssemblerTargetProvider.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/X86NativeAssemblerTargetProvider.cs @@ -13,6 +13,7 @@ class X86NativeAssemblerTargetProvider : NativeAssemblerTargetProvider public override string AbiName => Is64Bit ? X86_64 : X86; public override uint MapModulesAlignBits => Is64Bit ? 4u : 2u; public override uint MapJavaAlignBits => Is64Bit ? 4u : 2u; + public override uint DebugTypeMapAlignBits => Is64Bit ? 4u : 2u; public X86NativeAssemblerTargetProvider (bool is64Bit) { diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets index d13b53cc90f..e25de8f0b69 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets @@ -1053,6 +1053,9 @@ because xbuild doesn't support framework reference assemblies. <_NuGetAssetsFile Condition=" Exists('$(ProjectLockFile)') ">$(ProjectLockFile) <_NuGetAssetsFile Condition=" '$(_NuGetAssetsFile)' == '' and Exists('packages.config') ">packages.config <_NuGetAssetsTimestamp Condition=" '$(_NuGetAssetsFile)' != '' ">$([System.IO.File]::GetLastWriteTime('$(_NuGetAssetsFile)').Ticks) + <_TypeMapKind Condition=" '$(AndroidIncludeDebugSymbols)' != 'True' ">mvid + <_TypeMapKind Condition=" '$(AndroidIncludeDebugSymbols)' == 'True' And '$(_InstantRunEnabled)' == 'True' ">strings-files + <_TypeMapKind Condition=" '$(AndroidIncludeDebugSymbols)' == 'True' And '$(_InstantRunEnabled)' != 'True' ">strings-asm @@ -1083,6 +1086,7 @@ because xbuild doesn't support framework reference assemblies. <_PropertyCacheItems Include="AndroidIncludeDebugSymbols=$(AndroidIncludeDebugSymbols)" /> <_PropertyCacheItems Include="AndroidPackageNamingPolicy=$(AndroidPackageNamingPolicy)" /> <_PropertyCacheItems Include="_NuGetAssetsTimestamp=$(_NuGetAssetsTimestamp)" /> + <_PropertyCacheItems Include="TypeMapKind=$(_TypeMapKind)" /> from); +} + MonoReflectionType* -EmbeddedAssemblies::typemap_java_to_managed (MonoString *java_type) +EmbeddedAssemblies::typemap_java_to_managed (const char *java_type_name) { - timing_period total_time; - if (XA_UNLIKELY (utils.should_log (LOG_TIMING))) { - timing = new Timing (); - total_time.mark_start (); + 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); + if (entry != nullptr) + break; + } + } else { + entry = binary_search (java_type_name, type_map.java_to_managed, type_map.entry_count); } - if (XA_UNLIKELY (java_type == nullptr)) { - log_warn (LOG_ASSEMBLY, "typemap: null 'java_type' passed to 'typemap_java_to_managed'"); + if (XA_UNLIKELY (entry == nullptr)) { + log_warn (LOG_ASSEMBLY, "typemap: unable to find mapping to a managed type from Java type '%s'", java_type_name); return nullptr; } - simple_pointer_guard 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'"); + const char *managed_type_name = entry->to; + log_debug (LOG_DEFAULT, "typemap: Java type '%s' corresponds to managed type '%s'", java_type_name, managed_type_name); + + MonoType *type = mono_reflection_type_from_name (const_cast(managed_type_name), nullptr); + if (XA_UNLIKELY (type == nullptr)) { + log_warn (LOG_ASSEMBLY, "typemap: managed type '%s' (mapped from Java type '%s') could not be loaded", managed_type_name, java_type_name); + return nullptr; + } + + MonoReflectionType *ret = mono_type_get_object (mono_domain_get (), type); + if (XA_UNLIKELY (ret == nullptr)) { + log_warn (LOG_ASSEMBLY, "typemap: unable to instantiate managed type '%s'", managed_type_name); return nullptr; } + return ret; +} +#else +MonoReflectionType* +EmbeddedAssemblies::typemap_java_to_managed (const char *java_type_name) +{ int32_t type_token_id = -1; TypeMapModule *module; -#if defined (DEBUG) || !defined (ANDROID) - if (application_config.instant_run_enabled) { - size_t idx = 0; - for (; idx < module_count; idx++) { - const uint8_t *java_entry = binary_search (java_type_name.get (), modules[idx].java_map, modules[idx].entry_count, modules[idx].java_name_width + 3); - if (java_entry == nullptr) - continue; - type_token_id = *reinterpret_cast(java_entry + modules[idx].java_name_width); - break; - } - - if (idx >= module_count) { - log_error (LOG_ASSEMBLY, "typemap: unable to find module with Java type '%s' mapping", java_type_name.get ()); - return nullptr; - } - - module = &modules[idx]; - } else { -#endif - const TypeMapJava *java_entry = binary_search (java_type_name.get (), map_java, java_type_count, java_name_width); - if (java_entry == nullptr) { - log_warn (LOG_ASSEMBLY, "typemap: unable to find mapping to a managed type from Java type '%s'", java_type_name.get ()); - return nullptr; - } + const TypeMapJava *java_entry = binary_search (java_type_name, map_java, java_type_count, java_name_width); + if (java_entry == nullptr) { + log_warn (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.get ()); - 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); + 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); - if (entry == nullptr) { - log_warn (LOG_ASSEMBLY, "typemap: unable to find mapping from Java type '%s' to managed type with token ID %u in module [%s]", java_type_name.get (), java_entry->type_token_id, MonoGuidString (module->module_uuid).get ()); - return nullptr; - } - type_token_id = java_entry->type_token_id; -#if defined (DEBUG) || !defined (ANDROID) + module = const_cast(&map_modules[java_entry->module_index]); + const TypeMapModuleEntry *entry = binary_search (&java_entry->type_token_id, module->map, module->entry_count); + if (entry == nullptr) { + log_warn (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 ()); + return nullptr; } -#endif + type_token_id = java_entry->type_token_id; if (module->image == nullptr) { module->image = mono_image_loaded (module->assembly_name); @@ -246,23 +257,22 @@ EmbeddedAssemblies::typemap_java_to_managed (MonoString *java_type) } 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.get ()); + 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); return nullptr; } } - log_debug (LOG_ASSEMBLY, "typemap: java type '%s' corresponds to managed token id %u (0x%x)", java_type_name.get (), type_token_id, type_token_id); + 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.get ()); + 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); return nullptr; } MonoReflectionType *ret = mono_type_get_object (mono_domain_get (), mono_class_get_type (klass)); - if (XA_UNLIKELY (utils.should_log (LOG_TIMING))) { - total_time.mark_end (); - - Timing::info (total_time, "Typemap.java_to_managed: end, total time"); + 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); + return nullptr; } return ret; @@ -277,20 +287,10 @@ EmbeddedAssemblies::compare_java_name (const char *java_name, const TypeMapJava return strcmp (java_name, reinterpret_cast(entry->java_name)); } +#endif -#if defined (DEBUG) || !defined (ANDROID) -int -EmbeddedAssemblies::compare_java_name (const char *java_name, const uint8_t *entry) -{ - if (entry == nullptr) - return 1; - - return strcmp (java_name, reinterpret_cast(entry)); -} -#endif // DEBUG || !ANDROID - -const char* -EmbeddedAssemblies::typemap_managed_to_java (const uint8_t *mvid, const int32_t token) +MonoReflectionType* +EmbeddedAssemblies::typemap_java_to_managed (MonoString *java_type) { timing_period total_time; if (XA_UNLIKELY (utils.should_log (LOG_TIMING))) { @@ -298,24 +298,135 @@ EmbeddedAssemblies::typemap_managed_to_java (const uint8_t *mvid, const int32_t total_time.mark_start (); } - if (mvid == nullptr) { - log_warn (LOG_ASSEMBLY, "typemap: no mvid specified in call to typemap_managed_to_java"); + if (XA_UNLIKELY (java_type == nullptr)) { + log_warn (LOG_ASSEMBLY, "typemap: null 'java_type' passed to 'typemap_java_to_managed'"); return nullptr; } - const TypeMapModule *map; - size_t map_entry_count; + simple_pointer_guard 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'"); + return nullptr; + } + + MonoReflectionType *ret = typemap_java_to_managed (java_type_name.get ()); + + if (XA_UNLIKELY (utils.should_log (LOG_TIMING))) { + total_time.mark_end (); + + Timing::info (total_time, "Typemap.java_to_managed: end, total time"); + } + + return ret; +} + #if defined (DEBUG) || !defined (ANDROID) +inline const TypeMapEntry* +EmbeddedAssemblies::typemap_managed_to_java (const char *managed_type_name) +{ + const TypeMapEntry *entry = nullptr; + + log_warn (LOG_DEFAULT, "%s (\"%s\")", __PRETTY_FUNCTION__, managed_type_name == nullptr ? "" : managed_type_name); if (application_config.instant_run_enabled) { - map = modules; - map_entry_count = module_count; + TypeMap *module; + for (size_t i = 0; i < type_map_count; i++) { + module = &type_maps[i]; + entry = binary_search (managed_type_name, module->managed_to_java, module->entry_count); + if (entry != nullptr) + break; + } } else { -#endif - map = map_modules; - map_entry_count = map_module_count; -#if defined (DEBUG) || !defined (ANDROID) + log_warn (LOG_DEFAULT, " type_map == %p", type_map); + log_warn (LOG_DEFAULT, " type_map.managed_to_java == %p", type_map.managed_to_java); + log_warn (LOG_DEFAULT, " type_map.entry_count == %u", type_map.entry_count); + entry = binary_search (managed_type_name, type_map.managed_to_java, type_map.entry_count); } + + return entry; +} + +// TODO: `managed_type_name` is temporary, until https://github.com/mono/mono/issues/19377 is fixed +inline const char* +EmbeddedAssemblies::typemap_managed_to_java (MonoType *type, MonoClass *klass, [[maybe_unused]] const uint8_t *mvid, MonoString *managed_type_name) +{ + constexpr char error_message[] = "typemap: unable to find mapping to a Java type from managed type '%s'"; + +#if 0 + simple_pointer_guard type_name (mono_type_get_name (type)); +#else + simple_pointer_guard type_name (mono_string_to_utf8 (managed_type_name)); #endif + MonoImage *image = mono_class_get_image (klass); + const char *image_name = mono_image_get_name (image); + size_t type_name_len = strlen (type_name.get ()); + size_t image_name_len = strlen (image_name); + size_t full_name_size = type_name_len + image_name_len + 3; + const TypeMapEntry *entry = nullptr; + + if (full_name_size > 512) { // Arbitrary, we should be below this limit in most cases + char full_name[full_name_size]; + + char *p = full_name; + memmove (p, type_name.get (), type_name_len); + p += type_name_len; + *p++ = ','; + *p++ = ' '; + memmove (p, image_name, image_name_len); + p += image_name_len; + *p = '\0'; + + entry = typemap_managed_to_java (full_name); + + if (XA_UNLIKELY (entry == nullptr)) { + log_warn (LOG_ASSEMBLY, error_message, full_name); + } + } else { + simple_pointer_guard full_name = utils.string_concat (type_name.get (), ", ", image_name); + entry = typemap_managed_to_java (full_name.get ()); + if (XA_UNLIKELY (entry == nullptr)) { + log_warn (LOG_ASSEMBLY, error_message, full_name.get ()); + } + } + + if (XA_UNLIKELY (entry == nullptr)) { + return nullptr; + } + + 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); + } + + return *token - entry->type_token_id; +} + +inline int +EmbeddedAssemblies::compare_mvid (const uint8_t *mvid, const TypeMapModule *module) +{ + return memcmp (mvid, module->module_uuid, sizeof(module->module_uuid)); +} + +// TODO: `type_name` is temporary, until https://github.com/mono/mono/issues/19377 is fixed +inline const char* +EmbeddedAssemblies::typemap_managed_to_java ([[maybe_unused]] MonoType *type, MonoClass *klass, const uint8_t *mvid, [[maybe_unused]] MonoString *type_name) +{ + if (mvid == nullptr) { + log_warn (LOG_ASSEMBLY, "typemap: no mvid specified in call to typemap_managed_to_java"); + return nullptr; + } + + uint32_t token = mono_class_get_type_token (klass); + 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); if (match == nullptr) { log_warn (LOG_ASSEMBLY, "typemap: module matching MVID [%s] not found.", MonoGuidString (mvid).get ()); @@ -329,11 +440,11 @@ EmbeddedAssemblies::typemap_managed_to_java (const uint8_t *mvid, const int32_t 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 = binary_search (&token, match->map, match->entry_count); if (entry == 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) { @@ -343,42 +454,20 @@ EmbeddedAssemblies::typemap_managed_to_java (const uint8_t *mvid, const int32_t } uint32_t java_entry_count; -#if defined (DEBUG) || !defined (ANDROID) - if (application_config.instant_run_enabled) { - java_entry_count = match->entry_count; - } else { -#endif - java_entry_count = java_type_count; -#if defined (DEBUG) || !defined (ANDROID) - } -#endif + java_entry_count = java_type_count; if (entry->java_map_index >= java_entry_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 char *ret; -#if defined (DEBUG) || !defined (ANDROID) - if (application_config.instant_run_enabled) { - ret = reinterpret_cast(match->java_map + ((match->java_name_width + 4) * entry->java_map_index)); - } else { -#endif - const TypeMapJava *java_entry = reinterpret_cast (reinterpret_cast(map_java) + ((sizeof(TypeMapJava) + java_name_width) * entry->java_map_index)); - ret = reinterpret_cast(reinterpret_cast(java_entry) + 8); -#if defined (DEBUG) || !defined (ANDROID) - } -#endif + const TypeMapJava *java_entry = reinterpret_cast (reinterpret_cast(map_java) + ((sizeof(TypeMapJava) + java_name_width) * entry->java_map_index)); + ret = reinterpret_cast(reinterpret_cast(java_entry) + 8); if (XA_UNLIKELY (ret == nullptr)) { log_warn (LOG_ASSEMBLY, "typemap: empty Java type name returned for entry at index %u", entry->java_map_index); } - if (XA_UNLIKELY (utils.should_log (LOG_TIMING))) { - total_time.mark_end (); - - Timing::info (total_time, "Typemap.managed_to_java: end, total time"); - } - log_debug ( LOG_ASSEMBLY, "typemap: type with token %d (0x%x) in module {%s} (%s) corresponds to Java type '%s'", @@ -391,22 +480,33 @@ EmbeddedAssemblies::typemap_managed_to_java (const uint8_t *mvid, const int32_t return ret; } +#endif -int -EmbeddedAssemblies::compare_type_token (const int32_t *token, const TypeMapModuleEntry *entry) +// TODO: `type_name` is temporary, until https://github.com/mono/mono/issues/19377 is fixed +const char* +EmbeddedAssemblies::typemap_managed_to_java (MonoReflectionType *reflection_type, const uint8_t *mvid, MonoString *type_name) { - if (entry == nullptr) { - log_fatal (LOG_ASSEMBLY, "typemap: compare_type_token: entry is nullptr"); - exit (FATAL_EXIT_MISSING_ASSEMBLY); + timing_period total_time; + if (XA_UNLIKELY (utils.should_log (LOG_TIMING))) { + timing = new Timing (); + total_time.mark_start (); } - return *token - entry->type_token_id; -} + MonoType *type = mono_reflection_type_get_type (reflection_type); + if (type == nullptr) { + log_warn (LOG_DEFAULT, "Failed to map reflection type to MonoType"); + return nullptr; + } -int -EmbeddedAssemblies::compare_mvid (const uint8_t *mvid, const TypeMapModule *module) -{ - return memcmp (mvid, module->module_uuid, sizeof(module->module_uuid)); + const char *ret = typemap_managed_to_java (type, mono_type_get_class (type), mvid, type_name); + + if (XA_UNLIKELY (utils.should_log (LOG_TIMING))) { + total_time.mark_end (); + + Timing::info (total_time, "Typemap.managed_to_java: end, total time"); + } + + return ret; } EmbeddedAssemblies::md_mmap_info @@ -545,10 +645,8 @@ EmbeddedAssemblies::typemap_read_header ([[maybe_unused]] int dir_fd, const char uint8_t* EmbeddedAssemblies::typemap_load_index (TypeMapIndexHeader &header, size_t file_size, int index_fd) { - constexpr size_t UUID_SIZE = 16; - - size_t entry_size = header.module_file_name_width + UUID_SIZE; - size_t data_size = entry_size * module_count; + size_t entry_size = header.module_file_name_width; + size_t data_size = entry_size * type_map_count; if (sizeof(header) + data_size > file_size) { log_error (LOG_ASSEMBLY, "typemap: index file is too small, expected %u, found %u bytes", data_size + sizeof(header), file_size); return nullptr; @@ -562,9 +660,8 @@ EmbeddedAssemblies::typemap_load_index (TypeMapIndexHeader &header, size_t file_ } uint8_t *p = data; - for (size_t i = 0; i < module_count; i++) { - memcpy (modules[i].module_uuid, p, UUID_SIZE); - modules[i].assembly_name = reinterpret_cast(p + UUID_SIZE); + for (size_t i = 0; i < type_map_count; i++) { + type_maps[i].assembly_name = reinterpret_cast(p); p += entry_size; } @@ -585,8 +682,8 @@ EmbeddedAssemblies::typemap_load_index (int dir_fd, const char *dir_path, const goto cleanup; } - module_count = header.entry_count; - modules = new TypeMapModule[module_count](); + type_map_count = header.entry_count; + type_maps = new TypeMap[type_map_count](); data = typemap_load_index (header, file_size, fd); cleanup: @@ -597,72 +694,72 @@ EmbeddedAssemblies::typemap_load_index (int dir_fd, const char *dir_path, const } bool -EmbeddedAssemblies::typemap_load_file (BinaryTypeMapHeader &header, const char *dir_path, const char *file_path, int file_fd, TypeMapModule &module) +EmbeddedAssemblies::typemap_load_file (BinaryTypeMapHeader &header, const char *dir_path, const char *file_path, int file_fd, TypeMap &module) { size_t alloc_size = ADD_WITH_OVERFLOW_CHECK (size_t, header.assembly_name_length, 1); module.assembly_name = new char[alloc_size]; ssize_t nread = do_read (file_fd, module.assembly_name, header.assembly_name_length); + if (nread != header.assembly_name_length) { + log_error (LOG_ASSEMBLY, "tyemap: failed to read map assembly name from '%s/%s': %s", dir_path, file_path, strerror (errno)); + return false; + } + module.assembly_name [header.assembly_name_length] = 0; module.entry_count = header.entry_count; log_debug ( LOG_ASSEMBLY, - "typemap: '%s/%s':: entry count == %u; duplicate entry count == %u; Java type name field width == %u; MVID == %s; assembly name length == %u; assembly name == %s", - dir_path, file_path, header.entry_count, header.duplicate_count, header.java_name_width, - MonoGuidString (header.module_uuid).get (), header.assembly_name_length, module.assembly_name + "typemap: '%s/%s':: entry count == %u; Java name field width == %u; Managed name width == %u; assembly name length == %u; assembly name == %s", + dir_path, file_path, header.entry_count, header.java_name_width, header.managed_name_width, header.assembly_name_length, module.assembly_name ); - alloc_size = MULTIPLY_WITH_OVERFLOW_CHECK (size_t, header.java_name_width + 4, header.entry_count); - module.java_name_width = header.java_name_width; - module.java_map = new uint8_t[alloc_size]; - nread = do_read (file_fd, module.java_map, alloc_size); - if (nread != static_cast(alloc_size)) { - log_error (LOG_ASSEMBLY, "typemap: failed to read %u bytes (java-to-managed) from module file %s/%s. %s", alloc_size, dir_path, file_path, strerror (errno)); - return false; - } + // [name][index] + size_t java_entry_size = header.java_name_width + sizeof(uint32_t); + size_t managed_entry_size = header.managed_name_width + sizeof(uint32_t); + size_t data_size = ADD_WITH_OVERFLOW_CHECK ( + size_t, + header.entry_count * java_entry_size, + header.entry_count * managed_entry_size + ); - module.map = new TypeMapModuleEntry[header.entry_count]; - alloc_size = MULTIPLY_WITH_OVERFLOW_CHECK (size_t, sizeof(TypeMapModuleEntry), header.entry_count); - nread = do_read (file_fd, module.map, alloc_size); - if (nread != static_cast(alloc_size)) { - log_error (LOG_ASSEMBLY, "typemap: failed to read %u bytes (managed-to-java) from module file %s/%s. %s", alloc_size, dir_path, file_path, strerror (errno)); + module.data = new uint8_t [data_size]; + nread = do_read (file_fd, module.data, data_size); + if (nread != data_size) { + log_error (LOG_ASSEMBLY, "tyemap: failed to read map data from '%s/%s': %s", dir_path, file_path, strerror (errno)); return false; } - // alloc_size = module.java_name_width + 1; - // auto chars = new char[alloc_size](); - // uint8_t *p = module.java_map; - // log_debug (LOG_ASSEMBLY, "Java entries in %s/%s", dir_path, file_path); - // for (size_t i = 0; i < module.entry_count; i++) { - // memcpy (chars, p, module.java_name_width); - // uint32_t token = *reinterpret_cast(p + module.java_name_width); - // log_debug (LOG_ASSEMBLY, " %04u: %s; %u (0x%x)", i, chars, token, token); - // p += module.java_name_width + 4; - // } - // delete[] chars; - - // log_debug (LOG_ASSEMBLY, "Managed entries in %s/%s", dir_path, file_path); - // for (size_t i = 0; i < module.entry_count; i++) { - // log_debug (LOG_ASSEMBLY, " %04u: token %u (0x%x); index %u", i, module.map[i].type_token_id, module.map[i].type_token_id, module.map[i].java_map_index); - // } - - if (header.duplicate_count == 0) - return true; - - module.duplicate_map = new TypeMapModuleEntry[header.duplicate_count]; - alloc_size = MULTIPLY_WITH_OVERFLOW_CHECK (size_t, sizeof(TypeMapModuleEntry), header.duplicate_count); - nread = do_read (file_fd, module.duplicate_map, alloc_size); - if (nread != static_cast(alloc_size)) { - log_error (LOG_ASSEMBLY, "typemap: failed to read %u bytes (managed-to-java duplicates) from module file %s/%s. %s", alloc_size, dir_path, file_path, strerror (errno)); - return false; + module.java_to_managed = new TypeMapEntry [module.entry_count]; + module.managed_to_java = new TypeMapEntry [module.entry_count]; + + uint8_t *java_start = module.data; + uint8_t *managed_start = module.data + (module.entry_count * java_entry_size); + uint8_t *java_pos = java_start; + uint8_t *managed_pos = managed_start; + TypeMapEntry *cur; + + for (size_t i = 0; i < module.entry_count; i++) { + cur = &module.java_to_managed[i]; + cur->from = reinterpret_cast(java_pos); + + uint32_t idx = *(reinterpret_cast(java_pos + header.java_name_width)); + cur->to = reinterpret_cast(managed_start + (managed_entry_size * idx)); + java_pos += java_entry_size; + + cur = &module.managed_to_java[i]; + cur->from = reinterpret_cast(managed_pos); + + idx = *(reinterpret_cast(managed_pos + header.managed_name_width)); + cur->to = reinterpret_cast(java_start + (java_entry_size * idx)); + managed_pos += managed_entry_size; } return true; } bool -EmbeddedAssemblies::typemap_load_file (int dir_fd, const char *dir_path, const char *file_path, TypeMapModule &module) +EmbeddedAssemblies::typemap_load_file (int dir_fd, const char *dir_path, const char *file_path, TypeMap &module) { log_debug (LOG_ASSEMBLY, "typemap: loading TypeMap file '%s/%s'", dir_path, file_path); @@ -671,11 +768,10 @@ EmbeddedAssemblies::typemap_load_file (int dir_fd, const char *dir_path, const c size_t file_size; int fd = -1; - module.java_map = nullptr; - module.map = nullptr; - module.duplicate_map = nullptr; + module.java_to_managed = nullptr; + module.managed_to_java = nullptr; - if (!typemap_read_header (dir_fd, "TypeMap", dir_path, file_path, MODULE_MAGIC, header, file_size, fd)) { + if (!typemap_read_header (dir_fd, "TypeMap", dir_path, file_path, MODULE_MAGIC_NAMES, header, file_size, fd)) { ret = false; goto cleanup; } @@ -687,12 +783,10 @@ EmbeddedAssemblies::typemap_load_file (int dir_fd, const char *dir_path, const c close (fd); if (!ret) { - delete[] module.java_map; - module.java_map = nullptr; - delete[] module.map; - module.map = nullptr; - delete[] module.duplicate_map; - module.duplicate_map = nullptr; + delete[] module.java_to_managed; + module.java_to_managed = nullptr; + delete[] module.managed_to_java; + module.managed_to_java = nullptr; } return ret; @@ -706,6 +800,7 @@ EmbeddedAssemblies::try_load_typemaps_from_directory (const char *path) return; } + log_warn (LOG_ASSEMBLY, "typemap: %s (\"%s\")", __PRETTY_FUNCTION__, path); simple_pointer_guard dir_path (utils.path_combine (path, "typemaps")); monodroid_dir_t *dir; if ((dir = utils.monodroid_opendir (dir_path)) == nullptr) { @@ -730,9 +825,9 @@ EmbeddedAssemblies::try_load_typemaps_from_directory (const char *path) exit (FATAL_EXIT_NO_ASSEMBLIES); // TODO: use a new error code here } - for (size_t i = 0; i < module_count; i++) { - TypeMapModule &module = modules[i]; - if (!typemap_load_file (dir_fd, dir_path, module.assembly_name, module)) { + for (size_t i = 0; i < type_map_count; i++) { + TypeMap *module = &type_maps[i]; + if (!typemap_load_file (dir_fd, dir_path, module->assembly_name, *module)) { continue; } } diff --git a/src/monodroid/jni/embedded-assemblies.hh b/src/monodroid/jni/embedded-assemblies.hh index f000bab413b..e758d5e4832 100644 --- a/src/monodroid/jni/embedded-assemblies.hh +++ b/src/monodroid/jni/embedded-assemblies.hh @@ -41,9 +41,11 @@ namespace xamarin::android::internal { #if defined (DEBUG) || !defined (ANDROID) void try_load_typemaps_from_directory (const char *path); #endif + // TODO: `managed_type_name` is temporary, until https://github.com/mono/mono/issues/19377 is fixed + const char* typemap_managed_to_java (MonoReflectionType *type, const uint8_t *mvid, MonoString *managed_type_name); + void install_preload_hooks (); MonoReflectionType* typemap_java_to_managed (MonoString *java_type); - const char* typemap_managed_to_java (const uint8_t *mvid, const int32_t token); /* returns current number of *all* assemblies found from all invocations */ template @@ -66,6 +68,9 @@ namespace xamarin::android::internal { void set_assemblies_prefix (const char *prefix); private: + // TODO: `managed_type_name` is temporary, until https://github.com/mono/mono/issues/19377 is fixed + const char* typemap_managed_to_java (MonoType *type, MonoClass *klass, const uint8_t *mvid, MonoString *managed_type_name); + MonoReflectionType* typemap_java_to_managed (const char *java_type_name); 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); MonoAssembly* open_from_bundles (MonoAssemblyName* aname, bool ref_only); @@ -74,9 +79,10 @@ namespace xamarin::android::internal { bool typemap_read_header (int dir_fd, const char *file_type, const char *dir_path, const char *file_path, uint32_t expected_magic, H &header, size_t &file_size, int &fd); uint8_t* typemap_load_index (int dir_fd, const char *dir_path, const char *index_path); uint8_t* typemap_load_index (TypeMapIndexHeader &header, size_t file_size, int index_fd); - bool typemap_load_file (int dir_fd, const char *dir_path, const char *file_path, TypeMapModule &module); - bool typemap_load_file (BinaryTypeMapHeader &header, const char *dir_path, const char *file_path, int file_fd, TypeMapModule &module); + 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); #endif // DEBUG || !ANDROID bool register_debug_symbols_for_assembly (const char *entry_name, MonoBundledAssembly *assembly, const mono_byte *debug_contents, int debug_size); @@ -104,11 +110,12 @@ namespace xamarin::android::internal { template const Entry* binary_search (const Key *key, const Entry *base, size_t nmemb, size_t extra_size = 0); +#if defined (DEBUG) || !defined (ANDROID) + static int compare_type_name (const char *type_name, const TypeMapEntry *entry); +#else static int compare_mvid (const uint8_t *mvid, const TypeMapModule *module); - static int compare_type_token (const int32_t *token, const TypeMapModuleEntry *entry); + static int compare_type_token (const uint32_t *token, const TypeMapModuleEntry *entry); static int compare_java_name (const char *java_name, const TypeMapJava *entry); -#if defined (DEBUG) || !defined (ANDROID) - static int compare_java_name (const char *java_name, const uint8_t *java_map); #endif private: @@ -118,8 +125,8 @@ namespace xamarin::android::internal { #if defined (DEBUG) || !defined (ANDROID) TypeMappingInfo *java_to_managed_maps; TypeMappingInfo *managed_to_java_maps; - TypeMapModule *modules; - size_t module_count; + TypeMap *type_maps; + size_t type_map_count; #endif // DEBUG || !ANDROID const char *assemblies_prefix_override = nullptr; }; diff --git a/src/monodroid/jni/external-api.cc b/src/monodroid/jni/external-api.cc index c49a58308af..a835d4a8ca3 100644 --- a/src/monodroid/jni/external-api.cc +++ b/src/monodroid/jni/external-api.cc @@ -158,12 +158,6 @@ _monodroid_get_display_dpi (float *x_dpi, float *y_dpi) return monodroidRuntime.get_display_dpi (x_dpi, y_dpi); } -MONO_API const char * -monodroid_typemap_managed_to_java (const uint8_t *mvid, const int32_t token) -{ - return embeddedAssemblies.typemap_managed_to_java (mvid, token); -} - MONO_API int monodroid_embedded_assemblies_set_assemblies_prefix (const char *prefix) { embeddedAssemblies.set_assemblies_prefix (prefix); diff --git a/src/monodroid/jni/monodroid-glue-internal.hh b/src/monodroid/jni/monodroid-glue-internal.hh index 8ab70212529..37fd6f9b2b6 100644 --- a/src/monodroid/jni/monodroid-glue-internal.hh +++ b/src/monodroid/jni/monodroid-glue-internal.hh @@ -178,6 +178,9 @@ namespace xamarin::android::internal static void thread_end (MonoProfiler *prof, uintptr_t tid); static MonoReflectionType* typemap_java_to_managed (MonoString *java_type_name); + // TODO: `type_name` is temporary, until https://github.com/mono/mono/issues/19377 is fixed + static const char* typemap_managed_to_java (MonoReflectionType *type, const uint8_t *mvid, MonoString *type_name); + #if defined (DEBUG) void set_debug_env_vars (void); diff --git a/src/monodroid/jni/monodroid-glue.cc b/src/monodroid/jni/monodroid-glue.cc index a669016e5bd..38ce201d318 100644 --- a/src/monodroid/jni/monodroid-glue.cc +++ b/src/monodroid/jni/monodroid-glue.cc @@ -921,6 +921,7 @@ void MonodroidRuntime::init_android_runtime (MonoDomain *domain, 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)); struct JnienvInitializeArgs init = {}; init.javaVm = osBridge.get_jvm (); @@ -1411,6 +1412,13 @@ MonodroidRuntime::typemap_java_to_managed (MonoString *java_type_name) return embeddedAssemblies.typemap_java_to_managed (java_type_name); } +// TODO: `type_name` is temporary, until https://github.com/mono/mono/issues/19377 is fixed +const char* +MonodroidRuntime::typemap_managed_to_java (MonoReflectionType *type, const uint8_t *mvid, MonoString *type_name) +{ + return embeddedAssemblies.typemap_managed_to_java (type, mvid, type_name); +} + inline void MonodroidRuntime::Java_mono_android_Runtime_initInternal (JNIEnv *env, jclass klass, jstring lang, jobjectArray runtimeApksJava, jstring runtimeNativeLibDir, jobjectArray appDirs, jobject loader, diff --git a/src/monodroid/jni/xamarin-app.hh b/src/monodroid/jni/xamarin-app.hh index 6cbef89d428..c20e643c909 100644 --- a/src/monodroid/jni/xamarin-app.hh +++ b/src/monodroid/jni/xamarin-app.hh @@ -8,18 +8,19 @@ #include "monodroid.h" -static constexpr uint32_t MODULE_MAGIC = 0x4D544158; // 'XATM', little-endian +static constexpr uint32_t MODULE_MAGIC_MVID = 0x4D544158; // 'XATM', little-endian +static constexpr uint32_t MODULE_MAGIC_NAMES = 0x53544158; // 'XATS', little-endian static constexpr uint32_t MODULE_INDEX_MAGIC = 0x49544158; // 'XATI', little-endian -static constexpr uint8_t MODULE_FORMAT_VERSION = 1; // Keep in sync with the value in src/Xamarin.Android.Build.Tasks/Utilities/TypeMapGenerator.cs +static constexpr uint8_t MODULE_FORMAT_VERSION = 2; // Keep in sync with the value in src/Xamarin.Android.Build.Tasks/Utilities/TypeMapGenerator.cs +#if defined (DEBUG) || !defined (ANDROID) struct BinaryTypeMapHeader { uint32_t magic; uint32_t version; - uint8_t module_uuid[16]; uint32_t entry_count; - uint32_t duplicate_count; uint32_t java_name_width; + uint32_t managed_name_width; uint32_t assembly_name_length; }; @@ -31,9 +32,25 @@ struct TypeMapIndexHeader uint32_t module_file_name_width; }; +struct TypeMapEntry +{ + const char *from; + const char *to; +}; + +// MUST match src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingDebugNativeAssemblyGenerator.cs +struct TypeMap +{ + uint32_t entry_count; + char *assembly_name; + uint8_t *data; + TypeMapEntry *java_to_managed; + TypeMapEntry *managed_to_java; +}; +#else struct TypeMapModuleEntry { - int32_t type_token_id; + uint32_t type_token_id; uint32_t java_map_index; }; @@ -53,9 +70,10 @@ struct TypeMapModule struct TypeMapJava { uint32_t module_index; - int32_t type_token_id; + uint32_t type_token_id; uint8_t java_name[]; }; +#endif struct ApplicationConfig { @@ -73,11 +91,15 @@ struct ApplicationConfig const char *android_package_name; }; +#if defined (DEBUG) || !defined (ANDROID) +MONO_API const TypeMap type_map; // MUST match src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingDebugNativeAssemblyGenerator.cs +#else MONO_API const uint32_t map_module_count; MONO_API const uint32_t java_type_count; MONO_API const uint32_t java_name_width; MONO_API const TypeMapModule map_modules[]; MONO_API const TypeMapJava map_java[]; +#endif MONO_API ApplicationConfig application_config; MONO_API const char* app_environment_variables[];