#java #library #shared-libraries #dynamic-library #java-class

javawithrust

Transfer function calls between Rust and Java in a rust-like (ish) way

2 unstable releases

0.2.0 Oct 17, 2022
0.1.0 Oct 16, 2022

#167 in FFI

36 downloads per month

MIT license

12KB

JavaWithRust-Lib

Transfer function calls between Rust and Java in a rust-like (ish) way.

Features

  • Call static java functions from rust.
  • Call associated rust functions from java.
  • Convert java objects to rust structs.

Limitations

  • Can not call java methods from rust.
  • Can not call rust methods from java.

Setup

Create a dynamic library crate.

# Bash

cargo init . --lib
# Cargo.toml

[dependencies]
javawithrust = "0.1"
serde        = {version = "1.0", features = ["derive"]}

[lib]
crate_type = ["cdylib"]

Usage

Get the needed stuff.

// Rust

use javawithrust::prelude::*;

Link a new Java class. This will link JavaObject (in Rust) and io.example.JavaClassName (in Java).

// Rust

#[jclass(io.example.JavaClassName)]
pub struct JavaObject;

Link the functions.

// Rust

#[jfuncs]
impl JavaObject {

}

Examples

// Rust

use javawithrust::prelude::*;

#[jclass(io.example.CustomJavaClass)]
pub struct CustomRustStruct;

#[jfuncs]
impl CustomRustStruct {

    fn hello() { // Will return `Result<(), String>`
        println!("hello");
        CustomRustStruct::world()?;
        return Ok(());
    }

    // Will call `io.example.CustomJavaClass.world()`.
    fn world();

    // Callable from java.
    fn sum(a : i32, b : i32) -> i32 { // Will return `Result<i32, String>`
        return Ok(a + b);
    }

}
// Java

package io.example;

import org.astonbitecode.j4rs.api.Instance;
import org.astonbitecode.j4rs.api.dtos.InvocationArg;
import org.astonbitecode.j4rs.api.java2rust.Java2RustUtils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

public class ConvHelper {

    public static <T> Instance<T> j2rs(T from) {
        return Java2RustUtils.createInstance(from);
    }

    @SuppressWarnings("unchecked")
    public static <T> T rs2j(Instance<T> from) {
        if (from instanceof InvocationArg) {
            InvocationArg from_ia = ((InvocationArg)from);
            if (from_ia.isSerialized()) {
                ObjectMapper mapper = new ObjectMapper();
                try {
                    return mapper.<T>readValue(from_ia.getJson(), (Class<T>)(Class.forName(from_ia.getClassName())));
                } catch (JsonProcessingException | ClassNotFoundException e) {
                    e.printStackTrace();
                    System.exit(1);
                }
            }
        }
        return Java2RustUtils.getObjectCasted(from);
    }

}
// Java

package io.example;

import org.astonbitecode.j4rs.api.Instance;
import org.astonbitecode.j4rs.api.java2rust.Java2RustUtils;

public class CustomJavaClass
{
    // Load the dynamic library.
    // The library is loaded from the same directory as the jar.
    static {
        String os=System.getProperty("os.name").toLowerCase();String path=J2RS.class.getProtectionDomain().getCodeSource().getLocation().getPath();path=path.substring(0,path.lastIndexOf("/")).replaceAll("%20"," ")+"/"+
        "robot"; // Name of the dynamic library file. `.so` and `.dll` are added automatically.
        if (os.contains("linux") || os.contains("unix") || os.contains("android")) {
            path += ".so";
        } else if (os.contains("win")) {
            path += ".dll";
        }
        else {throw new UnsatisfiedLinkError("Can not load dynamic library in unknown operating system `" + os + "`");}
        System.load(path);
    }

    public static native void hello();

    public static void world() {
        System.out.println("world!");
    }

    private static native Instance<Integer> sum(Instance<Integer> i1, Instance<Integer> i2);
    public static Integer sumFromRust(Integer a, Integer b) {
        // Convert the objects.
        return ConvHelper.rs2j(J2RS.sum(
            ConvHelper.j2rs(a),
            ConvHelper.j2rs(b)
        ));
    }

}

Dependencies

~3–4MB
~49K SLoC