Skip to content

Latest commit

 

History

History
55 lines (46 loc) · 1.59 KB

DEVELOPMENT.md

File metadata and controls

55 lines (46 loc) · 1.59 KB

How Java and Rust communicate

Java allows to write native code, and to expose it through a Java interface. For example, in Instance.java, we can read:

class Instance {
    private native long nativeInstantiate(Instance self, byte[] moduleBytes) throws RuntimeException;
    // …
}

We define a public method to call a native method, such as:

    public Instance(byte[] moduleBytes) throws RuntimeException {
        long instancePointer = this.instantiate(this, moduleBytes);

        this.instancePointer = instancePointer;
    }

The native implementation is written in Rust. First, a C header is generated with just build-headers which is located in include/. The generated code for nativeInstantiate is the following:

/*
 * Class:     org_wasmer_Instance
 * Method:    nativeInstantiate
 * Signature: (Lorg/wasmer/Instance;[B)J
 */
JNIEXPORT jlong JNICALL Java_org_wasmer_Instance_nativeInstantiate
  (JNIEnv *, jobject, jobject, jbyteArray);

Second, on the Rust side, we have to declare a function with the same naming:

#[no_mangle]
pub extern "system" fn Java_org_wasmer_Instance_nativeInstantiate(
    env: JNIEnv,
    _class: JClass,
    this: JObject,
    module_bytes: jbyteArray,
) -> jptr {
    // …
}

And the dynamic linking does the rest (it's done with the java.library.path configuration on the Java side). It uses a shared library (.dylib on macOS, .so on Linux, .dll on Windows).

Then, we have to convert “Java data” to “Rust data”. jni-rs's documentation is our best friend here.