Skip to content

[Export] doesn't support float[] #1259

Description

@jonpryor

From: dotnet/java-interop#264

Steps to Reproduce

  1. Add a class which contains an [Export] method which contains an array parameter.
  2. Call the method in (1) from Java

Expected Behavior

It works!

Actual Behavior

System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. --->
System.NotSupportedException: Only primitive types and IJavaObject is supported in array type in callback method parameter or return value

What's funny is that float[] is a "primitive type" for array types.

Related: https://github.com/xamarin/xamarin-android/blob/20123b569151eea9faaa859de902ca9b09a17a5d/src/Mono.Android.Export/CallbackCode.cs#L252

Activity

  1. mfkl commented on Feb 25, 2018

    @mfkl

    Is there anything I can do to help to move this issue forward?

  2. atsushieno commented on Feb 27, 2018

    @atsushieno
    Contributor

    Some of these lines fixes the array item check part of the issue. https://gist.github.com/atsushieno/a69bff3afe0afc2e7893e26af61450a1

    However the changes from at https://github.com/xamarin/monodroid/commit/0d547ce broke array conversion that I have no idea how to fix. @jonpryor

  3. atsushieno commented on Feb 27, 2018

    @atsushieno
    Contributor

    Repro:

    FooBar.java (AndroidJavaSource):

    package androidtest1;
    
    public class FooBar
    {
    	public static void invokeJCW (Object instance) throws Exception
    	{
    		java.lang.reflect.Method [] methods = Class.forName("androidtest1.Example").getMethods ();
    		for (int i = 0; i < methods.length; i++)
    			if (methods [i].getName () == "SetRanges") {
    				Object arg = new double [] {0, 1, 2};
    				methods [i].invoke (instance, "foo", arg, 4);
    				return;
    			}
    		throw new Exception ("method does not exist");
    	}
    }
    

    MainActivity.cs:

    		protected override void OnCreate (Bundle savedInstanceState)
    		{
    			base.OnCreate (savedInstanceState);
    			var methods = Java.Lang.Class.ForName ("androidtest1.FooBar")
    							  .GetMethods ();
    			var invoker = methods.FirstOrDefault (_ => _.Name == "invokeJCW");
    			invoker.Invoke (null, new Example ());
    		}
    
    	public class Example : Java.Lang.Object
    	{
    		[Export]
    		public void SetRanges (string text, double [] args, double arg2)
    		{
    		}
    	}
    }
    
  4. mfkl commented on Mar 6, 2018

    @mfkl

    Ok.
    FYI, cannot access this link

    However the changes from at xamarin/monodroid@0d547ce broke array conversion

    Seems like not too many people need this feature?

  5. atsushieno commented on Mar 6, 2018

    @atsushieno
    Contributor

    Sorry it is a private repo that only makes sense to the devs.

  6. jonpryor commented on Mar 6, 2018

    @jonpryor
    ContributorAuthor

    @atsushieno: I don't immediately see how or why that commit broke things. Before the change, DynamicInvokeTypeInfo.jnienv_newarray was a MethodInfo to JNIEnv.NewArray<T>(T[]), and should have been a generic method definition.

    After the change, DynamicInvokeTypeInfo.jnienv_newarray is a MethodInfo to JNIEnv.NewArray<T>(T[]), and is a generic method definition.

    In System.Array terms:

    // Actually the `csharp` command, but close enough...
    
    // Ways to obtain the MethodInfo for System.Array.BinarySearch<T>(T[], int)
    var a = typeof(Array);
    
    // 1. Via Reflection + LINQ
    var bsr = a.GetMethods().First(m => m.IsGenericMethod && m.Name == "BinarySearch" && m.GetParameters().Length == 2);
    
    // 2: Via Delegate conversion + Delegate.Method
    public class E {
        public static MethodInfo GetBinarySearchT<T>(Func<T[], T, int> func) {
            return func.Method.GetGenericMethodDefinition ();
        }
    }
    var bsm = E.GetBinarySearchT<int>(Array.BinarySearch);
    
    // Results are the same, but mechanism 2 is much faster.
    Console.WriteLine(bsm == bsr);
    // true
  7. atsushieno commented on Mar 6, 2018

    @atsushieno
    Contributor

    The change, that you are quite aware of, therefore results in:

    [MonoDroid] UNHANDLED EXCEPTION:
    [MonoDroid] System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.InvalidOperationException: Call to method AndroidTest1.Example.SetRanges: Parameter 1. Can't cast from T[] to System.Double[]
    [MonoDroid]   at Mono.CodeGeneration.CodeGenerationHelper.GenerateMethodCall (System.Reflection.Emit.ILGenerator gen, Mono.CodeGeneration.CodeExpression target, System.Reflection.MethodBase method, System.Type[] parameterTypes, Mono.CodeGeneration.CodeExpression[] parameters) [0x00118] in <19387e49dc8946eda9d7ad05203f74fb>:0 
    [MonoDroid]   at Mono.CodeGeneration.CodeGenerationHelper.GenerateMethodCall (System.Reflection.Emit.ILGenerator gen, Mono.CodeGeneration.CodeExpression target, System.Reflection.MethodBase method, Mono.CodeGeneration.CodeExpression[] parameters) [0x00041] in <19387e49dc8946eda9d7ad05203f74fb>:0 
    [MonoDroid]   at Mono.CodeGeneration.CodeMethodCall.Generate (System.Reflection.Emit.ILGenerator gen) [0x00029] in <19387e49dc8946eda9d7ad05203f74fb>:0 
    [MonoDroid]   at Mono.CodeGeneration.CodeMethodCall.GenerateAsStatement (System.Reflection.Emit.ILGenerator gen) [0x00001] in <19387e49dc8946eda9d7ad05203f74fb>:0 
    [MonoDroid]   at Mono.CodeGeneration.CodeBlock.Generate (System.Reflection.Emit.ILGenerator gen) [0x0002a] in <19387e49dc8946eda9d7ad05203f74fb>:0 
    [MonoDroid]   at Mono.CodeGeneration.CodeBuilder.Generate (System.Reflection.Emit.ILGenerator gen) [0x00001] in <19387e49dc8946eda9d7ad05203f74fb>:0 
    [MonoDroid]   at Mono.CodeGeneration.CodeMethod.Generate (System.Reflection.Emit.ILGenerator gen) [0x00015] in <19387e49dc8946eda9d7ad05203f74fb>:0 
    [MonoDroid]   at Java.Interop.DynamicCallbackCodeGenerator.GenerateNativeCallbackDelegate () [0x000d3] in <19387e49dc8946eda9d7ad05203f74fb>:0 
    [MonoDroid]   at Java.Interop.DynamicCallbackCodeGenerator.GetCallback () [0x0000e] in <19387e49dc8946eda9d7ad05203f74fb>:0 
    [MonoDroid]   at Java.Interop.DynamicCallbackCodeGenerator.Create (System.Reflection.MethodInfo method) [0x00007] in <19387e49dc8946eda9d7ad05203f74fb>:0 
    [MonoDroid]   at (wrapper managed-to-native) System.Reflection.MonoMethod.InternalInvoke(System.Reflection.MonoMethod,object,object[],System.Exception&)
    [MonoDroid]   at System.Reflection.MonoMethod.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00032] in <316413990ea1468fadc4a3d4de9c4235>:0 
    
  8. jonpryor commented on Mar 6, 2018

    @jonpryor
    ContributorAuthor

    @atsushieno: From what I can tell, the change should have had no semantic effect: before the change, DynamicInvokeTypeInfo.jnienv_newarray is a MethodInfo referring to the generic method definition for JNIEnv.NewArray<T>(T[]), and after the change it continues to be a MethodInfo referring to the generic method definition for JNIEnv.NewArray<T>(T[]).

    I do not believe that the commit you're referring to is responsible for the System.InvalidOperationException. "Locally reverting" that patch should show that it has nothing to do with the InvalidOperationException, and would take ~5 minutes to verify. I cannot see why it would have anything to do with the problem.

    Furthermore, the only quick places I can find use of CallbackCode.jnienv_newarray is in CallbackCode.cs, e.g. within DynamicInvokeTypeInfo.ToNative(). This location uses jnienv_newarray.MakeGenericMethod(), so the created CodeMethodCall() shouldn't even be using a type parameter, it should be using a known type such as double.

    Additionally, if DynamicInvokeTypeInfo.jnienv_newarray weren't a generic method definition, then the .MakeGenericMethod() call would itself have been throwing. It isn't.

    Finally, I'm assuming you have a local patch to even be generating the above stack trace. What is that patch? Without any changes, we should be getting the originally mentioned NotSupportedException.

  9. atsushieno commented on Mar 9, 2018

    @atsushieno
    Contributor

    My stacktrace is based on the local patch I posted at #1259 (comment) . The patch also tells that there were method signature changes in JNIEnv GetArray() that resulted in method argument count mismatch. Therefore I noticed that there were incompatible API changes.

  10. atsushieno commented on Mar 9, 2018

    @atsushieno
    Contributor

    Currently md-addins broke debugger which makes it quite annoying to debug any issues.

  11. mfkl commented on Apr 3, 2018

    @mfkl

    Is there currently a workaround I could use regarding this?

  12. atsushieno commented on Apr 3, 2018

    @atsushieno
    Contributor

    No. I would completely avoid depending on any code that requires [Export] by writing managed code that overrides existing Java methods which would result in a working Java callable wrappers though.

  13. mfkl commented on Apr 3, 2018

    @mfkl

    Ok, thanks for the idea. Though I'm not sure this can work in my use case: C code calling JNI exposed code (which I intended to generate through [Export] attributes). There is no existing Java code per se.

    I will look for another way.

  14. atsushieno commented on Apr 3, 2018

    @atsushieno
    Contributor

    For any native (C) functions P/Invoke just works.

  15. mfkl commented on Apr 3, 2018

    @mfkl

    That's not it, maybe I was not clear.

    I'm not calling C from C# (in this case), C is calling me through JNI with https://github.com/videolan/vlc/blob/a16a24bce5e7e8fa6b2c05e63f5524e1d8df0640/modules/video_output/android/utils.c#L434
    I guess my best bet then is to build the relevant java code into a .jar and put that in an Android binding library project. Thanks though.

  16. rraallvv commented on Jun 29, 2018

    @rraallvv

    Same issue here when trying to export the following method:

    [JavascriptInterface]
    [Export("log")]
    public void Log(params string[] data)
    {
        System.Diagnostics.Debug.WriteLine("NetworkView: " + string.Join(" ", data));
    }
    
  17. rraallvv commented on Jul 2, 2018

    @rraallvv

    Is this a regression? If so, could someone please point me where to find what version of Xamarin does not have this issue. Thanks.

  18. yorkvolvo commented on Jan 28, 2019

    @yorkvolvo

    Is there any updates regrading this issue?

  19. dotnet-policy-service commented on May 8, 2026

    @dotnet-policy-service

    We suspect this issue is stale and no longer relevant. It will be closed if no further activity occurs within 14 more days. Any new comment (by anyone, not necessarily the author) will undo this process.

  20. locked and limited conversation to collaborators on Jun 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Area: BindingsIssues in Java Library Binding projects.possibly-staleIssues that are potentially no longer relevant.

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions