Git Product home page Git Product logo

java-interop's Introduction

Java.Interop

Java.Interop is a binding of the Java Native Interface for use from managed languages such as C#, and an associated set of code generators to allow Java code to invoke managed code.

This allows one to bridge code running on .NET's CLR and code running on a Java VM.

Note this does not mean that one can run Java code on .NET, or vice-versa.

Java.Interop currently does not ship independently. It is shipped as part of Microsoft's .NET for Android product, available via Visual Studio or .NET 6+. However, it is designed to be fully independent of Android and should be usable by other Java implementations. For other uses, please compile and distribute from source.

Some additional context for this project is documented in the Motivation and Architecture pages.

Building

  • The main branch is configured to build with .NET 7, available here.
  • The release/6.0.3xx branch is configured to build with .NET 6.

Java.Interop.sln must first run some "preparatory" tasks before it can be built:

dotnet build -t:Prepare

Once Java.Interop.sln has been prepared, it can be built in Visual Studio 2022 or with dotnet:

dotnet build

Additional build options are documented here.

Feedback and Contributing

This project welcomes issues and PRs.

License

Copyright (c) .NET Foundation Contributors. All rights reserved. Licensed under the MIT License.

java-interop's People

Contributors

akoeplinger avatar alexrp avatar atsushieno avatar brendanzagaeski avatar csigs avatar dellis1972 avatar dependabot[bot] avatar duncanmak avatar grendello avatar gugavaro avatar hamidzr avatar jamiemagee avatar jonathanpeppers avatar jonpryor avatar jpobst avatar kumpera avatar lewurm avatar luhenry avatar mattleibow avatar mblowey avatar msylvia avatar pjcollins avatar radekdoulik avatar radical avatar redth avatar simonrozsival avatar steveisok avatar vs-mobiletools-engineering-service2 avatar xmcclure avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

java-interop's Issues

Bindingsgenerator: Package public class inheriting from package class results in error

We have two classes, public class MyPublicClass extends MyPackageClass and abstract class MyPackageClass (package private class). This generates code for MyPublicClass which wants to inherit from MyPackageClass, but because parent class is package private, no C# code is generated for it.

package com.xamarin;

abstract class MyPackageClass {
    public abstract void bugOne();
}


package com.xamarin;

public class MyPublicClass extends MyPackageClass {

    @Override
    public void bugOne() {

    }
}

It generates c# code:

public partial class MyPublicClass : global::Com.Xamarin.MyPackageClass {

which results in this error:

obj/Debug/generated/src/Com.Xamarin.MyPublicClass.cs(9,59,9,73): error CS0234: The type or namespace name 'MyPackageClass' does not exist in the namespace 'Com.Xamarin' (are you missing an assembly reference?)

This reproduces with Visual Studio for Mac Community 2017 and also latest generator.exe code compiled from source. Attached is a problematic Jar. Please take a look. Just create an Android binding library project, add the attached jar, and build.
libxamarinbug.jar.zip

Generator should bind types with unresolvable interfaces

Context: Issue #358

If we have a //class which implements unresolvable //interface types, can we still bind that class?

For example, consider android.support.v7.widget.RecyclerView, which implements the interface android.support.v4.view.ScrollingView, which is either a package-private type or otherwise can't be resolved (different .jar file?).

Why can't generator bind this type?

Why shouldn't generator bind this type?

We should determine if this is actually a supportable/bindable scenario, and then improve generator so that such types are actually bound.

XA Binding xpath skips evaluation / transform

Having a class with methods with specific return type. Currently the generator skips this specific xpath:

<attr path="/api/package[@name='okio']/class[@name='Buffer']/method[@return='okio.BufferedSink']" name="managedReturn">Okio.IBufferedSink</attr>

(not even showing a warning that there is no match, so there are matches, I guess, since the transform is fine)

Expected:
All methods return Okio.IBufferedSink type.

What's actually happening:
The methods managed return type still the original. (Okio.Buffer)

It would be nice if I could do this "transform" since manually reference every piece of method in transform is really annoying.

(sample from com.squiareup.okio-okio)

Mono GC Bridge Support

We need a "proper" GC that uses Mono's GC bridge functionality instead of the terrible implementation currently used.

AppDomain Support

AppDomains aren't well supported.

"In theory" they may work: the JniRuntime.CurrentRuntime property will invoke JNI_GetCreatedJavaVMs() if there are no pre-existing JniRuntime instances, then create a new JniRuntime value around any any returned JavaVM* values. (This "just" requires that JNI_GetCreatedJavaVMs() be available, which may not be the case.)

In practice, there's a fundamental question: What runtime type for JniRuntime should be returned? For example, in a Xamarin.Android app, Android.Runtime.AndroidRuntime should (presumably) be used by default. At present, that won't happen.

How can/should we specify the default JniRuntime type to use when there is no previously set JniRuntime.CurrentRuntime value?

Additionally, what do we do about Java.Interop.JavaObject subclasses created in another AppDomain? We could plausibly make JavaObject a MarshalByRefObject, but what are the performance implications of this (assuming there are any)? We don't want to significantly impact method call invocation times. Perhaps we need to make JavaObject [Serializable] so that it can be marshaled by value?

Building in Release mode

Hi,

I tried building Java.Interop in release mode, but is something in java-interop-gc-bridge-mono.c that could never work:

https://github.com/xamarin/java.interop/blob/117b906deb916e5deaeb31ed739fb7452c7f940b/src/java-interop/java-interop-gc-bridge-mono.c#L871

`static mono_bool
add_reference (JavaInteropGCBridge *bridge, JNIEnv *env, MonoObject *obj, MonoJavaGCBridgeInfo *bridge_info, MonoObject *reffed_obj)
{

if DEBUG

MonoClass *klass    = mono_object_get_class (obj);

endif

void *handle;
mono_field_get_value (obj, bridge_info->handle, &handle);

jmethodID   add_method_id   = get_add_reference_method (bridge, env, handle, klass);`

The variable "klass" only exists in Debug mode, but it is accessed later.

When you build Xamarin.Android in release mode, don't you build java.interop in release mode too?

`javac -parameters` support

Java 8's javac now supports storing method parameter name information within .class files, using the javac -parameters option. Consider the following Java code:

class f {
	public void m (String a, int b, int[] c) {
	}
}

If we compile with:

javac -parameters f.java

We can see that there's a new MethodParameters metadata blob:

$ mono class-parse.exe f.class --dump
...
        10: Utf8("MethodParameters")
...
        1: m (Ljava/lang/String;I[I)V Public
                Code(1, Unknown[LineNumberTable](6))
                Unknown[MethodParameters](13)

class-parse needs support for the new MethodParameters Attribute blob:

MethodParameters_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u1 parameters_count;
    {   u2 name_index;
        u2 access_flags;
    } parameters[parameters_count];
}

XA Binding ambiguous reference

Having interfaces (generated) like this

public interface IInterface3 : { void Close(); }
public interface IInterface2 : { void Close(); }
public interface IInterface1 : IInterface2, IInterface3 {}

The IInterface1 's invoker generated code with ambiguous reference:

static void n_Close (IntPtr jnienv, IntPtr native__this)
{
	global::IInterface1 __this = global::Java.Lang.Object.GetObject<global::IInterface1> (jnienv, native__this, JniHandleOwnership.DoNotTransfer);
	__this.Close (); // problem is here, it should be IInterface2 or IInterface3
}

There is two scenarios in this situation:

