From 95de74b88cd66fe8200873ff3f1b1aa7ed33d283 Mon Sep 17 00:00:00 2001
From: Marek Habersack <grendel@twistedcode.net>
Date: Tue, 5 Jan 2021 09:10:53 +0100
Subject: [PATCH] [typemap] Handle managed -> Java duplicate mapping correctly

Fixes: https://github.com/xamarin/xamarin-android/issues/5409
Context: a017561b1e44c51a9af79fae0baaa50fe01c4123
Context: bb55bb064b967adf8ca975928736346694dceb02

This is another commit in the series of properly handling the N:1 type
mapping from Java to Managed types.

a017561b implemented most of the handling properly, bb55bb064 fixed it
up for generic types but neither of the two commits accounted for the
situation when a Managed type map is requested that points back to a
duplicate Java type.  In fact, such Managed types were removed from the
typemap unless they were the type pointed to from the Java duplicate.
This meant that, effectively, all the `*Invoker` classes were not part
of the mapping, leading to this error for Debug builds:

    app_process32: ---- DEBUG ASSERTION FAILED ----
    app_process32: ---- Assert Short Message ----
    app_process32: ManagedPeerType <=> JniTypeName Mismatch! javaVM.GetJniTypeInfoForType(typeof(Android.Views.LayoutInflaterInvoker)).JniTypeName="" != "android/view/LayoutInflater"
    app_process32: ---- Assert Long Message ----
    app_process32:
    app_process32:    at System.Diagnostics.DebugProvider.Fail(String message, String detailMessage)
    app_process32:    at System.Diagnostics.Debug.Fail(String message, String detailMessage)
    app_process32:    at System.Diagnostics.Debug.Assert(Boolean condition, String message, String detailMessage)
    app_process32:    at System.Diagnostics.Debug.Assert(Boolean condition, String message)
    app_process32:    at Java.Interop.JniPeerMembers..ctor(String jniPeerTypeName, Type managedPeerType, Boolean checkManagedPeerType, Boolean isInterface)
    app_process32:    at Java.Interop.JniPeerMembers..ctor(String jniPeerTypeName, Type managedPeerType)
    app_process32:    at Android.Runtime.XAPeerMembers..ctor(String jniPeerTypeName, Type managedPeerType)
    app_process32:    at Android.Views.LayoutInflaterInvoker..cctor()
    app_process32:    at System.Reflection.RuntimeConstructorInfo.InternalInvoke(RuntimeConstruct
            : CLR: Managed code called FailFast, saying "ManagedPeerType <=> JniTypeName Mismatch! javaVM.GetJniTypeInfoForType(typeof(Android.Views.LayoutInflaterInvoker)).JniTypeName="" != "android/view/LayoutInflater"
            :    at System.Diagnostics.DebugProvider.Fail(String message, String detailMessage)
            :    at System.Diagnostics.Debug.Fail(String message, String detailMessage)
            :    at System.Diagnostics.Debug.Assert(Boolean condition, String message, String detailMessage)
            :    at System.Diagnostics.Debug.Assert(Boolean condition, String message)
            :    at Java.Interop.JniPeerMembers..ctor(String jniPeerTypeName, Type managedPeerType, Boolean checkManagedPeerType, Boolean isInterface)
            :    at Java.Interop.JniPeerMembers..ctor(String jniPeerTypeName, Type managedPeerType)
            :    at Android.Runtime.XAPeerMembers..ctor(String jniPeerTypeName, Type managedPeerType)
            :    at Android.Views.LayoutInflaterInvoker..cctor()

This commit modifies the mapping code to make sure that all duplicate
Java types point to the **first** Managed type but that all the Managed
types point back to their original Java type.
---
 .../Utilities/TypeMapGenerator.cs                   | 13 +++++++++----
 .../TypeMappingDebugNativeAssemblyGenerator.cs      |  4 +++-
 2 files changed, 12 insertions(+), 5 deletions(-)

diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/TypeMapGenerator.cs b/src/Xamarin.Android.Build.Tasks/Utilities/TypeMapGenerator.cs
index 590b2018b71..7ab407f45ee 100644
--- a/src/Xamarin.Android.Build.Tasks/Utilities/TypeMapGenerator.cs
+++ b/src/Xamarin.Android.Build.Tasks/Utilities/TypeMapGenerator.cs
@@ -81,6 +81,7 @@ internal sealed class TypeMapDebugEntry
 			public int ManagedIndex;
 			public TypeDefinition TypeDefinition;
 			public bool SkipInJavaToManaged;
+			public TypeMapDebugEntry DuplicateForJavaToManaged;
 		}
 
 		// Widths include the terminating nul character but not the padding!
@@ -267,11 +268,13 @@ void SyncDebugDuplicates (Dictionary<string, List<TypeMapDebugEntry>> javaDuplic
 					continue;
 				}
 
+				// Java duplicates must all point to the same managed type
+				// Managed types, however, must point back to the original Java type instead
+				// File/assembly generator use the `DuplicateForJavaToManaged` field to know to which managed type the
+				// duplicate Java type must be mapped.
 				TypeMapDebugEntry template = duplicates [0];
 				for (int i = 1; i < duplicates.Count; i++) {
-					duplicates[i].TypeDefinition = template.TypeDefinition;
-					duplicates[i].ManagedName = template.ManagedName;
-					duplicates[i].SkipInJavaToManaged = template.SkipInJavaToManaged;
+					duplicates[i].DuplicateForJavaToManaged = template;
 				}
 			}
 		}
@@ -571,7 +574,9 @@ void OutputModule (BinaryWriter bw, ModuleDebugData moduleData)
 			foreach (TypeMapDebugEntry entry in moduleData.JavaToManagedMap) {
 				bw.Write (outputEncoding.GetBytes (entry.JavaName));
 				PadField (bw, entry.JavaName.Length, (int)moduleData.JavaNameWidth);
-				bw.Write (entry.SkipInJavaToManaged ? InvalidJavaToManagedMappingIndex : (uint)entry.ManagedIndex);
+
+				TypeMapGenerator.TypeMapDebugEntry managedEntry = entry.DuplicateForJavaToManaged != null ? entry.DuplicateForJavaToManaged : entry;
+				bw.Write (managedEntry.SkipInJavaToManaged ? InvalidJavaToManagedMappingIndex : (uint)managedEntry.ManagedIndex);
 			}
 
 			foreach (TypeMapDebugEntry entry in moduleData.ManagedToJavaMap) {
diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingDebugNativeAssemblyGenerator.cs b/src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingDebugNativeAssemblyGenerator.cs
index 3ae7ba809e4..678c012f180 100644
--- a/src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingDebugNativeAssemblyGenerator.cs
+++ b/src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingDebugNativeAssemblyGenerator.cs
@@ -70,7 +70,9 @@ protected override void WriteSymbols (StreamWriter output)
 			if (haveJavaToManaged) {
 				foreach (TypeMapGenerator.TypeMapDebugEntry entry in data.JavaToManagedMap) {
 					size += WritePointer (output, entry.JavaLabel);
-					size += WritePointer (output, entry.SkipInJavaToManaged ? null : entry.ManagedLabel);
+
+					TypeMapGenerator.TypeMapDebugEntry managedEntry = entry.DuplicateForJavaToManaged != null ? entry.DuplicateForJavaToManaged : entry;
+					size += WritePointer (output, managedEntry.SkipInJavaToManaged ? null : managedEntry.ManagedLabel);
 				}
 			}
 			WriteStructureSize (output, JavaToManagedSymbol, size, alwaysWriteSize: true);
