-
Notifications
You must be signed in to change notification settings - Fork 66
Description
Currently, swift-java can crash runtime for Android API 28-30, if you try to use the JavaVirtualMachine class. This means that any JExtract generated sources (JNI mode) only works on API 31+, since we rely on JavaVirtualMachine.shared().environment().
This is due to the fact that Android first added official support for the JavaVM JNI functions, such as JNI_GetCreatedJavaVMs, in API 31+.
We rely on these methods for getting the active JavaVM, through for example JavaVirtualMachine.shared().
We have a shim for these JNI functions in https://github.com/swiftlang/swift-java/blob/main/Sources/CSwiftJavaJNI/AndroidSupport.cpp - this works on API 31+, because those Android versions have the libnativehelper.so. Previous versions did not. We cannot load libart.so or libdvm.so instead, since previous Android versions restricted dynamically loading these "private" libraries.
It seems like the recommended solution to this issue on Android, is to tap into JNI_OnLoad and store the VM from there. This would mean that we would need to update the shared VM at runtime:
| private static let sharedJVM: LockedState<JavaVirtualMachine?> = .init(initialState: nil) |
We need to figure out which library to dynamically load, that contains the JNI_OnLoad and can set the shared VM. My concern is since we already statically link the SwiftJava package (the generated sources depend on it), it would not be OK to just also dynamically load it on the Java side, but I am not sure.
An alternative solution could be (if this works in practice):
- Define a function in
SwiftJavawith@_silgen_name("_swift_java_setSharedVM"), which takes in a JavaVM and updates the shared one. - Call that function in
JNI_OnLoadfrom the already dynamically loadedSwiftRuntimeFunctionstarget to set the shared VM.