Skip to content

Desktop JDK-11 Java.Base Binding #11891

Closed
Closed

Description

@jonpryor

Begin binding Desktop JVM libraries.

We should try to see if we can use a 1 "module" per assembly binding strategy.

We should look into updating class-parse to natively support the jmod file format. Lacking that, we can get class-parse to read all types in a Java module via e.g.

cd path/to/jdk-11
mkdir x
./bin/jmod extract jmods/java.base.jmod --dir x
mono path/to/class-parse.exe @classes.txt > api.xml

For me, with JDK 11, class-parse is able to find 5609 classes and 553 interfaces.

class-parse also emits 305 warnings:

class-parse: Unable to read file 'x/classes/module-info.class': Unknown constant type 0x00000013.

class-parse needs to actually support Java modules.

Plus:

class-parse: method com/sun/java/util/jar/pack/Attribute$Layout.compareTo(Ljava/lang/Object;)I: Local variable type descriptor mismatch! Got 'Ljava/lang/Object;'; expected 'Lcom/sun/java/util/jar/pack/Attribute$Layout;'.
…
class-parse: method java/util/concurrent/ConcurrentHashMap$EntrySetView.add(Ljava/lang/Object;)Z: Local variable type descriptor mismatch! Got 'Ljava/lang/Object;'; expected 'Ljava/util/concurrent/ConcurrentHashMap$EntrySetView;'
…

Regardless, we can emit an api.xml for the java.base.jmod module. However, to make a binding, we need generator support:

mono path/to/generator.exe -o bindings --codegen-target JavaInterop1 api.xml

This emits 35MB of C# bindings (yay!)

The problem is that the bindings are Xamarin.Android-style, and not what I had wanted for desktop use. For example, Java.Lang.Object:

namespace Java.Lang {

	// Metadata.xml XPath class reference: path="/api/package[@name='java.lang']/class[@name='Object']"
	[global::Android.Runtime.Register ("java/lang/Object", DoNotGenerateAcw=true)]
	public partial class Object {
		static readonly JniPeerMembers _members = new JniPeerMembers ("java/lang/Object", typeof (Object));

		// Metadata.xml XPath constructor reference: path="/api/package[@name='java.lang']/class[@name='Object']/constructor[@name='Object' and count(parameter)=0]"
		[Register (".ctor", "()V", "")]
		public unsafe Object () : this (IntPtr.Zero, JniHandleOwnership.DoNotTransfer)

This calls the Xamarin.Android-style (IntPtr, JniHandleOwnership) constructor. Java.Interop doesn't have an Android.Runtime.JniHandleOwnership enum; that's (currently) specific to Xamarin.Android. Instead, the desired Java.Interop approach is to instead have/use a (ref JniObjectReference, JniObjectReferenceOptions) constructor, as used by TestType:

https://github.com/xamarin/java.interop/blob/855ecfa3a85ce74f40a1cc47e67b70aedd697dc3/tests/Java.Interop-Tests/Java.Interop/TestType.cs#L47-L51

Next:

		public unsafe Java.Lang.Class Class {
			// Metadata.xml XPath method reference: path="/api/package[@name='java.lang']/class[@name='Object']/method[@name='getClass' and count(parameter)=0]"
			[Register ("getClass", "()Ljava/lang/Class;", "")]
			get {
				const string __id = "getClass.()Ljava/lang/Class;";
				try {
					var __rm = _members.InstanceMethods.InvokeNonvirtualObjectMethod (__id, this, null);
					return global::Java.Lang.Object.GetObject<Java.Lang.Class> (__rm.Handle, JniHandleOwnership.TransferLocalRef);
				} finally {
				}
			}
		}

Java.Interop doesn't currently have a Java.Lang.Object type, though that would be created/implicit to binding java.base.jmod. Instead, I would prefer investigating: https://github.com/xamarin/java.interop/blob/main/Documentation/Architecture.md#proposed-javainterop-architecture; that is, instead of Object.GetObject<T>(), use JniEnvironment.Runtime.ValueManager.GetValue<T>().

Then there's marshal methods:

		static Delegate cb_clone;
#pragma warning disable 0169
		static Delegate GetCloneHandler ()
		{
			if (cb_clone == null)
				cb_clone = JNINativeWrapper.CreateDelegate ((_JniMarshal_PP_L) n_Clone);
			return cb_clone;
		}

I would prefer that JNINativeWrapper not exist on Desktop.


Creating an api.xml is the easy part (but could certainly be made easier!). The hard part is figuring out what we want for a "nice" Desktop ABI, one that doesn't have "Android-Isms" everywhere. (For example, JniHandleOwnership is Android.Runtime.JniHandleOwnership; a Desktop JVM binding assembly shouldn't have any types in an Android.* namespace!)

Activity

  1. 9 remaining items

  2. self-assigned this
    on Jul 19, 2022
  3. changed the title [-]Desktop Java Bindings[/-] [+]Desktop JDK-11 Java.Base Binding[/+] on Jul 19, 2022
  4. added theissue type on Apr 29, 2025
  5. removed
    enhancementProposed change to current functionality.
    on Apr 29, 2025
  6. added
    java-interopIssues migrated from dotnet/java-interop / relates to the Java.Interop subtree
    needs-triageIssues that need to be assigned.
    on Jul 1, 2026
  7. dotnet-policy-service commented on Jul 1, 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.

  8. dotnet-policy-service commented on Jul 15, 2026

    @dotnet-policy-service

    This issue will now be closed since it had been marked "possibly-stale" but received no further activity in the past 14 days. It is still possible to reopen or comment on the issue, but please note that the issue will be locked if it remains inactive for another 30 days.

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

Metadata

Metadata

Assignees

Labels

java-interopIssues migrated from dotnet/java-interop / relates to the Java.Interop subtreeneeds-triageIssues that need to be assigned.possibly-staleIssues that are potentially no longer relevant.

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions