Generic Javascript to Java bridge

When embedding v8, one of the pain points is how to call Java/Kotlin code from Javascript. It is not just a matter of setting a FunctionCallbackInfo<Value>, but also of dealing with JNI. While there are really impressive exercises for automating JNI code calls, these are only valid suitable when you know your JNI needs upfront, e.g. method signatures, calling objects/classes, etc. More concretely, when you can compile your own v8 code. In my case, all my v8 is primarily embedded in Android, and I share around an AAR file with all needed v8 dependencies. How each project depending on this AAR exposes its own Javascript bindings, is as simple as annotating Java code with @Bridge.

This post depicts how I built a dynamic bridge between Java and v8, and how methods annotated as @Bridge are automatically exposed in javascript.

For example:

// Java
class Test {
  @Bridge(returnsJSON = true)
  String method2() {
    return "{\"result\":0}";
  }

  String method1(int a, int[] i32, double[] f64) {
    return "";
  }
}

// Javascript
> Test.method2();
> { result: 0 }
> Test.method1(1, [1,2,3], new Float64Array([.1, .2, .3]));
> ""

// Javascript call with wrong Java parameter signature:
> Test.method2(1,2);
> null
> Test.method1(32);
> null

As nice as it might sound, it comes with a myriad of limitations:

  • Method disambiguation. In Java we can have method overloading, which does not exist in Javascript.
  • Javascript to Java and vice versa type conversion. Only number, string, boolean, null, typed arrays and array, or object of these types can be safely converted between environments. Arrays and Objects are defined recursively.
  • Java method signatures. In Javascript, you can call a function with an arbitrary number of parameters and types. Exposed Java functions are no exception. Only Javascript calls that match Java method signature (based on previous point types’ conversion) will be executed. By now, this is a reflection based method invocation. Slow, but convenient.
  • Java method return types. While a Java method can virtually return anything, I constrained return types to String. Optionally, this string can be JSON parsed before setting the Javascript call return type. If any error has been caught, null will be returned instead.
  • Every Javascript call will pass through a unique JNI entry point.
  • Asynchronous calls. These can not be directly modelled with this approach. But they work under other @Bridge annotation options.
  • It tends to be slow: reflection in Java + JNI bridge. Read on my APT articles on how to remove reflection calls.

Are all these limitations worth it ? it totally is for non critical code paths. In my case I use it for things like calling the speech synthesiser, open share dialogs, create gl textures… But definitely not for calling into every OpenGL function per frame for example.

Implementation details in an upcoming article version 2.

Published by ibon

Chocolate engineer, software eater. Data visualisation at Workday. Past: Platochat, SdkBox, Chukong, Ludei.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: