Skip to content

Commit 79a0d54

Browse files
grendellojonpryor
authored andcommitted
[monodroid] Handle managed -> Java duplicate type mapping correctly (#5459)
Fixes: #5409 Context: a017561 Context: bb55bb0 Yet Another Java :: Managed Type Aliasing Issue. Type aliasing occurs when multiple managed types bind the same Java type. One common scenario for this is for `abstract` classes: // Java: public abstract class LayoutInflater { // … } // C# Binding [Register ("android/view/LayoutInflater", DoNotGenerateAcw=true)] public abstract class LayoutInflater : Java.Lang.Object { static readonly JniPeerMembers _members = new XAPeerMembers ("android/view/LayoutInflater", typeof (LayoutInflater)); } // Used at runtime [Register ("android/view/LayoutInflater", DoNotGenerateAcw=true)] internal partial class LayoutInflaterInvoker : LayoutInflater { static readonly JniPeerMembers _members = new XAPeerMembers ("android/view/LayoutInflater", typeof (LayoutInflaterInvoker)); } Both the C# `LayoutInflater` and `LayoutInflaterInvoker` types *alias* the Java `LayoutInflater` type. In a *Debug* configuration build, the mappings between the Java types and Managed types is held within a per-assembly mapping (7117414), and because of a017561 there is no explicit managed entry for `LayoutInflaterInvoker`: DebugTypemap = { .java_to_managed = { { "android/view/LayoutInflater", 0 }, // Java LayoutInflater -> C# LayoutInflater { "android/view/LayoutInflater", 0 }, // Java LayoutInflater -> C# LayoutInflater }, .managed_to_java = { { "Android.Views.LayoutInflater", 0 }, // C# LayoutInflater -> Java LayoutInflater { "Android.Views.LayoutInflater", 0 }, // C# LayoutInflaterInvoker -> Java LayoutInflater }, } At *runtime* when the `LayoutInflaterInvoker` static constructor is executed, we hit an [assert in the `JniPeerMembers` constructor][0]: 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() The cause of the assertion failure is because the `.managed_to_java` table doesn't contain an entry for `LayoutInflaterInvoker`. The fix is to modify a017561 behavior and *retain* the `*Invoker` types in the `.managed_to_java` table: DebugTypemap = { .java_to_managed = { { "android/view/LayoutInflater", 0 }, // Java LayoutInflater -> C# LayoutInflater { "android/view/LayoutInflater", 0 }, // Java LayoutInflater -> C# LayoutInflater }, .managed_to_java = { { "Android.Views.LayoutInflater", 0 }, // C# LayoutInflater -> Java LayoutInflater { "Android.Views.LayoutInflaterInvoker", 0 }, // C# LayoutInflaterInvoker -> Java LayoutInflater }, } This ensures that we can appropriately obtain a Java type name for the `LayoutInflaterInvoker` type, fixing the assert. [0]: https://github.com/xamarin/java.interop/blob/3894cd76f71f618949c8542f0edd95762e22881f/src/Java.Interop/Java.Interop/JniPeerMembers.cs#L29
1 parent 5561ce8 commit 79a0d54

File tree

2 files changed

+12
-5
lines changed

2 files changed

+12
-5
lines changed

src/Xamarin.Android.Build.Tasks/Utilities/TypeMapGenerator.cs

+9-4
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ internal sealed class TypeMapDebugEntry
8181
public int ManagedIndex;
8282
public TypeDefinition TypeDefinition;
8383
public bool SkipInJavaToManaged;
84+
public TypeMapDebugEntry DuplicateForJavaToManaged;
8485
}
8586

8687
// Widths include the terminating nul character but not the padding!
@@ -267,11 +268,13 @@ void SyncDebugDuplicates (Dictionary<string, List<TypeMapDebugEntry>> javaDuplic
267268
continue;
268269
}
269270

271+
// Java duplicates must all point to the same managed type
272+
// Managed types, however, must point back to the original Java type instead
273+
// File/assembly generator use the `DuplicateForJavaToManaged` field to know to which managed type the
274+
// duplicate Java type must be mapped.
270275
TypeMapDebugEntry template = duplicates [0];
271276
for (int i = 1; i < duplicates.Count; i++) {
272-
duplicates[i].TypeDefinition = template.TypeDefinition;
273-
duplicates[i].ManagedName = template.ManagedName;
274-
duplicates[i].SkipInJavaToManaged = template.SkipInJavaToManaged;
277+
duplicates[i].DuplicateForJavaToManaged = template;
275278
}
276279
}
277280
}
@@ -571,7 +574,9 @@ void OutputModule (BinaryWriter bw, ModuleDebugData moduleData)
571574
foreach (TypeMapDebugEntry entry in moduleData.JavaToManagedMap) {
572575
bw.Write (outputEncoding.GetBytes (entry.JavaName));
573576
PadField (bw, entry.JavaName.Length, (int)moduleData.JavaNameWidth);
574-
bw.Write (entry.SkipInJavaToManaged ? InvalidJavaToManagedMappingIndex : (uint)entry.ManagedIndex);
577+
578+
TypeMapGenerator.TypeMapDebugEntry managedEntry = entry.DuplicateForJavaToManaged != null ? entry.DuplicateForJavaToManaged : entry;
579+
bw.Write (managedEntry.SkipInJavaToManaged ? InvalidJavaToManagedMappingIndex : (uint)managedEntry.ManagedIndex);
575580
}
576581

577582
foreach (TypeMapDebugEntry entry in moduleData.ManagedToJavaMap) {

src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingDebugNativeAssemblyGenerator.cs

+3-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,9 @@ protected override void WriteSymbols (StreamWriter output)
7070
if (haveJavaToManaged) {
7171
foreach (TypeMapGenerator.TypeMapDebugEntry entry in data.JavaToManagedMap) {
7272
size += WritePointer (output, entry.JavaLabel);
73-
size += WritePointer (output, entry.SkipInJavaToManaged ? null : entry.ManagedLabel);
73+
74+
TypeMapGenerator.TypeMapDebugEntry managedEntry = entry.DuplicateForJavaToManaged != null ? entry.DuplicateForJavaToManaged : entry;
75+
size += WritePointer (output, managedEntry.SkipInJavaToManaged ? null : managedEntry.ManagedLabel);
7476
}
7577
}
7678
WriteStructureSize (output, JavaToManagedSymbol, size, alwaysWriteSize: true);

0 commit comments

Comments
 (0)