feat: Direct JNI converters to JSI
Before, a Java/Kotlin HybridObject always went through C++/std types, even if we discarded them right away since we pass them to Java.
For example, for Arrays this looked like this:
flowchart LR
jsi[jsi::Array]-->cpp[std::vector]-->jni[jni::JArray]
Now with this PR, we generate bindings directly to JSI:
flowchart LR
jsi[jsi::Array]-->jni[jni::JArray]
This is much more efficient, especially for Arrays, as we completely avoid C++/std types.
Additionally, since every HybridObject is a C++ object at it's base (since we also want to be able to use Kotlin/Swift types from C++ as if it was any conventional C++ class), we also need to generate bindings to C++:
flowchart LR
jsi[jsi::Array]-->jni[jni::JArray]
cpp[std::vector]-->jni[jni::JArray]
This looks like this in a HybridObject:
class JHybridMathSpec: public HybridMathSpec {
public:
// Called only if you use HybridMathSpec in C++ and call it's virtual method. Internally, this calls getSomethingJNI()
std::vector<double> getSomething();
// Bound to JS to override the prototype method so we go directly into JNI
jni::JArrayClass<double> getSomethingJNI();
}
It works!!! 🥳
So now it works (finally! 🥳), but the performance improvements are in the 5%-10% area, which is less than I hoped for.
We can still optimize a few things like trying to construct JString without std::string... I'll look into that.
Strings in Kotlin got a bit faster though:
The performance improvements are much less than what I originally anticipated. It seems like Nitro was already really fast on Android.
Going directly to JNI didn't really give any benefits other than a 20% speed improvement for Strings, but the rest roughly stayed the same as the C++ layer is so efficient already.
Also, with this PR the complexity of Kotlin HybridObjects greatly increased, because every property and method got generated twice - once for Kotlin (JNI <> JS), and once for C++ (JNI <> C++, to provide access to C++ if needed). This is very complex and potentially error prone.. Not sure if it's a good decision to do that.
I'll profile and investigate more on this, and think about if the added complexity in the code-generator CLI is worth the small performance improvements here.