mozilla / cbindgen

A project for generating C bindings from Rust code
Mozilla Public License 2.0
2.37k stars 306 forks source link

Add java support (using JNA) #857

Open fredszaq opened 1 year ago

fredszaq commented 1 year ago

This builds on #853 and adds a new language backend capable of generating java bindings.

The generated bindings use JNA (I used the last version 5.13.0 while developing but this will probably work with earlier ones as well)

Most of the new code lives in bindgen/language_backend/java_jna.rs and is pretty straightforward generation as most on the complicated things were done in the previous refactoring.

The test suite has been updated to generate and compile the bindings for java when running. On nice thing gained with this is the possibility to put a configuration file specific to a language alongside the standard one and the test will take this one instead of the default one if it exists (quite a few test use the header config to add some more code inside the generated bindings, C or preprocessor code doesn't work well in a java source file :P)

All tests are green, but that doesn't mean all the generated bindings are correct, some cases may fail at runtime as I didn't test all cases in a real program. I did however generate and use the bindings on a non trivial lib I'm developing and they worked as expected.

fredszaq commented 1 year ago

rebased with the changes from #853

coolbluewater commented 6 months ago

@fredszaq very cool! Currently I hand-author a Kotlin bridge class, e.g.:

package com.foo.bar
import android.view.Surface
class RustBridge {
    init { System.loadLibrary("mylibrary") }
    external fun enterFrame(rustObj: Long)
}

and use the following Rust code with the 'jni_fn' attribute to ensure the name is correct:

#[no_mangle]
#[jni_fn("com.foo.bar.RustBridge")]
pub fn enterFrame(_env: *mut JNIEnv, _: JClass, obj: jlong) {
    let obj = unsafe { &mut *(obj as *mut WgpuCanvas) };
    obj.enter_frame();
}

Will your backend be able to generate the kotlin bindings for this type? Also, will it handle the rust name conversion without needing the jni_fn crate? Thanks!

fredszaq commented 6 months ago

Hi @coolbluewater ! The generated bindings use JNA. JNA works a bit differently than JNI (which your example uses) even though JNA uses JNI under the hood.

When using JNI, the native code needs to be aware that it is called by a JVM. This is done for rust via the #[jni_fn()] proc macro.

When using JNA, the native code doesn't need to be aware it is called from a JVM, as JNA is able to work with plain C compatible symbols. This means you don't have to have specific symbols for your lib for it to be able to be loaded from the jvm.

This means the rust code would probably look something like that

#[no_mangle]
pub fn enterFrame(obj: *mut WgpuCanvas) {
   unsafe{ obj.enterFrame}
}

and the koltin code would look like that

class RustBridge {
    fun enterFrame(rustObj: MyLib.WgpuCanvasByReference) {
         MyLib.INSTANCE.entrerFrame(rustObj)
    }
}

With the code for the class MyLib beeing generated.

You can configure the name and the package of the generated class (actually the public api is an interface) in cbingen's config

[java_jna]
package = "my.package"
interface_name = "MyLib"
coolbluewater commented 6 months ago

@fredszaq thanks for your response! I need the JNIEnv and also need to pass jobjects from Kotlin to Rust. Is this possible in some way?

fredszaq commented 6 months ago

Not sure you want to mix JNI and JNA as the approaches are different and I'm not sure you'll be able to get access to the JNIEnv via JNA.

Writing a java-jni language backend that expects the rust code to be JNI aware could be a solution if you really need to access the JNIEnv/jobjects from the rust code. The the java-jna language backend in this PR kinda expects you are using plain old C compatible symbols.

fredszaq commented 5 months ago

@emilio I rebased this with the latest master, following the merge of #942

ZhaoXiangXML commented 3 months ago

I'm looking forward for this one to be merged. I'm working on a C# backend that has some common ground with Java.

fredszaq commented 1 month ago

Hi @emilio, I just rebased this on the latest master as there were some conflicts. It would be great if you have time to review again (I know this is a big piece). Regading length of various java types, a good documentation can be found here https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html We've been using bindings generated by this for a while at work and didn't have any problems with them

Larkooo commented 2 weeks ago

It would be great if we could have typed enums support. We can use unions from the JNA API for that; https://java-native-access.github.io/jna/4.2.1/com/sun/jna/Union.html.

Larkooo commented 2 weeks ago

This is very rough: https://github.com/Larkooo/cbindgen/tree/java-jna-backend but this should handle typed enums with unions and decorates them with a tag which is the original generated enum