-
Notifications
You must be signed in to change notification settings - Fork 529
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
mono.android.runtime.JavaObject cannot be cast to byte[] when using List<byte[]> #4098
Comments
The bug is that Consider this Java method: public class Gxa4098 {
public static int genericListMarshaling (List<byte[]> bytes) {
int count = 0;
for (byte[] value : bytes ) {
count += value.length;
}
return count;
}
} Which results in the binding:
Which calls Which hits the Which calls Because A fix to consider would be to add array types to |
Patch for possible unit test: diff --git a/tests/CodeGen-Binding/Xamarin.Android.JcwGen-Tests/BindingTests.cs b/tests/CodeGen-Binding/Xamarin.Android.JcwGen-Tests/BindingTests.cs
index d6cb60db..c53be34a 100644
--- a/tests/CodeGen-Binding/Xamarin.Android.JcwGen-Tests/BindingTests.cs
+++ b/tests/CodeGen-Binding/Xamarin.Android.JcwGen-Tests/BindingTests.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using NUnit.Framework;
@@ -201,6 +202,19 @@ namespace Xamarin.Android.JcwGenTests {
var m = e.Message;
Assert.IsTrue (e.GetMessageInvoked);
}
+
+ // Context: https://github.com/xamarin/xamarin-android/issues/4098
+ [Test]
+ public void GenericListMarshaling ()
+ {
+ var list = new List<byte[]> {
+ new byte[]{ 1, 2, 3 },
+ new byte[]{ 4, 5 },
+ new byte[]{ 6 },
+ };
+ int count = Com.Xamarin.Android.Gxa4098.GenericListMarshaling (list);
+ Assert.AreEqual (6, count);
+ }
}
class ConstructorTest : Com.Xamarin.Android.CallMethodFromCtor {
diff --git a/tests/CodeGen-Binding/Xamarin.Android.McwGen-Tests/Xamarin.Android.McwGen-Tests.csproj b/tests/CodeGen-Binding/Xamarin.Android.McwGen-Tests/Xamarin.Android.McwGen-Tests.csproj
index 044fe4bd..4826e56b 100644
--- a/tests/CodeGen-Binding/Xamarin.Android.McwGen-Tests/Xamarin.Android.McwGen-Tests.csproj
+++ b/tests/CodeGen-Binding/Xamarin.Android.McwGen-Tests/Xamarin.Android.McwGen-Tests.csproj
@@ -97,6 +97,9 @@
<TestJarEntry Include="java\com\xamarin\android\DataListener.java">
<OutputFile>Jars/xamarin-test.jar</OutputFile>
</TestJarEntry>
+ <TestJarEntry Include="java\com\xamarin\android\Gxa4098.java">
+ <OutputFile>Jars/xamarin-test.jar</OutputFile>
+ </TestJarEntry>
<TestJarEntry Include="java\com\xamarin\android\Logger.java">
<OutputFile>Jars/xamarin-test.jar</OutputFile>
</TestJarEntry>
diff --git a/tests/CodeGen-Binding/Xamarin.Android.McwGen-Tests/java/com/xamarin/android/Gxa4098.java b/tests/CodeGen-Binding/Xamarin.Android.McwGen-Tests/java/com/xamarin/android/Gxa4098.java
new file mode 100644
index 00000000..79ee5f06
--- /dev/null
+++ b/tests/CodeGen-Binding/Xamarin.Android.McwGen-Tests/java/com/xamarin/android/Gxa4098.java
@@ -0,0 +1,13 @@
+package com.xamarin.android;
+
+import java.util.List;
+
+public class Gxa4098 {
+ public static int genericListMarshaling (List<byte[]> bytes) {
+ int count = 0;
+ for (byte[] value : bytes ) {
+ count += value.length;
+ }
+ return count;
+ }
+} |
I have a Android binding for the Pointr SDK which is impacted by this issue and causes the following exception to be thrown. |
) Fixes: #4098 Consider the following Java method: // Java public static int count (List<int[]> values) { int c = 0; for (int[] v : values) { c += v.length; } return c; } This is bound as: // C# [Register ("count", "(Ljava/util/List;)I", "")] public static unsafe int Count (IList<int[]> p0) { const string __id = "count.(Ljava/util/List;)I"; IntPtr native_p0 = global::Android.Runtime.JavaList<int[]>.ToLocalJniHandle (p0); try { JniArgumentValue* __args = stackalloc JniArgumentValue [1]; __args [0] = new JniArgumentValue (native_p0); var __rm = _members.StaticMethods.InvokeInt32Method (__id, __args); return __rm; } finally { JNIEnv.DeleteLocalRef (native_p0); } } The problem is that when the `InvokeInt32Method()` statement is hit, *Java* throws an exception: mono.android.runtime.JavaObject cannot be cast to int[] The cause of the problem is that while `JavaList<T>.ToLocalJniHandle()` created a `java.util.ArrayList` instance, the *contents* of the created `ArrayList` were `JavaObject` instances, *not* `int[]` instances. Thus, when the Java code attempts to cast an element from the list to `int[]`, the cast fails. The underlying problem is that e.g. `JavaConvert.WithLocalJniHandle<TValue, TReturn>(…)` didn't handle array types, so if `TValue` were an array, the array would be wrapped into a `JavaObject` instance, instead of being "deep marshaled" to a Java-side array. Add support for array marshaling to `JavaConvert`, so that `JavaLists<int[]>.ToLocalJniHandle()` properly creates a Java-side `ArrayList` which contains `int[]` values. This fixes the cast. Note that I looked at also adding cases for Kotlin's unsigned primitive arrays, however Kotlin only allows their use in very limited circumstances, and `List<uint[]>` is not allowed: // Kotlin UnsignedMethods.kt:39:61: error: expecting a '>' public open fun uintListInstanceMethod (value: List<UInt[]>) : List<UInt[]> { return value; } ^
Release status update A new Preview version of Xamarin.Android has now been published that includes the fix for this item. The fix is not yet included in a Release version. I will update this item again when a Release version is available that includes the fix. Fix included in Xamarin.Android SDK version 11.0.99.9. Fix included on Windows in Visual Studio 2019 version 16.8 Preview 1. To try the Preview version that includes the fix, check for the latest updates in Visual Studio Preview. Fix included on macOS in Visual Studio 2019 for Mac version 8.8 Preview 1. To try the Preview version that includes the fix, check for the latest updates on the Preview updater channel |
Release status update A new Release version of Xamarin.Android has now been published that includes the fix for this item. Fix included in Xamarin.Android SDK version 11.1.0.17. Fix included on Windows in Visual Studio 2019 version 16.8. To get the new version that includes the fix, check for the latest updates or install the most recent release from https://visualstudio.microsoft.com/downloads/. Fix included on macOS in Visual Studio 2019 for Mac version 8.8. To get the new version that includes the fix, check for the latest updates on the Stable updater channel. |
Steps to Reproduce
List<byte[]>
in C#. Pass this to a Java library and themono.android.runtime.JavaObject cannot be cast to byte[]
error is received when the object is accessed in Java.Here is the sample call from the Java library. The call to
csdBuffer.get
throws the exception.Seems like a similar issue to the
java.lang.ClassCastException
information mentioned on this pagehttps://docs.microsoft.com/en-us/xamarin/android/troubleshooting/troubleshooting#javalangclasscastexception-monoandroidruntimejavaobject-cannot-be-cast-to
However the work around suggested there states to use the similar Java collection for the inner collection in the generic. However in this case a byte[] is the inner collection, so is there some comparable Java type that could be used?
Expected Behavior
A
List<byte[]>
can be passed between C# and Java code.Actual Behavior
ClassCastException
is thrown when the Java code attempts to access the byte array.Version Information
Xamarin 16.4.000.307
Running on Android 7.1
Log File
Caused by: java.lang.ClassCastException: mono.android.runtime.JavaObject cannot be cast to byte[]
The text was updated successfully, but these errors were encountered: