arsd.jni

Provides easy interoperability with Java code through JNI.

Given this Java:

1 class Hello {
2 	public native void hi(String s);
3 	public native String stringFromJNI();
4 	public native String returnNull();
5 	public native void throwException();
6 	static {
7 		System.loadLibrary("myjni");
8 	}
9 	public static void main(String[] args) {
10 		System.out.println("Hello from Java!");
11 		Hello h = new Hello();
12 		// we can pass data back and forth normally
13 		h.hi("jni");
14 		System.out.println(h.stringFromJNI());
15 		System.out.println(h.returnNull()); // it can handle null too
16 		// and even forward exceptions (sort of, it puts it in a RuntimeException right now)
17 		h.throwException();
18 	}
19 }

And this D:

1 import arsd.jni;
2 
3 // if it was in a Java package, you'd pass that
4 // in the string here instead of "".
5 final class Hello : JavaClass!("", Hello) {
6 
7 	@Export string stringFromJNI() {
8 		return "hey, D returned this";
9 	}
10 
11 	@Export string returnNull() {
12 		return null;
13 	}
14 
15 	@Export void throwException() {
16 		throw new Exception("exception from D");
17 	}
18 
19 	@Export void hi(string name) {
20 		import std.stdio;
21 		writefln("hello from D, %s", name);
22 	}
23 }

We can:

$ javac Hello.java
$ dmd -shared myjni.d jni.d # compile into a shared lib
$ LD_LIBRARY_PATH=. java Hello
Hello from Java!
hello from D, jni
hey, D returned this
null
Exception in thread "main" java.lang.RuntimeException: object.Exception@myjni.d(14): exception from D
----------------
??:? void myjni.Hello.throwException() [0x7f51d86dc17b]
??:? Java_Hello_throwException [0x7f51d86dd3e0]
??:? [0x7f51dd018406]
??:? [0x7f51dd007ffc]
??:? [0x7f51dd0004e6]
??:? [0x7f51f16b0709]
??:? [0x7f51f16c1339]
??:? [0x7f51f16d208d]
??:? [0x7f51f1f97058]
??:? [0x7f51f1fae06a]
	at Hello.throwException(Native Method)
	at Hello.main(Hello.java:17)

Exact details subject to change, especially of how I pass the exceptions over.

It is also possible to call Java methods and create Java objects from D with the @Import uda.

While you can write pretty ordinary looking D code, there's some things to keep in mind for safety and efficiency.

ALL references passed to you through Java, including arrays, objects, and even the this pointer, MUST NOT be stored outside the lifetime of the immediate function they were passed to!

You may be able to get the D compiler to help you with this with the scope attribute, but regardless, don't do it.

It is YOUR responsibility to make sure parameter and return types match between D and Java. The library will static assert given unrepresentable types, but it cannot see what Java actually expects. Getting this wrong can lead to memory corruption and crashes.

When possible, use wstring instead of string when working with Java APIs. wstring matches the format of Java's String so it avoids a conversion step.

All JavaClass sub-objects should be marked final on the D side. Java may subclass them, but D can't (at least not now).

Do not use default arguments on the exported methods. No promise the wrapper will do what you want when called from Java.

You may choose to only import JavaClass from here to minimize the namespace pollution.

Constructing Java objects works and it will pin it. Just remember that this inside a method is still subject to escaping restrictions!

Members

Classes

JavaClass
class JavaClass(string javaPackage, CRTP, Parent = void, bool isNewClass = false)

This is the base class you inherit from in D classes that represent Java classes. You can then mark your methods @Import if they are implemented in Java and you want to call them from D, or @Export if they are implemented in D and want to be called as a native method from Java.

Functions

createJvm
auto createJvm()

Creates a JVM for use when main is in D. Keep the returned struct around until you are done with it. While this struct is live, you can use imported Java classes and methods almost as if they were written in D.

jarToD
void jarToD(string jarPath, string dPackagePrefix, string outputDirectory, JavaTranslationConfig jtc, bool delegate(string className) classFilter = null)

translator.

rawClassBytesToD
void rawClassBytesToD(ubyte[] bytes, string dPackagePrefix, string outputDirectory, JavaTranslationConfig jtc)

translator

rawClassStructToD
void rawClassStructToD(ref ClassFile cf, string dPackagePrefix, string outputDirectory, JavaTranslationConfig jtc, ClassFile[string] allClasses)

translator.

Interfaces

CharSequence
interface CharSequence

Java's String class implements its CharSequence interface. D's string is not a class at all, so it cannot directly do that. Instead, this translation of the interface has static methods to return a dummy class wrapping D's string.

IJavaObject
interface IJavaObject

This is really used by the JavaClass class below to give a base for all Java classes. You can use it for that too, but you really shouldn't try to implement it yourself (it doesn't do much anyway and the other code in here assumes the presence of IJavaObject on an object also means various internal static members of JavaClass are present too).

JavaInterface
interface JavaInterface(string javaPackage, CRTP)

Indicates that your interface represents an interface in Java.

Static functions

dummyClass
T dummyClass(jobject obj)

I may not keep this. But for now if you need a dummy class in D to represent some object that implements this interface in Java, you can use this. The dummy class assumes all interface methods are @Imported.

Structs

Default
struct Default

hack used by the translator for default constructors not really being a default constructor

JavaName
struct JavaName

Can be used as a UDA for methods or classes where the D name and the Java name don't match (for example, if it happens to be a D keyword).

JavaTranslationConfig
struct JavaTranslationConfig

For the translator

Meta