  1. If both interface comes from MonoAndroid, you'll have no solution for this and have implement everything from scratch, manually.
  2. One of the interfaces is generated, so you can just remove the specific member from the interface. (which is clearly a bad solution even if the interface implementation is private)

(ps renaming the Close would not help, since the generator will create 2 same named pointer to the method:

IntPtr id_close; // this is gonna be duplicated anyway
public unsafe void Close ()
{
	if (id_close == IntPtr.Zero)
		id_close = JNIEnv.GetMethodID (class_ref, "close", "()V");
	JNIEnv.CallVoidMethod (((global::Java.Lang.Object) this).Handle, id_close);
}

Run Gendarme on codebase

Let's make sure we don't have anything stupid/overlooked.

Probable issue that will be triggered: types which have Handle/etc. properties should inherit from MarshalByRefObject to ensure that they're not "copied" across AppDomains, which could result in a "double delete".

JNI Marshal Method Generation, Registration

"Big picture" method registration picture

  • Remove marshal methods from binding assemblies
  • Support three method registration approaches:
    • Fully dynamic, via System.Linq.Expresssions/Java.Interop.Export
    • Using the above jnimarshalmethod-gen.exe-generated marshaling assembly; see above.
      • This would be for non-AOT "Release" registration
    • Using generated C code.
      • Possibly most performant; for each method to register, we'd emit a Java_... method as per Java/native convention.
      • Allows using the default Java linker to resolve method symbols!
      • Generated C code would use mono_method_invoke()/etc. to invoke the jnimarshalmethod-gen.exe-generated marshal methods.

Constructor with ExportAttribute loses managed parameters

Hi!

When a class constructor is decorated with [Export] in order to provide super arguments string, TypeManager.Activate() call loses constructor parameters types info and wrong (parameterless) constructor is being called instead.

MyView.cs:

public class MyView : Android.Views.View {
       public ItemFieldView(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
        {
        }

        [Export(SuperArgumentsString = "p0, null, proj.droid.proj.droid.R.attr.itemFieldStyle")]
        public MyView(Context context)
            : base(context, null, Resource.Attribute.myViewStyle)
        {
        }

        [Export(SuperArgumentsString = "p0, p1, proj.droid.proj.droid.R.attr.itemFieldStyle")]
        public MyView(Context context, IAttributeSet attrs)
            : base(context, attrs, Resource.Attribute.myViewStyle)
        {
        }

        public MyView(Context context, IAttributeSet attrs, int defStyleAttr)
            : base(context, attrs, defStyleAttr)
        {
        }

        public MyView(Context context, IAttributeSet attrs, int defStyleAttr, int defStyleRes)
            : base(context, attrs, defStyleAttr, defStyleRes)
        {
        }
}

The reason is that managedParameters are just not being passed to a new Signature at https://github.com/xamarin/java.interop/blob/master/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGenerator.cs#L251

XA Binding mutiple generic type issue

the generator strips / skips / escaping(?) comma, basically skips the second generic type

Having a java abstract method:
public abstract Map<List</*@Nullable*/ TagValue>, AggregationData> getAggregationMap();

What's generated:

public override unsafe global::System.Collections.Generic.IDictionary<global::System.Collections.Generic.IList<global::IO.Opencensus.Tags.TagValue>> AggregationMap {
			// Metadata.xml XPath method reference: path="/api/package[@name='io.opencensus.stats']/class[@name='ViewData']/method[@name='getAggregationMap' and count(parameter)=0]"
			[Register ("getAggregationMap", "()Ljava/util/Map;", "GetGetAggregationMapHandler")]
			get {
				const string __id = "getAggregationMap.()Ljava/util/Map;";
				try {
					var __rm = _members.InstanceMethods.InvokeAbstractObjectMethod (__id, this, null);
					return global::Android.Runtime.JavaDictionary<global::System.Collections.Generic.IList<global::IO.Opencensus.Tags.TagValue>>.FromJniHandle (__rm.Handle, JniHandleOwnership.TransferLocalRef);
				} finally {
				}
			}
		}

What expected:

public override unsafe global::System.Collections.Generic.IDictionary<global::System.Collections.Generic.IList<global::IO.Opencensus.Tags.TagValue>, AggregationData> AggregationMap {
			// Metadata.xml XPath method reference: path="/api/package[@name='io.opencensus.stats']/class[@name='ViewData']/method[@name='getAggregationMap' and count(parameter)=0]"
			[Register ("getAggregationMap", "()Ljava/util/Map;", "GetGetAggregationMapHandler")]
			get {
				const string __id = "getAggregationMap.()Ljava/util/Map;";
				try {
					var __rm = _members.InstanceMethods.InvokeAbstractObjectMethod (__id, this, null);
					return global::Android.Runtime.JavaDictionary<global::System.Collections.Generic.IList<global::IO.Opencensus.Tags.TagValue>, AggregationData>.FromJniHandle (__rm.Handle, JniHandleOwnership.TransferLocalRef);
				} finally {
				}
			}
		}

using transform fix, not working:
<attr path="/api/package[@name='io.opencensus.stats']/class[@name='ViewData']/method[@name='getAggregationMap' and count(parameter)=0]" name="managedReturn">System.Collections.Generic.IDictionary&lt;System.Collections.Generic.IList&lt;IO.Opencensus.Tags.TagValue&gt;, AggregationData&gt;</attr>

repro:
xabindingissue6.zip

etc:
9.0.04/26a17a147

class-parse output is missing types

Take the newest version of the recyclerview-v7 support library (Download https://dl.google.com/dl/android/maven2/com/android/support/recyclerview-v7/28.0.0-beta01/recyclerview-v7-28.0.0-beta01.aar extract the .aar, and run the mono class-parse.exe classes.jar > out.xml).

Notice that the output api xml does not include: com.android.support.v7.widget.RecyclerView, as well as RecyclerView.ViewHolder, RecyclerView.Adapter, among other types.

This wasn't a problem with v27.1.1 of this library and I don't see a lot that has changed public API wise in v28.x.

Bindingsgenerator: Callback interface with variadic arguments generates incorrect code

We have a class like this in our project jar:

package com.xamarin;

public class MyPublicClass extends MyPackageClass {
    public interface Listener {
        void call(Object... args);
    }

    @Override
    public void bugOne() {
    }

    public void bugTwo(Listener listener) {
        listener.call("abc", "def", new Integer(5));
    }
}

The C# code generated for bugTwo method and callback interface is like this:

public partial class EventArgs : global::System.EventArgs {
			public EventArgs (params global:: Java.Lang.Object[] p0)
			{
				this.p0 = p0;
			}

			params global:: Java.Lang.Object[] p0;
			public params global:: Java.Lang.Object[] P0 {
				get { return p0; }
			}
		}

It converts variadic arguments to array of Object which is fine, but it mistakenly carries through the params declaration which is invalid for member variables and return types.

This bug reproduces with Visual Studio for Mac community edition 2017 and I also built from source the generator.exe and it still repros the problem. I'm attaching a jar which shows the bug. Just create a new visual studio android binding library project and add the attached jar, build, and observe the error.
libxamarinbug.jar.zip

We miss some of the registered methods in jnimarshalmethod-gen

For some types we miss some of the registered methods. That leads to problems during registration and later crashes, like here with CellAdapter type:

I/mono-stdout( 2532): Registering JNI marshal methods in Xamarin.Forms.Platform.Android.CellAdapter
W/art     ( 2532): Attempt to remove index outside index area (6 vs 7-7)
W/art     ( 2532): JNI WARNING: DeleteLocalRef(0x1bf00019) failed to find entry

Java side of things describes 10 methods, while jnimarshalmethod-gen finds and generates just 6.

Java side:

__md_methods = 
                        "n_onItemLongClick:(Landroid/widget/AdapterView;Landroid/view/View;IJ)Z:GetOnItemLongClick_Landroid_widget_AdapterView_Landroid_view_View_IJHandler:Android.Widget.AdapterView/IOnItemLongClickListenerInvoker, Mono.Android, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null\n" +
                        "n_onActionItemClicked:(Landroid/view/ActionMode;Landroid/view/MenuItem;)Z:GetOnActionItemClicked_Landroid_view_ActionMode_Landroid_view_MenuItem_Handler:Android.Views.ActionMode/ICallbackInvoker, Mono.Android, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null\n" +
                        "n_onCreateActionMode:(Landroid/view/ActionMode;Landroid/view/Menu;)Z:GetOnCreateActionMode_Landroid_view_ActionMode_Landroid_view_Menu_Handler:Android.Views.ActionMode/ICallbackInvoker, Mono.Android, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null\n" +
                        "n_onDestroyActionMode:(Landroid/view/ActionMode;)V:GetOnDestroyActionMode_Landroid_view_ActionMode_Handler:Android.Views.ActionMode/ICallbackInvoker, Mono.Android, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null\n" +
                        "n_onPrepareActionMode:(Landroid/view/ActionMode;Landroid/view/Menu;)Z:GetOnPrepareActionMode_Landroid_view_ActionMode_Landroid_view_Menu_Handler:Android.Views.ActionMode/ICallbackInvoker, Mono.Android, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null\n" +
                        "n_onItemClick:(Landroid/widget/AdapterView;Landroid/view/View;IJ)V:GetOnItemClick_Landroid_widget_AdapterView_Landroid_view_View_IJHandler:Android.Widget.AdapterView/IOnItemClickListenerInvoker, Mono.Android, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null\n" +
                        "n_onActionItemClicked:(Landroid/support/v7/view/ActionMode;Landroid/view/MenuItem;)Z:GetOnActionItemClicked_Landroid_support_v7_view_ActionMode_Landroid_view_MenuItem_Handler:Android.Support.V7.View.ActionMode/ICallbackInvoker, Xamarin.Android.Support.v7.AppCompat\n" +
                        "n_onCreateActionMode:(Landroid/support/v7/view/ActionMode;Landroid/view/Menu;)Z:GetOnCreateActionMode_Landroid_support_v7_view_ActionMode_Landroid_view_Menu_Handler:Android.Support.V7.View.ActionMode/ICallbackInvoker, Xamarin.Android.Support.v7.AppCompat\n" +
                        "n_onDestroyActionMode:(Landroid/support/v7/view/ActionMode;)V:GetOnDestroyActionMode_Landroid_support_v7_view_ActionMode_Handler:Android.Support.V7.View.ActionMode/ICallbackInvoker, Xamarin.Android.Support.v7.AppCompat\n" +
                        "n_onPrepareActionMode:(Landroid/support/v7/view/ActionMode;Landroid/view/Menu;)Z:GetOnPrepareActionMode_Landroid_support_v7_view_ActionMode_Landroid_view_Menu_Handler:Android.Support.V7.View.ActionMode/ICallbackInvoker, Xamarin.Android.Support.v7.AppCompat\n" +
                        "";

C# side:

[JniAddNativeMethodRegistration]
public static void __RegisterNativeMembers (JniNativeMethodRegistrationArguments args)
{
	Console.WriteLine ("Registering JNI marshal methods in Xamarin.Forms.Platform.Android.CellAdapter");
	args.AddRegistrations (new JniNativeMethodRegistration[6] {
		new JniNativeMethodRegistration ("n_onActionItemClicked", "(Landroid/view/ActionMode;Landroid/view/MenuItem;)Z", new Func<IntPtr, IntPtr, IntPtr, IntPtr, bool> (__<$>_jni_marshal_methods.n_onActionItemClicked_Landroid_view_ActionMode_Landroid_view_MenuItem_)),
		new JniNativeMethodRegistration ("n_onCreateActionMode", "(Landroid/view/ActionMode;Landroid/view/Menu;)Z", new Func<IntPtr, IntPtr, IntPtr, IntPtr, bool> (__<$>_jni_marshal_methods.n_onCreateActionMode_Landroid_view_ActionMode_Landroid_view_Menu_)),
		new JniNativeMethodRegistration ("n_onDestroyActionMode", "(Landroid/view/ActionMode;)V", new Action<IntPtr, IntPtr, IntPtr> (__<$>_jni_marshal_methods.n_onDestroyActionMode_Landroid_view_ActionMode_)),
		new JniNativeMethodRegistration ("n_onPrepareActionMode", "(Landroid/view/ActionMode;Landroid/view/Menu;)Z", new Func<IntPtr, IntPtr, IntPtr, IntPtr, bool> (__<$>_jni_marshal_methods.n_onPrepareActionMode_Landroid_view_ActionMode_Landroid_view_Menu_)),
		new JniNativeMethodRegistration ("n_onItemClick", "(Landroid/widget/AdapterView;Landroid/view/View;IJ)V", new Action<IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr> (__<$>_jni_marshal_methods.n_onItemClick_Landroid_widget_AdapterView_Landroid_view_View_IJ)),
		new JniNativeMethodRegistration ("n_onItemLongClick", "(Landroid/widget/AdapterView;Landroid/view/View;IJ)Z", new Func<IntPtr, IntPtr, IntPtr, IntPtr, float, float, bool> (__<$>_jni_marshal_methods.n_onItemLongClick_Landroid_widget_AdapterView_Landroid_view_View_IJ))
	});

The problem is located here https://github.com/xamarin/java.interop/blob/master/tools/jnimarshalmethod-gen/App.cs#L482-L495, where we expect named arguments.

JNI Global References by default

In part, revert commit 1caf077 (which provides the logic for JNI local references by default), and follow Xamarin.Android in making JNI Global References the default semantic.

As per Xummit discussions, a "local reference by default" worldview doesn't "work" for developers; it adds a useless complication with myriad knock-on effects.

For example, Android is limited to only 512 JNI local references at a time, which means you could only have 512 JavaObject subclass instances at a time unless JavaObject.RegisterWithVM() were invoked (to turn the JNI local reference into a global reference). Furthermore, since handles are now GC SafeHandle constructs, it may require a GC to actually collect them, prolonging lifetimes and making an Android crash more likely.

(We already had to move away from the "JNI Local References" semantic in commit 972c5bc by giving the instance a JNI global reference during construction, so that when invoking a constructor, if the Java constructor invoked any overridden virtual methods, the correct instance could be looked up to perform the method dispatch.)

Instead of JNI Local References by default, make them JNI Global References by default, and add:

partial JavaVM {
    public T UnregisterInstance<T>(T value)
        where T : IJavaObject, IJavaObjectEx;
}

(Additionally, remove RegisterWithVM()/etc. from JavaObject; let's try to keep that API as small as possible.)

Add a JniObjectReferenceOptions.DoNotRegister value, and if specified don't register the instance with the VM.

Adds thread-safety; allows creation of temporary instances w/o registration, removing a "window of mapping".

Related: Xamarin.Android Bug #25995

Rename `IJavaObject` to `IJavaPeerable`

Xamarin.Android already has IJavaObject. Reducing gratuitous type collisions would be handy.

Alternate name choices:

  • IJniPeerable
  • IJavaPeerable
  • IJavaBridgeable

I'm currently preferring "Jni" over "Java" nomenclature, though based on the presence of JavaObject and JavaException -- which I don't think should be renamed -- preferring "Java" in the naming may be better.

Improve java-interop project build on Windows

I'm having a problem where the project java-interop isn't outputting any dll. Without this project being built, nothing can be done.

I'm using VS Community 2017 and I've tried building with make but it outputs a lot of errors.

XA Binding namespace conflict

Having a generated code:

// Metadata.xml XPath field reference: path="/api/package[@name='com.google.android.gms.vision.barcode']/class[@name='Barcode']/field[@name='cornerPoints']"
[Register ("cornerPoints")]
public IList<Android.Graphics.Point> CornerPoints {
	get {
		const string __id = "cornerPoints.[Landroid/graphics/Point;";

		var __v = _members.InstanceFields.GetObjectValue (__id, this);
		return global::Android.Runtime.JavaArray<global::Android.Graphics.Point>.FromJniHandle (__v.Handle, JniHandleOwnership.TransferLocalRef);
			}
	set {
		const string __id = "cornerPoints.[Landroid/graphics/Point;";

		IntPtr native_value = global::Android.Runtime.JavaArray<global::Android.Graphics.Point>.ToLocalJniHandle (value);
		try {
			_members.InstanceFields.SetValue (__id, this, new JniObjectReference (native_value));
		} finally {
			global::Android.Runtime.JNIEnv.DeleteLocalRef (native_value);
		}
	}
}

What I expected as return type:
IList<global::Android.Graphics.Point>
What's actually generated:
IList<Android.Graphics.Point>

The global prefix disappers at the property's type while the get method body still using it correctly 🗡

I'm using the latest stable xamarin-android-builds-d15-8 9.0.04/26a17a147

ps:
I gave a try with:
<attr path="/api/package[@name='com.google.android.gms.vision.barcode']/class[@name='Barcode']/field[@name='cornerPoints']" name="managedReturn">global::Android.Graphics.Point</attr> <attr path="/api/package[@name='com.google.android.gms.vision.barcode']/class[@name='Barcode']/field[@name='cornerPoints']" name="return">global::Android.Graphics.Point</attr>
(I knew it won't work but.. supporting such scenarios with transforms/xpath!?)
So I ended up just removing and re-adding with a partial class with fixed return type.

Add a JniEnvironment.BeginGetValueScope() method.

Building upon Issue #3, we expect JavaVM.GetValue() calls to be "buried" within binding code, not directly invoked by developers (most of the time). Thus, how does a developer tell a binding method that a new value should be created instead of retrieving a possibly shared value?

Introduce JniEnvironment.BeginGetValueScope(GetValueScope):

[Flags]
public enum GetValueBehaviors {
    Default                = 0,
    CreateValues           = 1,
    DoNotMarshalExceptions = 2,
}

partial class JniEnvironment {
    public static IDisposable BeginGetValueBehaviors (GetValueBehaviors scope);
}

Calling JniEnviornment.BeginGetValueBehaviors() would alter the behavior of JavaVM.GetValue(): if GetValueBehaviors.CreateValues is specified, then JavaVM.GetValue() will instead behave like JavaVM.CreateValue(). This allows the end user to maintain some degree of control:

using (var scope = JniEnvironment.BeginGetValueBehaviors (GetValueBehaviors.CreateValues))
using (var typeface = Typeface.Create (...)) {
    // use typeface
}

The above allows disposing of the temporary with impunity, as BeginGetValueScope() will ensure that Typeface.Create() returns unique wrappers instead of possibly shared wrappers.

Make `IJavaPeerable` implementable

https://twitter.com/sh4na/status/600662831017676800

If a user of your API complains of breakage and you reply “oh, you shouldn’t be doing that”, it’s a sign that your API is badly designed.

What's the problem with IJavaPeerable? It shouldn't be implemented by developers. Ever. (In theory it could be properly implemented. In practice, I wouldn't want to try to do so.)

We should nuke the IJavaPeerable interface from the public API.

Problem: JniPeerMethods and other types make use of IJavaObject, which would need to be fixed. Perhaps we could refactor things to remove the problematic members, e.g. IJavaPeerable.PeerReference?

Internal methods

Hello, add please ability to extent or replace default codegenerator from box(i can fork, i know)
Thanks.

JniTypeSignature uses CultureInfo

(This might be an effectively meaningless optimization; presumably someone is going to initialize CultureInfo during process startup. It just so happens that in a profiler run, JniTypeSignature was the first one, hence this bug...)

The JniTypeSignature(string) constructor uses string.Contains(): https://github.com/xamarin/java.interop/blob/8ad8e1205a29b22f4b353fa3f760e5f9959712a7/src/Java.Interop/Java.Interop/JniTypeSignature.cs#L42

On Mono (and .NET?), string.Contains() delegates to CultureInfo, which requires loading and initializing the entire CultureInfo infrastructure (if it hasn't already been loaded & initialized).

As this is hitting Xamarin.Android process startup, and should be straightforward to avoid, we should fix JniTypeSignature to avoid Culture-aware string comparisons, especially as we don't need culture-aware string comparisons.

(Note: using StringComparison.Ordinal* still hits CultureInfo; it just uses CultureInfo.InvariantCulture.)

Permit hash conflicts in instance mapping

We can't use a Dictionary<int, IJavaObject> mapping between JNI handles wherein the dictionary key is the result of System.identityHashCode(), as it's possible for multiple Java objects to share the same System.identityHashCode() value. (Rare...but possible, and will only get more possible.)

We need to move to a system that uses JNIEnv::IsSameObject() to ensure we lookup the correct instance for a given JNI handle.

Support binding types with unresolved interfaces

Context:
#358 (comment)

Imagine the following:

public interface Fooable {
}
public class Foo implements Fooable {
}

Further imagine that Foo and Fooable are in separate foo.jar and fooable.jar files, and we attempt to bind only foo.jar.

Should this work?

Offhand, I can't think of any reason why it shouldn't be supportable.

Does this work? No. As a specific example, consider recyclerview-v7-28.0.0-beta01.aar: android.support.v7.widget.RecyclerView is a non-abstract type which implements the interface android.support.v4.view.ScrollingView, which cannot be resolved (as it's in a different .jar file). The RecyclerView type is not bound.

Usage and JNI array questions

Hi!

Is this code included in recent Xamarin.Android releases (currently running Xamarin.Android 8.1 and VS 15.5.4)?
I'd assume so, since I can reference types such as JavaTypeParameters which seem to be absent from Xamarin.Android repo and the official Xamarin doc.

Anyways.. I'm stuck on a JNI marshalling issue regarding arrays and thought you could maybe help.

My C lib (which I cannot edit) is making this call:
GET_METHOD(method, "method", "([F)Z", true);
My C# so far looks like this:

[Export]
void method(float[] parameter)
{
}

But it crashes with

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

I have read your helpful post at https://forums.xamarin.com/discussion/1930/creating-arrays-of-custom-java-objects-in-jni and tried several things but can't seem to make it work.

Any idea what pieces I'm missing here?

Thank you for your time.

Value Marshaling with Expressions

Fix marshaling

  • I don't like JniMarshalInfo; it's too convoluted, makes the simple cases hard.

  • Do in conjunction w/ Java.Interop.Dynamic

  • Need to permit use of custom attributes on return types, parameter types, "inline" into marshal methods.

  • Need to use System.Linq.Expressions/etc. to "merge"/do the work of monodroid/tools/generator, so that Java.Interop.Export can support value types, etc.

  • Use case: Android.Graphics.Color, which is marshaled from JNI as an int but to managed as an Android.Graphics.Color struct, and vice-versa.

  • Should probably split JniMarshalInfo into separate "to managed" and "from managed" types?

    • No; needless added complexity, and when it comes to System.Linq.Expressions many of the type information will be shared between them.
  • "New" marshaling support should try to leverage Expression<TDelegate> to make it easier to write marshalers:

    public class JnIValueMarshaler<TJni, TManaged> : JniValueMarshaler {
        public sealed override Expression<Func<JniValueMarshalerContext, TJni, TManaged>> JniTypeToManagedType (JniValueMarshalerContext context,  Expression jniSourceValue,     Expression manageDestinationdValue);
        public abstract Expression<Func<JniValueMarshaler, TJni, TManaged> JniTypeToManagedType ();
    }
    class Int32Marshaler : JniValueMarshaler<int, int> {
        public override Expression<Func<JniValueMarshalerContext, TJni, TManaged>> JniTypeToManagedType (JniValueMarshalerContext context,  Expression jniSourceValue,     Expression manageDestinationdValue)
        {
            return (c, jni) => jni;
        }
    }
    
  • With such a system in place, we'll need to be able to "rebind" the Expression<Func<...>> parameters with the generated parameters

    http://stackoverflow.com/questions/8610506/binding-parameter-in-expression-trees

    class MyVisitor : ExpressionVisitor {
        ParameterExpression[] old, cur;
        public MyVisitor(ParameterExpression[] old, ParameterExpression[] cur)
        {
            this.old = old;
            this.cur = cur;
        }
        protected override Expression VisitParameter(ParameterExpression node)
        {
            for (int i = 0; i < old.Length; ++i) {
                if (old [i].Name == node.Name && old [i].Type == node.Type)
                    return cur [i];
                }
            return node;
        }
    }
    

    With MyVisitor, we can then convert the result of Int32Marshaler.JniTypeToManagedType():

    var p = Expression.Parameter (typeof (int), "p");
    var c = Expression.Parameter (typeof (JniValueMarshalerContext), "c");
    var e = new Int32Marshaler ().JniValueMarshalerContext ();
    var v = new MyVisitor (e.Parameters, new[]{c, p});
    var e = Expression.Lambda<Func<JniValueMarshalerContext, int, int>>(v.Visit (e.Body), new[]{c, p});
    

Probably related: JniMarshalInfoAttribute, a custom attribute to control marshaling behavior?

We'll also need similar custom attributes to control parameter and return type marshaling, via the above Expression architecture.

class-parse fails when windows culture is French

While generating the binding for ExoPlayer, i stumbled upon this annoying bug.
A workaround is to change in Windows the default digit separator from "," (europe) to "." (usa)

Wrong generated code:

	[Register ("DIMEN_UNSET")]
	public const float DimenUnset = (float) 1,401298E-45;

Should be instead:

	[Register ("DIMEN_UNSET")]
	public const float DimenUnset = (float) 1.401298E-45;

This means the float value is formatted using the default culture instead of the invariant culture.

Remove `JCW_ONLY_TYPE_NAMES`

Many of the types within src/Xamarin.Android.NamingCustomAttributes use JCW_ONLY_TYPE_NAMES to control member visibility.

This is the wrong way to control member visibility.

Instead, all the Xamarin.Android.NamingCustomAttributes types should be partial types, not public by default, and contain only naming-related functionality.

Android-specific functionality not related to type names -- i.e. not used in this repo -- should be moved into the xamarin-android repo.

Type coercion and JavaCast<T> support

Java.Interop.dll assembly “needs” a .JavaCast extension method, but the question is how to “sanely” provide one when Mono.Android.dll provides one w/o causing compilation errors

Rename JavaVM.GetObject() to JavaVM.GetValue().

Rename JavaVM.GetObject() to JavaVM.GetValue().

Rationale: I want to remove the IJavaObject constraint so that we can do e.g. vm.GetValue<int>(javaLangIntegerHandle) to read a java.lang.Integer into an int (ditto arrays, etc.), and "Object" no longer makes sense when a copy of the value will be created.

Java 8 Interface Binding

Android N uses new Java 8 language features such as interface default methods and interface static methods, which are features that tools/generator hasn't had to deal with before.

The question: how should they be dealt with?

    // Java
    public interface HelloJava8 {
        public static final int VALUE = 42;
        public void a ();
        public default void defaultMethod() {
        }
        public static void staticMethod() {
        }
    }

There are at least three ways to do so:

  1. Ignore them.
  2. Expose them.
  3. Treat them specially.

Ignore them

Interface default methods don't need to be implemented, and static methods aren't implementable. The binding mechanism could thus simply ignore them entirely.

    // C#
    public interface IHelloJava8 {
        public void A ();
    }
    public static class HelloJava8 {
        public const int Value = 42;
    }

This isn't entirely desirable, because it also means default interface methods can't be called.

    IHelloJava8 value = ...
    value.DefaultMethod(); // error
    // No way to invoke HelloJava8.staticMethod()

On the plus side, implementors don't need to worry about them:

    public class WootJava8 : Java.Lang.Object, IHelloJava8 {
        public void A () {}
    }

Expose them

The "simple" version of "expose them" is do the simple thing: static methods are bound in the constant-containing static class, and default methods are bound as normal methods:

    // C#
    public interface IHelloJava8 {
        public void A ();
        public void DefaultMethod ();
    }
    public static partial class HelloJava8 {
        public const int Value = 42;
        public void StaticMethod ()
        {...}
    }

This allows calling both methods:

    IHelloJava8 value = ...
    value.DefaultMethod(); // works
    HelloJava8.StaticMethod(); // works

However, this means that C# subclasses need to implement all the default methods:

    public class WootJava8 : Java.Lang.Object, IHelloJava8 {
        public void A () {}
        public void DefaultMethod () {...}
    }

This might not seem bad in this example, but java.util.Collection contains five default methods (one inherited from Iterable).

This might be fine and acceptable, and we could help this scenario by providing access to the default method implementation:

    public static partial class HelloJava8 {
        public void InvokeDefaultMethod(IHelloJava8 self)
        {
            /* Non-virtual invocation of HelloJava8.defaultMethod() */
        }
    }

    public partial class WootJava8 {
        public void DefaultMethod ()
        {
            HelloJava8.InvokeDefaultMethod (this);
        }
    }

However, such "help" will require documentation and training to know about, complicating use.

Treat them specially

To a large extent, the concern is around C# implementations of the interface. It is seen that requiring that optional methods be implemented will complicate the C# experience. Perhaps we can fix this by splitting out the optional methods?

Then, we can use extension methods to invoke the default methods:

    // C#
    public interface IHelloJava8 {
        public void A ();
    }
    public static partial class HelloJava8 {
        public const int Value = 42;
        public void StaticMethod ()
        {...}

        public void DefaultMethod (this IHelloJava8 self)
        {
            // *Virtual* dispatch to invoke self.defaultMethod()
        }

        public interface IDefaultMethods {
            public void DefaultMethod ();
        }
    }

This allows calling both methods:

    IHelloJava8 value = ...
    value.DefaultMethod(); // works via extension method
    HelloJava8.StaticMethod(); // works

It also means that C# types don't need to implement the default methods:

    public partial class WootJava8 : Java.Lang.Object, IHelloJava8 {
        public void A () {}
    }

...but if the C# type does want to implement them, they can all be implemented by using the IDefaultMethods interface:

    public partial class WootJava8 : HelloJava8.IDefaultMethods {
        public void DefaultMethod () {}
    }

Creating "custom" Java classes from new threads.

Xamarin.Android uses ClassLoader.loadClass() to get jclass values instead of the normal JNIEnv::FindClass() because, on Android, "new" threads get a default class loader that only finds Java classes present in android.jar and not the app.apk.

We need to verify this behavior and fixup things accordingly.

This may mean that instead of using JNI convention types everywhere (e.g. java/lang/Object) that we instead standardize on Java names (java.lang.Object), though I'm not sure what this means for nested types.

Support for the native-world.

So I was initializing Mono in a Java program through JNI, yet the Mono runtime couldn't sense the Java runtime at all! java.interop didn't work in this case and I was forced to use the internal implementation callbacks strategy which is pretty verbose and hard-work for me, since I have to port a lot of methods, marshalling, conversion, etc, it was quite messy, but it works anyway.
But I can provide the JVM pointers from native world...I think it could be very cool to let user running java.interop from native world.

Add a JavaVM.CreateValue() method.

Context: Xamarin.Android Bug 25443

The problem is that it is often desirable to dispose of handles ASAP because you "know" that the instances won't be used anymore:

using (var typeface = Typeface.Create (...)) {
    // use typeface
}

The problem is that the above code is making an assumption that Typeface.Create() will be returning a new, unique, instance. If it doesn't (as is actually the case), then the returned wrapper could be shared with other threads, and if the handle is Dispose()d while another thread is using it...Bad Things™ happen.

Addressing this involves two parts; see Issue #4 for the second half.

The first half involves adding another "primitive" operation: in addition to JavaVM.GetValue() (Issue #2), we should have JavaVM.CreateValue() methods which always create a new, possibly aliasing, wrapper instance (if a wrapper would be used, e.g. vm.CreateValue<int>(...) clearly doesn't involve a wrapper...).

We would thus have two ways to turn a JNI handle into a value:

  • JavaVM.GetValue() to maintain object identity semantics, maintaining a single wrapper instance per encountered Java instance.
  • JavaVM.CreateValue(), which always creates a new wrapper (when required), allowing the caller to always Dispose() of it.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.