Skip to content

Commit 3388efd

Browse files
committed
JavaKit: support for Android NDK JVM
1 parent 86b44fa commit 3388efd

File tree

7 files changed

+131
-7
lines changed

7 files changed

+131
-7
lines changed

Package.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,9 @@ let package = Package(
202202
"-L\(javaHome)/lib"
203203
],
204204
.when(platforms: [.windows])),
205-
.linkedLibrary("jvm"),
205+
.linkedLibrary(
206+
"jvm",
207+
.when(platforms: [.iOS, .macOS, .tvOS, .watchOS, .macCatalyst, .linux, .openbsd, .wasi, .windows])),
206208
]
207209
),
208210
.target(

Samples/JavaKitSampleApp/Package.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ func findJavaHome() -> String {
3030
}
3131
let javaHome = findJavaHome()
3232

33-
let javaIncludePath = "\(javaHome)/include"
33+
let javaIncludePath = ProcessInfo.processInfo.environment["JAVA_INCLUDE_PATH"] ?? "\(javaHome)/include"
3434
#if os(Linux)
3535
let javaPlatformIncludePath = "\(javaIncludePath)/linux"
3636
#elseif os(macOS)

Samples/JavaKitSampleApp/Sources/JavaKitExample/JavaKitExample.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,10 @@ extension HelloSwift: HelloSwiftNativeMethods {
2727
let answer = self.sayHelloBack(i + j)
2828
print("Swift got back \(answer) from Java")
2929

30+
#if !canImport(Android)
31+
// no class variables in Kotlin
3032
print("We expect the above value to be the initial value, \(self.javaClass.initialValue)")
33+
#endif
3134

3235
print("Updating Java field value to something different")
3336
self.value = 2.71828

Sources/ExampleSwiftLibrary/MySwiftLibrary.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ import Glibc
2323
import CRT
2424
#elseif canImport(Darwin)
2525
import Darwin.C
26+
#elseif canImport(Android)
27+
import Android
2628
#endif
2729

2830
public func helloWorld() {

Sources/JavaKit/JavaEnvironment.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@
1414

1515
import JavaRuntime
1616

17+
#if canImport(Android)
18+
typealias JNINativeInterface_ = JNINativeInterface
19+
#endif
20+
1721
extension UnsafeMutablePointer<JNIEnv?> {
1822
var interface: JNINativeInterface_ { self.pointee!.pointee }
1923
}

Sources/JavaKit/JavaKitVM/JavaVirtualMachine.swift

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ import Foundation
1919
#endif
2020

2121
public typealias JavaVMPointer = UnsafeMutablePointer<JavaVM?>
22+
#if canImport(Android)
23+
typealias JNIEnvPointer = UnsafeMutablePointer<JNIEnv?>
24+
#else
25+
typealias JNIEnvPointer = UnsafeMutableRawPointer
26+
#endif
2227

2328
public final class JavaVirtualMachine: @unchecked Sendable {
2429
/// The JNI version that we depend on.
@@ -61,7 +66,7 @@ public final class JavaVirtualMachine: @unchecked Sendable {
6166
) throws {
6267
self.classpath = classpath
6368
var jvm: JavaVMPointer? = nil
64-
var environment: UnsafeMutableRawPointer? = nil
69+
var environment: JNIEnvPointer? = nil
6570
var vmArgs = JavaVMInitArgs()
6671
vmArgs.version = JavaVirtualMachine.jniVersion
6772
vmArgs.ignoreUnrecognized = jboolean(ignoreUnrecognized ? JNI_TRUE : JNI_FALSE)
@@ -161,12 +166,18 @@ extension JavaVirtualMachine {
161166
return environment.assumingMemoryBound(to: JNIEnv?.self)
162167
}
163168

169+
#if canImport(Android)
170+
var jniEnv = environment?.assumingMemoryBound(to: JNIEnv?.self)
171+
#else
172+
var jniEnv = environment
173+
#endif
174+
164175
// Attach the current thread to the JVM.
165176
let attachResult: jint
166177
if asDaemon {
167-
attachResult = jvm.pointee!.pointee.AttachCurrentThreadAsDaemon(jvm, &environment, nil)
178+
attachResult = jvm.pointee!.pointee.AttachCurrentThreadAsDaemon(jvm, &jniEnv, nil)
168179
} else {
169-
attachResult = jvm.pointee!.pointee.AttachCurrentThread(jvm, &environment, nil)
180+
attachResult = jvm.pointee!.pointee.AttachCurrentThread(jvm, &jniEnv, nil)
170181
}
171182

172183
// If we failed to attach, report that.
@@ -175,9 +186,13 @@ extension JavaVirtualMachine {
175186
throw attachError
176187
}
177188

178-
JavaVirtualMachine.destroyTLS.set(environment!)
189+
JavaVirtualMachine.destroyTLS.set(jniEnv!)
179190

180-
return environment!.assumingMemoryBound(to: JNIEnv?.self)
191+
#if canImport(Android)
192+
return jniEnv!
193+
#else
194+
return jniEnv!.assumingMemoryBound(to: JNIEnv?.self)
195+
#endif
181196
}
182197

183198
/// Detach the current thread from the Java Virtual Machine. All Java
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2024 Apple Inc. and the Swift.org project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of Swift.org project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
#ifdef __ANDROID__
16+
17+
#include <JavaRuntime.h>
18+
#include <android/log.h>
19+
#include <dlfcn.h>
20+
21+
// these are not exported by the Android SDK
22+
23+
extern "C" {
24+
using JavaRuntime_GetDefaultJavaVMInitArgs_fn = jint (*)(void *vm_args);
25+
using JavaRuntime_CreateJavaVM_fn = jint (*)(JavaVM **, JNIEnv **, void *);
26+
using JavaRuntime_GetCreatedJavaVMs_fn = jint (*)(JavaVM **, jsize, jsize *);
27+
}
28+
29+
static JavaRuntime_GetDefaultJavaVMInitArgs_fn
30+
JavaRuntime_GetDefaultJavaVMInitArgs;
31+
static JavaRuntime_CreateJavaVM_fn JavaRuntime_CreateJavaVM;
32+
static JavaRuntime_GetCreatedJavaVMs_fn JavaRuntime_GetCreatedJavaVMs;
33+
34+
static void *JavaRuntime_dlhandle;
35+
36+
__attribute__((constructor)) static void JavaRuntime_init(void) {
37+
JavaRuntime_dlhandle = dlopen("libnativehelper.so", RTLD_NOW | RTLD_LOCAL);
38+
if (JavaRuntime_dlhandle == nullptr) {
39+
__android_log_print(ANDROID_LOG_FATAL, "JavaRuntime",
40+
"failed to open libnativehelper.so: %s", dlerror());
41+
return;
42+
}
43+
44+
JavaRuntime_GetDefaultJavaVMInitArgs =
45+
reinterpret_cast<JavaRuntime_GetDefaultJavaVMInitArgs_fn>(
46+
dlsym(JavaRuntime_dlhandle, "JNI_GetDefaultJavaVMInitArgs"));
47+
if (JavaRuntime_GetDefaultJavaVMInitArgs == nullptr)
48+
__android_log_print(ANDROID_LOG_FATAL, "JavaRuntime",
49+
"JNI_GetDefaultJavaVMInitArgs not found: %s",
50+
dlerror());
51+
52+
JavaRuntime_CreateJavaVM = reinterpret_cast<JavaRuntime_CreateJavaVM_fn>(
53+
dlsym(JavaRuntime_dlhandle, "JNI_CreateJavaVM"));
54+
if (JavaRuntime_CreateJavaVM == nullptr)
55+
__android_log_print(ANDROID_LOG_FATAL, "JavaRuntime",
56+
"JNI_CreateJavaVM not found: %s", dlerror());
57+
58+
JavaRuntime_GetCreatedJavaVMs =
59+
reinterpret_cast<JavaRuntime_GetCreatedJavaVMs_fn>(
60+
dlsym(JavaRuntime_dlhandle, "JNI_GetCreatedJavaVMs"));
61+
if (JavaRuntime_GetCreatedJavaVMs == nullptr)
62+
__android_log_print(ANDROID_LOG_FATAL, "JavaRuntime",
63+
"JNI_GetCreatedJavaVMs not found: %s", dlerror());
64+
}
65+
66+
__attribute__((destructor)) static void JavaRuntime_deinit(void) {
67+
if (JavaRuntime_dlhandle) {
68+
dlclose(JavaRuntime_dlhandle);
69+
JavaRuntime_dlhandle = nullptr;
70+
}
71+
72+
JavaRuntime_GetDefaultJavaVMInitArgs = nullptr;
73+
JavaRuntime_CreateJavaVM = nullptr;
74+
JavaRuntime_GetCreatedJavaVMs = nullptr;
75+
}
76+
77+
jint JNI_GetDefaultJavaVMInitArgs(void *vm_args) {
78+
if (JavaRuntime_GetDefaultJavaVMInitArgs == nullptr)
79+
return JNI_ERR;
80+
81+
return (*JavaRuntime_GetDefaultJavaVMInitArgs)(vm_args);
82+
}
83+
84+
jint JNI_CreateJavaVM(JavaVM **vm, JNIEnv **env, void *vm_args) {
85+
if (JavaRuntime_CreateJavaVM == nullptr)
86+
return JNI_ERR;
87+
88+
return (*JavaRuntime_CreateJavaVM)(vm, env, vm_args);
89+
}
90+
91+
jint JNI_GetCreatedJavaVMs(JavaVM **vmBuf, jsize bufLen, jsize *nVMs) {
92+
if (JavaRuntime_GetCreatedJavaVMs == nullptr)
93+
return JNI_ERR;
94+
95+
return (*JavaRuntime_GetCreatedJavaVMs)(vmBuf, bufLen, nVMs);
96+
}
97+
98+
#endif

0 commit comments

Comments
 (0)