Skip to content

Commit ccaeec1

Browse files
authored
Bump to xamarin/Java.Interop/main@f9faaaba; skip jcw interfaces (#5859)
Context: dotnet/java-interop@ebd7d76 Context: dotnet/java-interop@f9faaab Changes: dotnet/java-interop@a3de91e...f9faaab * dotnet/java-interop@f9faaaba: [jcw-gen] Skip interface types (#825) * dotnet/java-interop@64399900: [Java.Interop.Tools.JavaCallableWrappers] Fix typo. * dotnet/java-interop@ebd7d761: [Java.Interop.Tools.JavaCallableWrappers] Include interfaces in type maps (#824) * dotnet/java-interop@002dea4a: Bump to xamarin/xamarin-android-tools/main@d92fc3e3 (#823) Fix a runtime assertion failure within a `Java.Interop.JniPeerMembers` constructor when `JniRuntime.JniTypeManager.GetTypeSignature()` is used with a bound interface type: var type = typeof(global::Java.Lang.Iterable); var sig = JniEnvironment.Runtime.TypeManager.GetTypeSignature(type); var jniType = sig.JniTypeName; The expectation is that `jniType` should be `java/lang/Iterable`. In actuality, `jniType` was the empty string `""`. When `JniTypeSignature.JniTypeName` didn't match the expected value, an assertion failed, causing the app to crash: ---- DEBUG ASSERTION FAILED ---- ---- Assert Short Message ---- ManagedPeerType <=> JniTypeName Mismatch! javaVM.GetJniTypeInfoForType(typeof(Java.Lang.IIterable)).JniTypeName="" != "java/lang/Iterable" ---- Assert Long Message ---- 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) Process terminated due to "ManagedPeerType <=> JniTypeName Mismatch! javaVM.GetJniTypeInfoForType(typeof(Java.Lang.IIterable)).JniTypeName="" != "java/lang/Iterable" … The "cause" of the crash is that type map files did not contain interfaces. There was no actual "need" to store typemap data for interfaces, as the most common use of `JniPeerMembers` was for bound types, and our interface bindings (mostly) didn't use `JniPeerMembers`, so the lack of typemap data for interfaces wasn't a problem. This "lack of need" changed with dotnet/java-interop@29f97075, when Java default interface methods were bound as C# Interface Default Members. *Now*, interface bindings *can* use `JniPeerMembers`: public partial interface IIterable : IJavaObject, IJavaPeerable { private static readonly JniPeerMembers _members = new XAPeerMembers ("java/lang/Iterable", typeof (IIterable), isInterface: true); virtual unsafe void ForEach(Java.Util.Functions.IConsumer action) => … } *However*, the assertion is only reached if a default interface method is executed, as that's what causes e.g. `IIterable._members` to be constructed, triggering the assertion. Related question: why didn't we see this back in Fall 2020? Two reasons: 1. Most important, this was a `Debug.Assert()` assertion, which in turn requires that the assembly be built with `DEBUG` defined. This is *never* the case for assemblies shipped to customers; those are built in the Release configuration. Thus, this could only happen to developers/maintainers of Xamarin.Android. 2. When *Mono* is used, `Debug.Assert()` prints out a message, and continues execution. The process does not crash. This changes with .NET 6: `Debug.Assert()` failures will abort the process! Thus, the only "reasonable" way to trigger this assert was to: 1. Be a maintainer of xamarin-android, and have Debug builds of the product. 2. Be running on .NET 6. 3. "Just happen" to be running an app that makes use of interface default members. In this particular case, the [HelloMaui][0] sample. The fix is twofold: 1. Start including interfaces within typemaps. 2. Update Java Callable Wrapper generation to *ignore* interfaces. This is necessary because our Java Callable Wrapper infrastructure doesn't support "exporting" C# interfaces to Java, and will thus throw an exception if we attempt to do so. This allows `HelloMaui` to launch on .NET 6 with Debug builds of Xamarin.Android without triggering the assertion. [0]: https://github.com/dotnet/net6-mobile-samples/tree/46129f85d78b55589280e5bb0ea399bb1dd72167/HelloMaui
1 parent d066ccf commit ccaeec1

File tree

3 files changed

+8
-2
lines changed

3 files changed

+8
-2
lines changed

external/Java.Interop

src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,11 @@ bool CreateJavaSources (IEnumerable<TypeDefinition> javaTypes, TypeDefinitionCac
328328

329329
bool ok = true;
330330
foreach (var t in javaTypes) {
331+
if (t.IsInterface) {
332+
// Interfaces are in typemap but they shouldn't have JCW generated for them
333+
continue;
334+
}
335+
331336
using (var writer = MemoryStreamPool.Shared.CreateStreamWriter ()) {
332337
try {
333338
var jti = new JavaCallableWrapperGenerator (t, Log.LogWarning, cache) {

src/monodroid/jni/build-info.hh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
namespace xamarin::android::internal
99
{
1010
#define STRINGIFY(_val_) #_val_
11+
#define API_STRING(_api_) STRINGIFY(_api_)
1112
#define VERSION_STRING(_major_, _minor_, _build_) STRINGIFY(_major_) "." STRINGIFY(_minor_) "." STRINGIFY(_build_)
1213

1314
class BuildInfo final
@@ -50,7 +51,7 @@ namespace xamarin::android::internal
5051

5152
static constexpr char ndk_api_level[] =
5253
#if defined (__ANDROID_API__)
53-
STRINGIFY(__ANDROID_API__);
54+
API_STRING(__ANDROID_API__);
5455
#else // def __ANDROID_API__
5556
"";
5657
#endif // ndef __ANDROID_API__

0 commit comments

Comments
 (0)