Skip to content

[typemap] Handle managed -> Java duplicate mapping correctly #5459

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Merged
merged 1 commit into from
Jan 7, 2021

Conversation

grendello
Copy link
Contributor

Fixes: #5409
Context: a017561
Context: bb55bb0

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

a017561 implemented most of the handling properly, bb55bb0 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.

Fixes: dotnet#5409
Context: a017561
Context: bb55bb0

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

a017561 implemented most of the handling properly, bb55bb0 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.
Copy link
Member

@radekdoulik radekdoulik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be useful to have a test for that?

@grendello
Copy link
Contributor Author

Would it be useful to have a test for that?

Definitely, but it would be nice if it was part of our XF integration app - I don't really know what elements in XF trigger it, so short of copying the .NET6 sample I don't know how to create the test

@radekdoulik
Copy link
Member

We are also running tests with NET6 and XForms in https://github.com/xamarin/xamarin-android/blob/master/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs

That would require knowing what causes the issue in the net6 sample, so that we can add relevant code there. Mentioning it just in case you find out later.

@grendello
Copy link
Contributor Author

We are also running tests with NET6 and XForms in https://github.com/xamarin/xamarin-android/blob/master/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs

That would require knowing what causes the issue in the net6 sample, so that we can add relevant code there. Mentioning it just in case you find out later.

That's the thing, though, the sample is mostly empty... I also don't think it's specific to .NET6, I suspect it's about the XF version used. XF 4.8 used by the .NET6 sample must be doing something differently than the 4.0 version we use in our XF integration test

@jonathanpeppers
Copy link
Member

XF 4.0 would be using the old Android Support libraries and XF 4.6 and higher (with TFV 10.0+) uses AndroidX.

It might just be a difference in using AndroidX.

@jonpryor
Copy link
Member

jonpryor commented Jan 7, 2021

work-in-progress commit message;

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

Context: a017561b1e44c51a9af79fae0baaa50fe01c4123
Context: bb55bb064b967adf8ca975928736346694dceb02

This is another commit in the series of properly handling 1:N
type mappings 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.

The typical example is for the `*Invoker` classes:

	// Java:
	interface Runnable {
	    void run();
	}

	// C# Binding
	[Register ("java/lang/Runnable", "", "Java.Lang.IRunnableInvoker")]
	public interface IRunnable : IJavaObject, IJavaPeerable {
	    [Register (…)]
	    void Run();
	}

	// Used at runtime
	[Register ("java/lang/Runnable", DoNotGenerateAcw=true)]
	internal partial class IRunnableInvoker : Java.Lang.Object, IRunnable {
	    public void Run() {…}
	}

Here, both `IRunnable` and `IRunnableInvoker` have a `[Register]`
custom attribute for `java.lang.Runnable`, and thus *alias* the
`Runnable` type.

@grendello TODO: what is meant by "points back to a duplicate java type"?

In fact, such Managed types aliases were removed from the typemap
unless they were the type pointed to from the Java duplicate.

[I'm still not sure what this means? -@jonpryor]

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()

Modify 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.

This ensures that `IRunnableInvoker` remains in the type map, and is
appropriately mapped to `java.lang.Runnable`.

@grendello: I still have some questions/confusion in ^^; please review. :-)

@grendello
Copy link
Contributor Author

grendello commented Jan 7, 2021

@jonpryor I think the confusion stems from a typo, the 1:N should be N:1 - we have several entries for the same Java type pointing/mapped to a single Managed type. However, the original mapping (before the generator aliases all the Java duplicates to a single Managed type) is 1:1 - every Java type name points to a separate entry for some Managed type. The generator changes that to 1:N for Java -> Managed but not for Managed to Java. The latter will still be a 1:1 mapping, as far as entries in the table are concerned, but it will be a 1:N mapping as far as type names are concerned - thus the Managed type in question will "point back" to a set (effectively) of Java type names sharing the same type name (even though it will reference a single entry in the Java type name table). Thus the "points back to a duplicate" phrasing.

