Git Product home page Git Product logo

rucaja's Introduction

Rucaja (Rust calls Java) Build Status

Calling JVM code from Rust via JNI.

Usage

JNI calls are about 10-20 times slower than regular JVM instructions. It is adviced to call as few functions as possible that do as much work as possible.

Creating a flat Java wrapper class and producing a fat JAR with all the dependencies also reduces the amount of Rust interface code.

A more complete example is kud1ing/tinkerpop-rs which uses Rucaja to call Apache TinkerPop.

Platforms

The code is tested on Linux and macOS. Your platform might need adjustments in build.rs.

Mac

Trying to run may give:

dyld: Library not loaded: @rpath/libjvm.dylib
  Referenced from: ./target/debug/rucaja
  Reason: image not found
Abort trap: 6

this might require something like:

sudo ln -s $(/usr/libexec/java_home)/jre/lib/server/libjvm.dylib /usr/local/lib

License

Licensed under either of

at your option.

rucaja's People

Contributors

fpoli avatar kud1ing avatar treyzania 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

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

rucaja's Issues

Refactoring

  • move Jvm::new_jvm_string() => JvmString::new()
  • move Jvm::get_static_method() => JvmMethod::get_static_method()
  • move Jvm::get_method() => JvmMethod::get_method()
  • move Jvm::get_constructor() => JvmMethod::new_constructor()
  • move Jvm::get_class() => JvmClass::new()
  • move Jvm::call_*() to JvmMethod
  • change every struct member of type &Jvm => &JvmAttachment

Multi-thread use case

My code is in a multi-thread setting, inside each thread, there is a need to call jvm functions.

I find that the "jvm" instance itself cannot be constructed separately inside each thread, by loading the classpath with proper options. In this case, it will panic with a message:

 '`JNI_CreateJavaVM()` signaled an error: JVM exists already'` ...rucaja-0.4.0/src/jvm.rs:186:12

This alternative seems to be putting JVM construction code before spawning threads and then passing as parameters.

The problem is that I'm not quite sure whether this will cause data race internally at the JVM side.

Since I know little about JNI and would like to be merely a "user" of rucaja, Could you please advise?

How to build a Java array of strings

Is it possible to build a Java array from rucaja?

I would like to call a Java method that takes an array of strings (eg. void main(String[] args)), but I can't find how to build the `jvalue' of the array.

...

let java_string: JvmString = jvm.new_jvm_string("Hello").unwrap();

let args: Vec<jvalue> = vec![
    // ?
];

// Call the main
jvm.call_static_object_method(
    &main_class,
    &main_method,
    args.as_ptr()
);

NullPointerException in tests/java_array

