From 7117414ca27d88a71b4a272705a0207f772423bd Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Mon, 6 Apr 2020 18:31:30 +0000 Subject: [PATCH] [monodroid] Return of the Strings (#4487) Fixes: https://github.com/xamarin/xamarin-android/issues/4415 Context: ce2bc689a19cb45f7d7bfdd371c16c54b018a020 Commit ce2bc689 optimized type mappings between managed types and Java types in large part by removing strings from the managed -> JNI mapping: instead of using an assembly-qualified *string* as a key, the assembly MVID & type metadata token were used as keys. This setup works reliably in Release apps, in which the assemblies don't change. In a commercial Debug with Fast Deployment situation, it falls down badly because every change to any source code that's built into a mapped assembly may cause the assembly to change its MVID, and renaming of any type -- removing or adding a type -- will rearrange the type definition table in the resulting assembly, thus changing the type token ids (which are basically offsets into the type definition table in the PE executable). This is what may cause an app to crash on the runtime with an exception similar to: android.runtime.JavaProxyThrowable: System.NotSupportedException: Cannot create instance of type 'com.glmsoftware.OBDNowProto.SettingsFragmentCompat': no Java peer type found. at Java.Interop.JniPeerMembers+JniInstanceMethods..ctor (System.Type declaringType) [0x0004b] in :0 at Java.Interop.JniPeerMembers+JniInstanceMethods.GetConstructorsForType (System.Type declaringType) [0x00031] in :0 at Java.Interop.JniPeerMembers+JniInstanceMethods.StartCreateInstance (System.String constructorSignature, System.Type declaringType, Java.Interop.JniArgumentValue* parameters) [0x00038] in :0 at AndroidX.Preference.PreferenceFragmentCompat..ctor () [0x00034] in <005e3ae6340747e1aea6d08b095cf286>:0 at com.glmsoftware.OBDNowProto.SettingsFragmentCompat..ctor () [0x00026] in :0 at com.glmsoftware.OBDNowProto.SettingsActivity.OnCreate (Android.OS.Bundle bundle) [0x00083] in :0 at Android.App.Activity.n_OnCreate_Landroid_os_Bundle_ (System.IntPtr jnienv, System.IntPtr native__this, System.IntPtr native_savedInstanceState) [0x00011] in :0 at (wrapper dynamic-method) Android.Runtime.DynamicMethodNameCounter.3(intptr,intptr,intptr) at crc64596a13587a898911.SettingsActivity.n_onCreate(Native Method) at crc64596a13587a898911.SettingsActivity.onCreate(SettingsActivity.java:40) at android.app.Activity.performCreate(Activity.java:7825) at android.app.Activity.performCreate(Activity.java:7814) at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1306) at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3245) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3409) at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83) at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135) at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2016) at android.os.Handler.dispatchMessage(Handler.java:107) at android.os.Looper.loop(Looper.java:214) at android.app.ActivityThread.main(ActivityThread.java:7356) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930) A "workaround" would be fully rebuild the application, which negates the point to incremental builds and the inner-dev-loop cycle. The fix is to partially revert ce2bc689 in the sense that it restores the use of string-based type names for Java-to-Managed and Managed-to-Java type lookups for ***Debug*** builds only. Unlike the pre-ce2bc689 world, only *one* copy of the set of string names is present within the data structures, at a tiny (sub millisecond) expense at the run time to fix up pointers between the two tables. ~~ File Formats ~~ All data in all file formats remains little-endian. In Debug configuration builds, each assembly will have a corresponding `*.typemap` file which will be loaded at runtime. The file format in pseudo-C++: struct DebugTypemapFileHeader { byte magic [4]; // "XATS" uint32_t format_version; // 2 uint32_t entry_count; uint32_t java_type_name_width; uint32_t managed_type_name_width; uint32_t assembly_name_size; byte assembly_name [assembly_name_size]; DebugTypemapFileJavaToManagedEntry java_to_managed [entry_count]; DebugTypemapFileManagedToJavaEntry managed_to_java [entry_count]; } struct DebugTypemapFileJavaToManagedEntry { byte jni_name [DebugTypemapFileHeader::java_type_name_width]; uint32_t managed_index; // Index into DebugTypemapFileHeader::managed_to_java }; struct DebugTypemapFileManagedToJavaEntry { byte managed_name [DebugTypemapFileHeader::java_type_name_width]; uint32_t jni_index; // Index into DebugTypemapFileHeader::java_to_managed }; `DebugTypemapFileHeader::java_type_name_width` and `DebugTypemapFileHeader::managed_type_name_width` are the maximum length + 1 (terminating NUL) for JNI names and assembly-qualified managed names. `DebugTypemapFileJavaToManagedEntry::jni_name` and `DebugTypemapFileManagedToJavaEntry::managed_name` are NUL-padded. --- Documentation/release-notes/4487.md | 6 + src/Mono.Android/Android.Runtime/JNIEnv.cs | 19 +- .../Test/Java.Interop/JnienvTest.cs | 11 +- .../Tasks/GenerateJavaStubs.cs | 2 +- .../Tasks/PrepareAbiItems.cs | 14 +- .../IncrementalBuildTest.cs | 1 - .../ARMNativeAssemblerTargetProvider.cs | 1 + .../NativeAssemblerTargetProvider.cs | 1 + .../Utilities/NativeTypeMappingData.cs | 14 +- .../Utilities/TypeMapGenerator.cs | 388 +++++++++------ ...TypeMappingDebugNativeAssemblyGenerator.cs | 140 ++++++ ...eMappingReleaseNativeAssemblyGenerator.cs} | 20 +- .../X86NativeAssemblerTargetProvider.cs | 1 + .../Xamarin.Android.Common.targets | 8 + src/monodroid/CMakeLists.txt | 39 +- src/monodroid/jni/application_dso_stub.cc | 15 + src/monodroid/jni/embedded-assemblies.cc | 450 +++++++++++------- src/monodroid/jni/embedded-assemblies.hh | 21 +- src/monodroid/jni/external-api.cc | 6 - src/monodroid/jni/monodroid-glue-internal.hh | 2 + src/monodroid/jni/monodroid-glue.cc | 7 + src/monodroid/jni/xamarin-app.hh | 33 +- .../Tests/InstantRunTest.cs | 4 +- .../MSBuildDeviceIntegration.csv | 6 +- 24 files changed, 804 insertions(+), 405 deletions(-) create mode 100644 Documentation/release-notes/4487.md create mode 100644 src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingDebugNativeAssemblyGenerator.cs rename src/Xamarin.Android.Build.Tasks/Utilities/{TypeMappingNativeAssemblyGenerator.cs => TypeMappingReleaseNativeAssemblyGenerator.cs} (90%) diff --git a/Documentation/release-notes/4487.md b/Documentation/release-notes/4487.md new file mode 100644 index 00000000000..3de1e92b0c8 --- /dev/null +++ b/Documentation/release-notes/4487.md @@ -0,0 +1,6 @@ +#### Application behavior on device and emulator + + * [GitHub 4415](https://github.com/xamarin/xamarin-android/issues/4415): + Starting in Xamarin.Android 10.2.100.7, *System.NotSupportedException: + Cannot create instance of type ... no Java peer type found* caused certain + apps to crash during launch after incremental deployments. diff --git a/src/Mono.Android/Android.Runtime/JNIEnv.cs b/src/Mono.Android/Android.Runtime/JNIEnv.cs index 5bf56887c42..9d424517717 100644 --- a/src/Mono.Android/Android.Runtime/JNIEnv.cs +++ b/src/Mono.Android/Android.Runtime/JNIEnv.cs @@ -670,8 +670,8 @@ public static string GetClassNameFromInstance (IntPtr jobject) } } - [DllImport ("__Internal", CallingConvention = CallingConvention.Cdecl)] - static extern IntPtr monodroid_typemap_managed_to_java (byte[] mvid, int token); + [MethodImplAttribute(MethodImplOptions.InternalCall)] + static extern unsafe IntPtr monodroid_typemap_managed_to_java (Type type, byte* mvid); internal static void LogTypemapTrace (StackTrace st) { @@ -685,19 +685,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); + } 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/Tasks/PrepareAbiItems.cs b/src/Xamarin.Android.Build.Tasks/Tasks/PrepareAbiItems.cs index d298dfd4ce0..7d9f58eaf60 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/PrepareAbiItems.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/PrepareAbiItems.cs @@ -24,6 +24,12 @@ public class PrepareAbiItems : AndroidTask [Required] public bool TypeMapMode { get; set; } + [Required] + public bool Debug { get; set; } + + [Required] + public bool InstantRunEnabled { get; set; } + [Output] public ITaskItem[] AssemblySources { get; set; } @@ -53,9 +59,11 @@ public override bool RunTask () if (!TypeMapMode) continue; - item = new TaskItem (Path.Combine (NativeSourcesDir, $"{baseName}.{abi}-managed.inc")); - item.SetMetadata ("abi", abi); - includes.Add (item); + if (!InstantRunEnabled && !Debug) { + item = new TaskItem (Path.Combine (NativeSourcesDir, $"{baseName}.{abi}-managed.inc")); + item.SetMetadata ("abi", abi); + includes.Add (item); + } } if (haveArmV7SharedSource) { 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..25d2ff1cd2e 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,40 @@ 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 but not the padding! + internal sealed class ModuleDebugData + { + public uint EntryCount; + public uint JavaNameWidth; + public uint ManagedNameWidth; + public List JavaToManagedMap; + public List ManagedToJavaMap; + public string OutputFilePath; + public byte[] ModuleNameBytes; } Action logger; @@ -102,7 +125,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 +133,165 @@ 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), + ModuleNameBytes = outputEncoding.GetBytes (moduleName) + }; + + if (module.ModuleNameBytes.Length > maxModuleNameWidth) + maxModuleNameWidth = module.ModuleNameBytes.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 +312,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 +337,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 +354,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 +365,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 +406,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 +416,6 @@ public bool Generate (bool skipJniAddNativeMethodRegistrationAttributeScan, List sharedBitsWritten = true; } } - return true; } // Binary index file format, all data is little-endian: @@ -276,37 +428,35 @@ 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 (ModuleDebugData module in modules.Values) { + OutputModule (module); - string outputFilePath = Path.GetFileName (data.OutputFilePath); + string outputFilePath = Path.GetFileName (module.OutputFilePath); indexWriter.Write (outputEncoding.GetBytes (outputFilePath)); PadField (indexWriter, outputFilePath.Length, moduleFileNameWidth); } } - void OutputModule (byte[] moduleUUID, ModuleData moduleData) + void OutputModule (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, moduleData); bw.Flush (); MonoAndroidHelper.CopyIfStreamChanged (bw.BaseStream, moduleData.OutputFilePath); } @@ -315,104 +465,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, 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 (moduleData.ModuleNameBytes.Length); + bw.Write (moduleData.ModuleNameBytes); + + 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 a474961ed75..841974febb4 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets @@ -935,6 +935,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 @@ -965,6 +968,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)" /> @@ -1847,6 +1853,8 @@ because xbuild doesn't support framework reference assemblies. diff --git a/src/monodroid/CMakeLists.txt b/src/monodroid/CMakeLists.txt index 316d5857064..8add132a12e 100644 --- a/src/monodroid/CMakeLists.txt +++ b/src/monodroid/CMakeLists.txt @@ -146,12 +146,13 @@ if(ENABLE_NDK) add_definitions("-D__ANDROID_API_Q__=29") endif() - link_directories("${XA_LIB_TOP_DIR}/${ANDROID_ABI}") + set(XA_LIBRARY_OUTPUT_DIRECTORY "${XA_LIB_TOP_DIR}/${ANDROID_ABI}") + link_directories("${XA_LIBRARY_OUTPUT_DIRECTORY}") else() set(CMAKE_REQUIRED_DEFINITIONS "-D__USE_GNU") check_cxx_symbol_exists(gettid unistd.h HAVE_GETTID_IN_UNISTD_H) if(HAVE_GETTID_IN_UNISTD_H) - add_definitions("-DHAVE_GETTID_IN_UNISTD_H") + add_definitions("-DHAVE_GETTID_IN_UNISTD_H") endif() # MinGW needs it for {v,a}sprintf @@ -166,7 +167,7 @@ else() set(HOST_BUILD_NAME "host-Linux") if(EXISTS "/.flatpak-info") - add_definitions("-DLINUX_FLATPAK") + add_definitions("-DLINUX_FLATPAK") endif() endif() endif() @@ -196,10 +197,12 @@ else() endif() endif() - link_directories("${XA_LIB_TOP_DIR}/${ANDROID_ABI}") + set(XA_LIBRARY_OUTPUT_DIRECTORY "${XA_LIB_TOP_DIR}/${ANDROID_ABI}") + link_directories("${XA_LIBRARY_OUTPUT_DIRECTORY}") endif() if(DEFINED HOST_BUILD_NAME) + set(XA_LIBRARY_OUTPUT_DIRECTORY "${XA_LIB_TOP_DIR}/${HOST_BUILD_NAME}") link_directories("${XA_LIB_TOP_DIR}/${HOST_BUILD_NAME}") include_directories("${DEFAULT_BIN_DIR}/include/${HOST_BUILD_NAME}") include_directories("${DEFAULT_BIN_DIR}/include/${HOST_BUILD_NAME}/eglib") @@ -289,21 +292,29 @@ endif() set(XAMARIN_APP_STUB_SOURCES ${SOURCES_DIR}/application_dso_stub.cc) add_library(xamarin-app SHARED ${XAMARIN_APP_STUB_SOURCES}) +if (ENABLE_NDK) + # Only Android builds need to go in separate directories, desktop builds have the same ABI + set_target_properties( + xamarin-app + PROPERTIES + LIBRARY_OUTPUT_DIRECTORY "${XA_LIBRARY_OUTPUT_DIRECTORY}/${CMAKE_BUILD_TYPE}" + ) +endif() if(NOT WIN32 AND NOT MINGW AND CMAKE_BUILD_TYPE STREQUAL Debug) set(XAMARIN_DEBUG_APP_HELPER_SOURCES - ${SOURCES_DIR}/basic-android-system.cc - ${SOURCES_DIR}/basic-utilities.cc - ${SOURCES_DIR}/cpu-arch-detect.cc - ${SOURCES_DIR}/debug-app-helper.cc - ${SOURCES_DIR}/new_delete.cc - ${SOURCES_DIR}/shared-constants.cc - ) + ${SOURCES_DIR}/basic-android-system.cc + ${SOURCES_DIR}/basic-utilities.cc + ${SOURCES_DIR}/cpu-arch-detect.cc + ${SOURCES_DIR}/debug-app-helper.cc + ${SOURCES_DIR}/new_delete.cc + ${SOURCES_DIR}/shared-constants.cc + ) add_library(xamarin-debug-app-helper SHARED ${XAMARIN_DEBUG_APP_HELPER_SOURCES}) target_compile_definitions( - xamarin-debug-app-helper - PUBLIC -DDEBUG_APP_HELPER - ) + xamarin-debug-app-helper + PUBLIC -DDEBUG_APP_HELPER + ) endif() string(TOLOWER ${CMAKE_BUILD_TYPE} MONO_ANDROID_SUFFIX) diff --git a/src/monodroid/jni/application_dso_stub.cc b/src/monodroid/jni/application_dso_stub.cc index d7fe5d85205..77eb2060510 100644 --- a/src/monodroid/jni/application_dso_stub.cc +++ b/src/monodroid/jni/application_dso_stub.cc @@ -5,12 +5,27 @@ // This file MUST have "valid" values everywhere - the DSO it is compiled into is loaded by the // designer on desktop. +#if defined (DEBUG) || !defined (ANDROID) +static TypeMapEntry java_to_managed[] = {}; + +static TypeMapEntry managed_to_java[] = {}; + +// MUST match src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingDebugNativeAssemblyGenerator.cs +const TypeMap type_map = { + 0, + nullptr, + nullptr, + java_to_managed, + managed_to_java +}; +#else const uint32_t map_module_count = 0; const uint32_t java_type_count = 0; const uint32_t java_name_width = 0; const TypeMapModule map_modules[] = {}; const TypeMapJava map_java[] = {}; +#endif ApplicationConfig application_config = { /*.uses_mono_llvm =*/ false, diff --git a/src/monodroid/jni/embedded-assemblies.cc b/src/monodroid/jni/embedded-assemblies.cc index 19f4467a544..018289db9cd 100644 --- a/src/monodroid/jni/embedded-assemblies.cc +++ b/src/monodroid/jni/embedded-assemblies.cc @@ -175,68 +175,78 @@ EmbeddedAssemblies::binary_search (const Key *key, const Entry *base, size_t nme return nullptr; } +#if defined (DEBUG) || !defined (ANDROID) +int +EmbeddedAssemblies::compare_type_name (const char *type_name, const TypeMapEntry *entry) +{ + if (entry == nullptr) + return 1; + + return strcmp (type_name, entry->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; } - 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; - } + 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; + } - 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; - } + return ret; +} +#else +MonoReflectionType* +EmbeddedAssemblies::typemap_java_to_managed (const char *java_type_name) +{ + TypeMapModule *module; + 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 + uint32_t type_token_id = java_entry->type_token_id; if (module->image == nullptr) { module->image = mono_image_loaded (module->assembly_name); @@ -246,23 +256,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,45 +286,140 @@ EmbeddedAssemblies::compare_java_name (const char *java_name, const TypeMapJava return strcmp (java_name, reinterpret_cast(entry->java_name)); } +#endif + +MonoReflectionType* +EmbeddedAssemblies::typemap_java_to_managed (MonoString *java_type) +{ + timing_period total_time; + if (XA_UNLIKELY (utils.should_log (LOG_TIMING))) { + timing = new Timing (); + total_time.mark_start (); + } + + if (XA_UNLIKELY (java_type == nullptr)) { + log_warn (LOG_ASSEMBLY, "typemap: null 'java_type' passed to 'typemap_java_to_managed'"); + 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'"); + 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) -int -EmbeddedAssemblies::compare_java_name (const char *java_name, const uint8_t *entry) +inline const TypeMapEntry* +EmbeddedAssemblies::typemap_managed_to_java (const char *managed_type_name) { - if (entry == nullptr) - return 1; + const TypeMapEntry *entry = nullptr; - return strcmp (java_name, reinterpret_cast(entry)); + 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 (managed_type_name, module->managed_to_java, module->entry_count); + if (entry != nullptr) + break; + } + } else { + entry = binary_search (managed_type_name, type_map.managed_to_java, type_map.entry_count); + } + + return entry; } -#endif // DEBUG || !ANDROID -const char* -EmbeddedAssemblies::typemap_managed_to_java (const uint8_t *mvid, const int32_t token) +inline const char* +EmbeddedAssemblies::typemap_managed_to_java ([[maybe_unused]] MonoType *type, MonoClass *klass, [[maybe_unused]] const uint8_t *mvid) { - timing_period total_time; - if (XA_UNLIKELY (utils.should_log (LOG_TIMING))) { - timing = new Timing (); - total_time.mark_start (); + constexpr char error_message[] = "typemap: unable to find mapping to a Java type from managed type '%s'"; + + simple_pointer_guard type_name (mono_type_get_name_full (type, MONO_TYPE_NAME_FORMAT_REFLECTION)); + 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); + } + + 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) +{ + 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) +{ 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; -#if defined (DEBUG) || !defined (ANDROID) - if (application_config.instant_run_enabled) { - map = modules; - map_entry_count = module_count; - } else { -#endif - map = map_modules; - map_entry_count = map_module_count; -#if defined (DEBUG) || !defined (ANDROID) - } -#endif + 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 +433,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 +447,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 +473,32 @@ 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) +const char* +EmbeddedAssemblies::typemap_managed_to_java (MonoReflectionType *reflection_type, const uint8_t *mvid) { - 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); + + 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 +637,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 +652,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 +674,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 +686,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 != static_cast(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 != static_cast(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 +760,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 +775,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; @@ -730,9 +816,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..9f4a6409f98 100644 --- a/src/monodroid/jni/embedded-assemblies.hh +++ b/src/monodroid/jni/embedded-assemblies.hh @@ -41,9 +41,10 @@ namespace xamarin::android::internal { #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); + 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 +67,8 @@ namespace xamarin::android::internal { void set_assemblies_prefix (const char *prefix); private: + const char* typemap_managed_to_java (MonoType *type, MonoClass *klass, const uint8_t *mvid); + 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 +77,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 +108,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 +123,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..678ce9b8a63 100644 --- a/src/monodroid/jni/monodroid-glue-internal.hh +++ b/src/monodroid/jni/monodroid-glue-internal.hh @@ -178,6 +178,8 @@ namespace xamarin::android::internal static void thread_end (MonoProfiler *prof, uintptr_t tid); static MonoReflectionType* typemap_java_to_managed (MonoString *java_type_name); + static const char* typemap_managed_to_java (MonoReflectionType *type, const uint8_t *mvid); + #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..6b05c884115 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,12 @@ MonodroidRuntime::typemap_java_to_managed (MonoString *java_type_name) return embeddedAssemblies.typemap_java_to_managed (java_type_name); } +const char* +MonodroidRuntime::typemap_managed_to_java (MonoReflectionType *type, const uint8_t *mvid) +{ + return embeddedAssemblies.typemap_managed_to_java (type, mvid); +} + 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..49b6154abe9 100644 --- a/src/monodroid/jni/xamarin-app.hh +++ b/src/monodroid/jni/xamarin-app.hh @@ -8,18 +8,18 @@ #include "monodroid.h" -static constexpr uint32_t MODULE_MAGIC = 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 +31,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 +69,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 +90,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[]; diff --git a/tests/MSBuildDeviceIntegration/Tests/InstantRunTest.cs b/tests/MSBuildDeviceIntegration/Tests/InstantRunTest.cs index 2aca514ed24..75eae2770b5 100644 --- a/tests/MSBuildDeviceIntegration/Tests/InstantRunTest.cs +++ b/tests/MSBuildDeviceIntegration/Tests/InstantRunTest.cs @@ -285,13 +285,13 @@ public void InstantRunFastDevTypemaps ([Values ("dx", "d8")] string dexTool) Assert.IsTrue (b.Install (proj), "packaging should have succeeded. 0"); var apk = Path.Combine (Root, b.ProjectDirectory, proj.IntermediateOutputPath, "android", "bin", "UnnamedProject.UnnamedProject.apk"); - Assert.IsNull (ZipHelper.ReadFileFromZip (apk, "MonoAndroid.0.typemap"), $"MonoAndroid.0.typemap should NOT be in {apk}."); + Assert.IsNull (ZipHelper.ReadFileFromZip (apk, "Mono.Android.typemap"), $"Mono.Android.typemap should NOT be in {apk}."); var logLines = b.LastBuildOutput; Assert.IsTrue (logLines.Any (l => l.Contains ("Building target \"_BuildApkFastDev\" completely.") || l.Contains ("Target _BuildApkFastDev needs to be built")), "Apk should have been built"); Assert.IsTrue (logLines.Any (l => l.Contains ("Building target \"_Upload\" completely")), "_Upload target should have run"); - Assert.IsTrue (logLines.Any (l => l.Contains ("NotifySync CopyFile") && l.Contains ("Mono.Android.0.typemap")), "Mono.Android.0.typemap should have been uploaded"); + Assert.IsTrue (logLines.Any (l => l.Contains ("NotifySync CopyFile") && l.Contains ("Mono.Android.typemap")), "Mono.Android.typemap should have been uploaded"); Assert.IsTrue (logLines.Any (l => l.Contains ("NotifySync CopyFile") && l.Contains ("typemap.index")), "typemap.index should have been uploaded"); } } diff --git a/tests/msbuild-times-reference/MSBuildDeviceIntegration.csv b/tests/msbuild-times-reference/MSBuildDeviceIntegration.csv index 73704537f5c..73c4d161475 100644 --- a/tests/msbuild-times-reference/MSBuildDeviceIntegration.csv +++ b/tests/msbuild-times-reference/MSBuildDeviceIntegration.csv @@ -6,10 +6,10 @@ Build_From_Clean_DontIncludeRestore,10000 Build_No_Changes,3250 Build_CSharp_Change,4450 Build_AndroidResource_Change,4150 -Build_AndroidManifest_Change,4350 +Build_AndroidManifest_Change,4400 Build_Designer_Change,3600 -Build_JLO_Change,8700 -Build_CSProj_Change,9500 +Build_JLO_Change,9100 +Build_CSProj_Change,9800 Build_XAML_Change,9400 Build_XAML_Change_RefAssembly,6000 Install_CSharp_Change,5000