@jonpryor
Copy link
Member

jonpryor commented Jan 7, 2021

@grendello wrote:

I think the confusion stems from a typo, the 1:N should be N:1 - we have several entries for the same Java type pointing/mapped to a single Managed type.

Part of the confusion here is whether we're talking about types or entries. Consider the above example.

This defines two C# types which alias the same Java type.

Thus, for entries, we'd presumably have:

  • Java Runnable :: C# IRunnable
  • Java Runnable :: C# IRunnableInvoker

For Java-to-managed mappings, we'd have 1:N:

  • Java Runnable :: C# { IRunnable, IRunnableInvoker }

For C#-to-Java mappings, we'd have N:1:

  • C# { IRunnable, IRunnableInvoker } :: Java Runnable

Fundamental Question #1: is Runnable an actually meaningfully useful example here? Or do we need an abstract class and not an interface, e.g. LayoutInflater (which perhaps my examples should use anyway so that it matches the error message…).

All the above has been a mental exercise for me; what about the file format, as per 7117414, which is where the problem is?

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::managed_type_name_width];
    uint32_t            jni_index;      // Index into DebugTypemapFileHeader::java_to_managed
};

DebugTypemapFileHeader::entry_count is a single shared value, used by JavaToManaged and ManagedToJava entries.

What I don't know/remember is whether those tables have duplicates. The current commit message suggests that there can be:

In fact, such Managed types were removed from the typemap unless they were the type pointed to from the Java duplicate.

Thus, question #2: before this fix, what was the tabular structure? I think it would be:

java_to_managed = {
    { "android/view/LayoutInflater", 0 },
    { "android/view/LayoutInflater", uint.MaxValue },  // invalid
}
managed_to_java = {
    { "Android.View.LayoutInflater", 0 },
    { "Android.View.LayoutInflaterInvoker", 0 },
}

After this fix, what is the tabular structure? I think it will be:

java_to_managed = {
    { "android/view/LayoutInflater", 0 },
    { "android/view/LayoutInflater", 0 },
}
managed_to_java = {
    { "Android.View.LayoutInflater", 0 },
    { "Android.View.LayoutInflaterInvoker", 0 },
}

@grendello
Copy link
Contributor Author

The Java to Managed tables have duplicates, yes. There can be X entries witth the same type name, and all of them pointing to a single Managed type. There are no duplicates in the Managed to Java table, but there's an equal number of entries - this comes from the fact that many different Managed types (as in your invoker example) can register the same Java type.

This fix doesn't change where the Java to Managed entries point - all the duplicate Java types still point to the FIRST Managed type. However, previously all the Managed types were made equal to the FIRST Managed type - that's what caused the issue. With this fix, all the Managed types remain with their original names. And that's the only change here. So, where previously we didn't have a Managed to Java entry for Android.View.LayoutInflaterInvoker -> android/vewLayoutInflater (we had TWO Android.View.LayoutInflater -> android/vewLayoutInflater entries, as the managed Android.View.LayoutInflaterInvoker type name was made to be equal as the FIRST managed type pointed to by the Java 'android/views/LayoutInflater- that is,Android.View.LayoutInflater`.

@jonpryor
Copy link
Member

jonpryor commented Jan 7, 2021

Commit message take 2!

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

Context: a017561b1e44c51a9af79fae0baaa50fe01c4123
Context: bb55bb064b967adf8ca975928736346694dceb02

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 (7117414c),
and because of a017561b 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 a017561b 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

@jonpryor jonpryor merged commit 66f7206 into dotnet:master Jan 7, 2021
@grendello grendello deleted the typemap-duplicates-again branch January 7, 2021 17:57
jonpryor pushed a commit that referenced this pull request Jan 15, 2021
…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
@github-actions github-actions bot locked and limited conversation to collaborators Jan 26, 2024
# for free to subscribe to this conversation on GitHub. Already have an account? #.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[.NET 6] Debug build of Java.Interop.dll crashes Xamarin.Forms applications
5 participants