I open this issue to track the NullPointerException in tests/java_array.rs (introduced in #22).

Starting the JVM with -Xcheck:jni gives a (slightly) better error message:

FATAL ERROR in native method: Bad global or local ref passed to JNI

Disabling -Xcheck:jni and running the test with a debug JVM the message becomes more interesting:

# To suppress the following error report, specify this argument
# after -XX: or in .hotspotrc:  SuppressErrorAt=/jniHandles.hpp:178
#
# A fatal error has been detected by the Java Runtime Environment:
#
#  Internal Error (/home/fpoli/hg/jdk8u/hotspot/src/share/vm/runtime/jniHandles.hpp:178), pid=15159, tid=0x00007fd6ef1ed840
#  assert(result != badJNIHandle) failed: Pointing to zapped jni handle area
#
# JRE version: OpenJDK Runtime Environment (8.0) (build 1.8.0-internal-fastdebug-fpoli_2017_11_21_14_33-b00)
# Java VM: OpenJDK 64-Bit Server VM (25.71-b00-fastdebug mixed mode linux-amd64 compressed oops)
# Core dump written. Default location: /home/fpoli/src/rust-calls-jvm/core or core.15159
#
# An error report file with more information is saved as:
# /home/fpoli/src/rust-calls-jvm/hs_err_pid15159.log
#
# If you would like to submit a bug report, please visit:
#   http://bugreport.java.com/bugreport/crash.jsp
#

Looking at hotspot/src/share/vm/runtime/jniHandles.hpp:178 the content is this:

inline oop JNIHandles::resolve(jobject handle) {
  oop result = (handle == NULL ? (oop)NULL : *(oop*)handle);
  assert(result != NULL || (handle == NULL || !CheckJNICalls || is_weak_global_handle(handle)), "Invalid value read from jni handle");
  assert(result != badJNIHandle, "Pointing to zapped jni handle area"); // <-- line 178
  return result;
};

convert jstring to Rust string buffer

The primitive java types have direct mappings in jni_sys.

So is there any convenient way to convert jstring (e.g., generated by jvm::call_static_object_method) to a "str"/"&[u8]"?

In my opinion this should be reasonable since Java String is quite special.
Maybe there should be an additional parameter to keep the length?

Crash

Process 66240 launched: './target/debug/rucaja' (x86_64)
Process 66240 stopped
* thread #1: tid = 0x3c105d, 0x00007fffe740be80 libsystem_platform.dylib_platform_strncmp + 320, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x0) frame #0: 0x00007fffe740be80 libsystem_platform.dylib_platform_strncmp + 320
libsystem_platform.dylib`_platform_strncmp:
-> 0x7fffe740be80 <+320>: movzbq (%rdi,%rcx), %rax
0x7fffe740be85 <+325>: movzbq (%rsi,%rcx), %r8
0x7fffe740be8a <+330>: subq %r8, %rax
0x7fffe740be8d <+333>: jne 0x7fffe740be9d ; <+349>

Type for holding Java objects

Currently there are JvmClass, JvmObject and JvmStrings to bring some compile time type safety into Rust regarding different JVM classes. Unfortunately there is some code duplication.

Come up with an optimal way to hold Java objects in a Rust type that reflect the most specific Java class.

Aspects:

  • jobject is just a type alias to *mut _jobject
    • jclass, jstring etc. are just type aliases to jobject
  • in the constructor of the wrapper
    • do not accept JVM null pointers
    • do not accept JVM objects of the wrong type
      • IsInstanceOf() compares a jobject with a jclass.

Options:

Determining linkage to `libjvm` is less than perfect, doesn't work on certain Linux distros

This won't work properly my machine as I don't have $JAVA_HOME set and I'm not on macOS.

On my system (Xubuntu 16.04), libjvm.so lives in /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/server. But there's a symlink to the java-8-openjdk-amd64 directory at /usr/lib/jvm/default-java/jre/lib/amd64/server. Different Linux distributions do things different ways so this might not work 100% the same outside of Debian-land.

My /etc/os-release is:

NAME="Ubuntu"
VERSION="16.04.3 LTS (Xenial Xerus)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 16.04.3 LTS"
VERSION_ID="16.04"
HOME_URL="http://www.ubuntu.com/"
SUPPORT_URL="http://help.ubuntu.com/"
BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"
VERSION_CODENAME=xenial
UBUNTU_CODENAME=xenial

The problem is that not all Linux distributions have an OS release file like this, so it's actually really hard to figure out where libjvm.so is supposed to live. But worse-case scenario we can just search through /usr/lib/jvm until we find a directory with libjvm.so in it, as most people don't have enough JVMs installed to make that unreasonably slow.

I might make a pull request soon with a fix in build.rs that'll probably work on Debians, but I don't really have anything to test it with beyond cargo test.

Reduce the usage of `unwrap()`

We use unwrap() in some places where the result should "in theory" always be existant.

We should handle those unlikely cases. Consider:

  • bubble Nones up in functions that return an Option. Consider using ? for Options in Rust 1.22.
  • panic if JVM methods fail when we can not handle it otherwise

Add JVM options

(fluent interface).

JavaVMOption options[4];
options[0].optionString = "-Djava.compiler=NONE";
...
vm_args.options = options;
vm_args.nOptions = 4;

Provide easier to use high-level functions

Currently it takes a lot of code in order to call a JVM method: resolve the class, resolve the method, build the arguments from Rust data, possibly convert the result back to Rust data types etc.

It would be nice to be able to do this with less code in Rust.
I think being more wasteful, like resolving classes/method, constructing data with each call, is fine for the sake of convenience. Users can still opt to avoid those calls by using the current wordy interface.

The official recommendation is - and maybe we should reproduce the tip exlicitely in our documentation - is to use as few JNI calls as possible.

The signature builder from #3 supports usability too.

Reduce `unsafe` from the function declarations to smallest possible `unsafe` blocks

Should we hide unsafe inside the functions or is more honest/beneficial to leak unsafe in the function declarations as it is?

Discussion from the perspective of keep leaking unsafeness:

  • Pro:
    • what Rucaja does is possibly unsafe
      • the JNI is low-level and not well documented
      • but we try hard to use JNI correctly and thus everything we do should be safe, unless it is a bug
  • Con:
    • forces users to deal with unsafeness

`new_jstring()` should return a non-interned Java String

NewStringUTF() produces a java.security.AccessControlContext and not a java.lang.String. An object of the same type is generated by static_method_that_returns_an_interned_string() in the example.

We probably need to write a JvmString analogous to JvmObject so that the Java string is retained using NewGlobalRef() for a longer time.

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.