Skip to content

Commit ebd7d76

Browse files
authored
[Java.Interop.Tools.JavaCallableWrappers] Include interfaces in type maps (#824)
Context: 29f9707 Context: dotnet/android#5859 Java typemaps (38a1a2c) have never included interfaces because, until very recently (29f9707), they couldn't contain methods, and thus they couldn't have been used as method invocation targets. With the addition of Default Interface Methods, this is no longer true. The setup: // 3fcc714, a27a5c2, … namespace Java.Interop { partial class JniPeerMembers { JniPeerMembers (string jniPeerType, Type managedPeerType, bool checkManagedPeerType) { … Debug.Assert ( JniEnvironment.Runtime.TypeManager.GetTypeSignature (managedPeerType).JniTypeName == jniPeerType $"ManagedPeerType <=> JniTypeName Mismatch! javaVM.GetJniTypeInfoForType(typeof({managedPeerType.FullName})).JniTypeName=\"{JniEnvironment.Current.JavaVM.GetJniTypeInfoForType (managedPeerType).JniTypeName}\" != \"{jniPeerType}\" } } } // generated in xamarin-android namespace Android.App { public partial class Application { public partial interface IActivityLifecycleCallbacks : IJavaObject, IJavaPeerable { private static readonly JniPeerMembers _members = new JniPeerMembers ("android/app/Application$ActivityLifecycleCallbacks", typeof (IActivityLifecycleCallbacks), isInterface: true); } } } // https://github.com/dotnet/maui/blob/18e0f4ebbcca7c904ccdb67e6439cb72382e2a40/src/Core/src/Platform/Android/MauiApplication.cs#L56-L79 namespace Microsoft.Maui { public partial class MauiApplication : Application { public class ActivityLifecycleCallbacks : Java.Lang.Object, IActivityLifecycleCallbacks { // … } } } The punch; at runtime, with .NET 6, when using a *Debug* build of `Java.Interop.dll`, the app crashes with an assertion failure when instantiating the `MauiApplication.ActivityLifecycleCallbacks` type: ---- DEBUG ASSERTION FAILED ---- ---- Assert Short Message ---- ManagedPeerType <=> JniTypeName Mismatch! javaVM.GetJniTypeInfoForType(typeof(Android.App.Application+IActivityLifecycleCallbacks)).JniTypeName="" != "android/app/Application$ActivityLifecycleCallbacks" ---- Assert Long Message ---- at System.Diagnostics.DebugProvider.Fail(String message, String detailMessage) in System.Private.CoreLib.dll:token 0x6005586+0x0 at System.Diagnostics.Debug.Fail(String message, String detailMessage) in System.Private.CoreLib.dll:token 0x6005548+0x0 at System.Diagnostics.Debug.Assert(Boolean condition, String message, String detailMessage) in System.Private.CoreLib.dll:token 0x6005545+0x0 at System.Diagnostics.Debug.Assert(Boolean condition, String message) in System.Private.CoreLib.dll:token 0x6005544+0x0 at Java.Interop.JniPeerMembers..ctor(String jniPeerTypeName, Type managedPeerType, Boolean checkManagedPeerType, Boolean isInterface) in Java.Interop.dll:token 0x600032e+0x0 at Java.Interop.JniPe Process terminated due to "ManagedPeerType <=> JniTypeName Mismatch! javaVM.GetJniTypeInfoForType(typeof(Android.App.Application+IActivityLifecycleCallbacks)).JniTypeName="" != "android/app/Application$ActivityLifecycleCallbacks" at System.Diagnostics.DebugProvider.Fail(String message, String detailMessage) in System.Private.CoreLib.dll:token 0x6005586+0x0 at System.Diagnostics.Debug.Fail(String message, String detailMessage) in System.Private.CoreLib.dll:token 0x6005548+0x0 at System.Diagnostics.Debug.Assert(Boolean condition, String message, String detailMessage) in System.Private.CoreLib.dll:token 0x6005545+0x0 at System.Diagnostics.Debug.Assert(Boolean condition, String message) in System.Private.CoreLib.dll:token 0x6005544+0x0 at Java.Interop.JniPeerMembers..ctor(String jniPeerTypeName, Type managedPeerType, Boolean checkManagedPeerType, Boolean isInterface) in Java.Interop.dll:token 0x600032e+0x0 at Java.Interop.JniPeerMembers..ctor(String jniPeerTypeName, Type managedPeerType, Boolean isInterface) in Java.Interop.dll:token 0x600032c+0x0 at Android.Runtime.XAPeerMembers..ctor(String jniPeerTypeName, Type managedPeerType, Boolean isInterface) in Mono.Android.dll:token 0x6017ed3+0x0 at Android.App.Application.IActivityLifecycleCallbacks..cctor() in Mono.Android.dll:token 0x6025027+0x0 at Android.Runtime.AndroidTypeManager.RegisterNativeMembers(JniType nativeClass, Type type, String methods) in Mono.Android.dll:token 0x6017c19+0x0 at Android.Runtime.JNIEnv.RegisterJniNatives(IntPtr typeName_ptr, Int32 typeName_len, IntPtr jniClass, IntPtr methods_ptr, Int32 methods_len) in Mono.Android.dll:token 0x6017d48+0x0 at Java.Interop.NativeMethods.java_interop_jnienv_alloc_object(IntPtr , IntPtr& , IntPtr ) in Java.Interop.dll:token 0x0+0x0 at Java.Interop.JniEnvironment.Object.AllocObject(JniObjectReference type) in Java.Interop.dll:token 0x6000563+0x0 at Java.Interop.JniType.AllocObject() in Java.Interop.dll:token 0x60003a1+0x0 at Java.Interop.JniPeerMembers.JniInstanceMethods.StartCreateInstance(String constructorSignature, Type declaringType, JniArgumentValue* parameters) in Java.Interop.dll:token 0x60005e5+0x0 at Java.Lang.Object..ctor() in Mono.Android.dll:token 0x601ea58+0x0 at Microsoft.Maui.MauiApplication.ActivityLifecycleCallbacks..ctor() in Microsoft.Maui.dll:token 0x60004ef+0x0 at Microsoft.Maui.MauiApplication`1[[HelloMaui.Startup, HelloMaui, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].OnCreate() in Microsoft.Maui.dll:token 0x6000114+0x0 at Android.App.Application.n_OnCreate(IntPtr jnienv, IntPtr native__this) in Mono.Android.dll:token 0x60153c7+0x0 at Android.Runtime.DynamicMethodNameCounter.1(IntPtr , IntPtr ) in Mono.Android.dll:token 0x0+0x0" This is caused by the fact that a class (`MauiApplication.ActivityLifecycleCallbacks` in this case) implements a Java interface (`IActivityLifecycleCallbacks` here) but it does not implement all the interface members. In such an instance, the default interface methods may be called (if available), and calling the Java default interface methods causes the `IActivityLifecycleCallbacks` static constructor to run, which in turn initializes `IActivityLifecycleCallbacks._members`, which constructs its corresponding `JniPeerMembers` instance, triggering the assert. The assert fails because the type maps don't include bound Java interfaces, and thus `JniTypeManager.GetTypeSignature (managedPeerType)` returns a default (empty, unset) value. This in turn means that `JniTypeSignature.JniTypeName` is the empty string, which *doesn't* match the `jniPeerType` parameter. Fix the assert by including all interfaces implementing the `Java.Interop.IJavaPeerable` interface within typemaps. This ensures that `JniTypeManager.GetTypeSignature(Type)` will work when given an bound interface type.
1 parent 002dea4 commit ebd7d76

File tree

1 file changed

+4
-3
lines changed
  • src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers

1 file changed

+4
-3
lines changed

src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaTypeScanner.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,12 @@ public List<TypeDefinition> GetJavaTypes (IEnumerable<string> assemblies, IAssem
5050

5151
void AddJavaTypes (List<TypeDefinition> javaTypes, TypeDefinition type)
5252
{
53-
if (type.IsSubclassOf ("Java.Lang.Object", cache) || type.IsSubclassOf ("Java.Lang.Throwable", cache)) {
54-
53+
if (type.IsSubclassOf ("Java.Lang.Object", cache) ||
54+
type.IsSubclassOf ("Java.Lang.Throwable", cache) ||
55+
(type.IsInterface && type.ImplementsInterface ("Java.Interop.IJavaPeerable", cache))) {
5556
// For subclasses of e.g. Android.App.Activity.
5657
javaTypes.Add (type);
57-
} else if (type.IsClass && !type.IsSubclassOf ("System.Exception", cache) && type.ImplementsInterface ("Android.Runtime.IJavaObject", cache)) {
58+
} else if (type.IsClass && !type.IsSubclassOf ("System.Exception", cache) && type.ImplementsInterface ("Android.Runtime.IJa", cache)) {
5859
var level = ErrorOnCustomJavaObject ? TraceLevel.Error : TraceLevel.Warning;
5960
var prefix = ErrorOnCustomJavaObject ? "error" : "warning";
6061
Logger (

0 commit comments

Comments
 (0)