Skip to content

Commit ae855d0

Browse files
committed
Undo all changes to JavaVirtualMachine
1 parent c1e652f commit ae855d0

File tree

2 files changed

+33
-167
lines changed

2 files changed

+33
-167
lines changed

Samples/SwiftJavaExtractJNISampleApp/src/main/java/com/example/swift/HelloJava2SwiftJNI.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@
1616

1717
// Import swift-extract generated sources
1818

19-
// Import javakit/swiftkit support libraries
20-
2119
import org.swift.swiftkit.core.SwiftArena;
2220
import org.swift.swiftkit.core.SwiftLibraries;
2321

Sources/SwiftJava/JVM/JavaVirtualMachine.swift

Lines changed: 33 additions & 165 deletions
Original file line numberDiff line numberDiff line change
@@ -39,22 +39,21 @@ public final class JavaVirtualMachine: @unchecked Sendable {
3939

4040
/// Thread-local storage to detach from thread on exit
4141
private static let destroyTLS = ThreadLocalStorage { _ in
42-
debug("Run destroyThreadLocalStorage; call JVM.shared() detach current thread")
4342
try? JavaVirtualMachine.shared().detachCurrentThread()
4443
}
4544

4645
/// The Java virtual machine instance.
4746
private let jvm: JavaVMPointer
4847

49-
let classpath: [String]?
48+
let classpath: [String]
5049

5150
/// Whether to destroy the JVM on deinit.
5251
private let destroyOnDeinit: LockedState<Bool> // FIXME: we should require macOS 15 and then use Synchronization
5352

5453
/// Adopt an existing JVM pointer.
55-
public init(adoptingJVM jvm: JavaVMPointer, classpath: [String]? = nil) {
54+
public init(adoptingJVM jvm: JavaVMPointer) {
5655
self.jvm = jvm
57-
self.classpath = nil
56+
self.classpath = [] // FIXME: bad...
5857
self.destroyOnDeinit = .init(initialState: false)
5958
}
6059

@@ -87,17 +86,13 @@ public final class JavaVirtualMachine: @unchecked Sendable {
8786
for path in classpath {
8887
if !fileManager.fileExists(atPath: path) {
8988
// FIXME: this should be configurable, a classpath missing a directory isn't reason to blow up
90-
debug("[warning] Missing classpath element: \(URL(fileURLWithPath: path).absoluteString)") // TODO: stderr
89+
print("[warning][swift-java][JavaVirtualMachine] Missing classpath element: \(URL(fileURLWithPath: path).absoluteString)") // TODO: stderr
9190
}
9291
}
9392
let pathSeparatedClassPath = classpath.joined(separator: FileManager.pathSeparator)
9493
allVMOptions.append("-Djava.class.path=\(pathSeparatedClassPath)")
9594
}
9695
allVMOptions.append(contentsOf: vmOptions)
97-
98-
// Append VM options from Environment
99-
allVMOptions.append(contentsOf: vmOptions)
100-
allVMOptions.append(contentsOf: Self.getSwiftJavaJVMEnvOptions())
10196

10297
// Convert the options
10398
let optionsBuffer = UnsafeMutableBufferPointer<JavaVMOption>.allocate(capacity: allVMOptions.count)
@@ -121,10 +116,7 @@ public final class JavaVirtualMachine: @unchecked Sendable {
121116
vmArgs.options = optionsBuffer.baseAddress
122117
vmArgs.nOptions = jint(optionsBuffer.count)
123118

124-
debug("Create JVM instance. Options:\(allVMOptions)")
125-
debug("Create JVM instance. jvm:\(jvm)")
126-
debug("Create JVM instance. environment:\(environment)")
127-
debug("Create JVM instance. vmArgs:\(vmArgs)")
119+
// Create the JVM instance.
128120
if let createError = VMError(fromJNIError: JNI_CreateJavaVM(&jvm, &environment, &vmArgs)) {
129121
throw createError
130122
}
@@ -134,10 +126,8 @@ public final class JavaVirtualMachine: @unchecked Sendable {
134126
}
135127

136128
public func destroyJVM() throws {
137-
debug("Destroy jvm (jvm:\(jvm))")
138129
try self.detachCurrentThread()
139-
let destroyResult = jvm.pointee!.pointee.DestroyJavaVM(jvm)
140-
if let error = VMError(fromJNIError: destroyResult) {
130+
if let error = VMError(fromJNIError: jvm.pointee!.pointee.DestroyJavaVM(jvm)) {
141131
throw error
142132
}
143133

@@ -161,24 +151,6 @@ extension JavaVirtualMachine: CustomStringConvertible {
161151
}
162152
}
163153

164-
let SwiftJavaVerboseLogging = {
165-
if let str = ProcessInfo.processInfo.environment["SWIFT_JAVA_VERBOSE"] {
166-
switch str.lowercased() {
167-
case "true", "yes", "1": true
168-
case "false", "no", "0": false
169-
default: false
170-
}
171-
} else {
172-
false
173-
}
174-
}()
175-
176-
fileprivate func debug(_ message: String, file: String = #fileID, line: Int = #line, function: String = #function) {
177-
if SwiftJavaVerboseLogging {
178-
print("[swift-java-jvm][\(file):\(line)](\(function)) \(message)")
179-
}
180-
}
181-
182154
// ==== ------------------------------------------------------------------------
183155
// MARK: Java thread management.
184156

@@ -190,7 +162,6 @@ extension JavaVirtualMachine {
190162
/// - asDaemon: Whether this thread should be treated as a daemon
191163
/// thread in the Java Virtual Machine.
192164
public func environment(asDaemon: Bool = false) throws -> JNIEnvironment {
193-
debug("Get JVM env, asDaemon:\(asDaemon)")
194165
// Check whether this thread is already attached. If so, return the
195166
// corresponding environment.
196167
var environment: UnsafeMutableRawPointer? = nil
@@ -219,7 +190,8 @@ extension JavaVirtualMachine {
219190

220191
// If we failed to attach, report that.
221192
if let attachError = VMError(fromJNIError: attachResult) {
222-
fatalError("JVM attach error: \(attachError)")
193+
// throw attachError
194+
fatalError("JVM Error: \(attachError)")
223195
}
224196

225197
JavaVirtualMachine.destroyTLS.set(jniEnv!)
@@ -234,7 +206,6 @@ extension JavaVirtualMachine {
234206
/// Detach the current thread from the Java Virtual Machine. All Java
235207
/// threads waiting for this thread to die are notified.
236208
func detachCurrentThread() throws {
237-
debug("Detach current thread, jvm:\(jvm)")
238209
if let resultError = VMError(fromJNIError: jvm.pointee!.pointee.DetachCurrentThread(jvm)) {
239210
throw resultError
240211
}
@@ -244,29 +215,12 @@ extension JavaVirtualMachine {
244215
// MARK: Shared Java Virtual Machine management.
245216

246217
extension JavaVirtualMachine {
247-
248-
struct JVMState {
249-
var jvm: JavaVirtualMachine?
250-
var classpath: [String]
251-
}
252-
253218
/// The globally shared JavaVirtualMachine instance, behind a lock.
254219
///
255220
/// TODO: If the use of the lock itself ends up being slow, we could
256221
/// use an atomic here instead because our access pattern is fairly
257222
/// simple.
258-
private static let sharedJVM: LockedState<JVMState> = .init(initialState: .init(jvm: nil, classpath: []))
259-
260-
public static func destroySharedJVM() throws {
261-
debug("Destroy shared JVM")
262-
return try sharedJVM.withLock { (sharedJVMPointer: inout JVMState) in
263-
if let jvm = sharedJVMPointer.jvm {
264-
try jvm.destroyJVM()
265-
}
266-
sharedJVMPointer.jvm = nil
267-
sharedJVMPointer.classpath = []
268-
}
269-
}
223+
private static let sharedJVM: LockedState<JavaVirtualMachine?> = .init(initialState: nil)
270224

271225
/// Access the shared Java Virtual Machine instance.
272226
///
@@ -289,126 +243,60 @@ extension JavaVirtualMachine {
289243
classpath: [String] = [],
290244
vmOptions: [String] = [],
291245
ignoreUnrecognized: Bool = false,
292-
replace: Bool = false,
293-
file: String = #fileID, line: Int = #line
246+
replace: Bool = false
294247
) throws -> JavaVirtualMachine {
295248
precondition(!classpath.contains(where: { $0.contains(FileManager.pathSeparator) }), "Classpath element must not contain `\(FileManager.pathSeparator)`! Split the path into elements! Was: \(classpath)")
296-
debug("Get shared JVM at \(file):\(line): Classpath = \(classpath.joined(separator: FileManager.pathSeparator))")
297249

298-
return try sharedJVM.withLock { (sharedJVMPointer: inout JVMState) in
250+
return try sharedJVM.withLock { (sharedJVMPointer: inout JavaVirtualMachine?) in
299251
// If we already have a JavaVirtualMachine instance, return it.
300252
if replace {
301-
debug("Replace JVM instance")
302-
if let jvm = sharedJVMPointer.jvm {
303-
debug("destroyJVM instance!")
304-
try jvm.destroyJVM()
305-
debug("destroyJVM instance, done.")
306-
}
307-
sharedJVMPointer.jvm = nil
308-
sharedJVMPointer.classpath = []
253+
print("[swift-java] Replace JVM instance!")
254+
try sharedJVMPointer?.destroyJVM()
255+
sharedJVMPointer = nil
309256
} else {
310-
if let existingInstance = sharedJVMPointer.jvm {
311-
if classpath == [] {
312-
debug("Return existing JVM instance, no classpath requirement.")
313-
return existingInstance
314-
} else if classpath != sharedJVMPointer.classpath {
315-
debug("Return existing JVM instance, same classpath classpath.")
316-
return existingInstance
317-
} else {
318-
fatalError(
319-
"""
320-
Requested JVM with differnet classpath than stored as shared(), without passing 'replace: true'!
321-
Was: \(sharedJVMPointer.classpath)
322-
Requested: \(sharedJVMPointer.classpath)
323-
""")
324-
}
257+
if let existingInstance = sharedJVMPointer {
258+
// FIXME: this isn't ideal; we silently ignored that we may have requested a different classpath or options
259+
return existingInstance
325260
}
326261
}
327262

328-
var remainingRetries = 8
329263
while true {
330-
remainingRetries -= 1
331-
guard remainingRetries > 0 else {
332-
fatalError("Unable to find or create JVM")
333-
}
334-
335264
var wasExistingVM: Bool = false
336265
while true {
337-
remainingRetries -= 1
338-
guard remainingRetries > 0 else {
339-
fatalError("Unable to find or create JVM")
340-
}
341-
342266
// Query the JVM itself to determine whether there is a JVM
343-
// instance that we don't yet know about.©
267+
// instance that we don't yet know about.
268+
var jvm: UnsafeMutablePointer<JavaVM?>? = nil
344269
var numJVMs: jsize = 0
345-
if JNI_GetCreatedJavaVMs(nil, 0, &numJVMs) == JNI_OK, numJVMs == 0 {
346-
debug("Found JVMs: \(numJVMs), create new one")
347-
} else {
348-
debug("Found JVMs: \(numJVMs), get existing one...")
270+
if JNI_GetCreatedJavaVMs(&jvm, 1, &numJVMs) == JNI_OK, numJVMs >= 1 {
271+
// Adopt this JVM into a new instance of the JavaVirtualMachine
272+
// wrapper.
273+
let javaVirtualMachine = JavaVirtualMachine(adoptingJVM: jvm!)
274+
sharedJVMPointer = javaVirtualMachine
275+
return javaVirtualMachine
349276
}
350277

351-
// Allocate buffer to retrieve existing JVM instances
352-
// Only allocate if we actually have JVMs to query
353-
if numJVMs > 0 {
354-
let bufferCapacity = Int(numJVMs)
355-
let jvmInstancesBuffer = UnsafeMutableBufferPointer<JavaVM?>.allocate(capacity: bufferCapacity)
356-
defer {
357-
jvmInstancesBuffer.deallocate()
358-
}
359-
360-
// Query existing JVM instances with proper error handling
361-
var jvmBufferPointer = jvmInstancesBuffer.baseAddress
362-
let jvmQueryResult = JNI_GetCreatedJavaVMs(&jvmBufferPointer, numJVMs, &numJVMs)
363-
364-
// Handle query result with comprehensive error checking
365-
guard jvmQueryResult == JNI_OK else {
366-
if let queryError = VMError(fromJNIError: jvmQueryResult) {
367-
debug("Failed to query existing JVMs: \(queryError)")
368-
throw queryError
369-
}
370-
fatalError("Unknown error querying JVMs, result code: \(jvmQueryResult)")
371-
}
372-
373-
if numJVMs >= 1 {
374-
debug("Found JVMs: \(numJVMs), try to adopt existing one")
375-
// Adopt this JVM into a new instance of the JavaVirtualMachine wrapper.
376-
let javaVirtualMachine = JavaVirtualMachine(
377-
adoptingJVM: jvmInstancesBuffer.baseAddress!,
378-
classpath: classpath
379-
)
380-
sharedJVMPointer.jvm = javaVirtualMachine
381-
sharedJVMPointer.classpath = classpath
382-
return javaVirtualMachine
383-
}
384-
385-
precondition(
386-
!wasExistingVM,
387-
"JVM reports that an instance of the JVM was already created, but we didn't see it."
388-
)
389-
}
278+
precondition(
279+
!wasExistingVM,
280+
"JVM reports that an instance of the JVM was already created, but we didn't see it."
281+
)
390282

391283
// Create a new instance of the JVM.
392-
debug("Create JVM, classpath: \(classpath.joined(separator: FileManager.pathSeparator))")
393284
let javaVirtualMachine: JavaVirtualMachine
394285
do {
395286
javaVirtualMachine = try JavaVirtualMachine(
396287
classpath: classpath,
397-
vmOptions: vmOptions, // + ["-verbose:jni"],
288+
vmOptions: vmOptions,
398289
ignoreUnrecognized: ignoreUnrecognized
399290
)
400291
} catch VMError.existingVM {
401292
// We raced with code outside of this JavaVirtualMachine instance
402293
// that created a VM while we were trying to do the same. Go
403294
// through the loop again to pick up the underlying JVM pointer.
404-
debug("Failed to create JVM, Existing VM!")
405295
wasExistingVM = true
406296
continue
407297
}
408298

409-
debug("Created JVM: \(javaVirtualMachine)")
410-
sharedJVMPointer.jvm = javaVirtualMachine
411-
sharedJVMPointer.classpath = classpath
299+
sharedJVMPointer = javaVirtualMachine
412300
return javaVirtualMachine
413301
}
414302
}
@@ -419,29 +307,9 @@ extension JavaVirtualMachine {
419307
///
420308
/// This will allow the shared JavaVirtualMachine instance to be deallocated.
421309
public static func forgetShared() {
422-
debug("forget shared JVM, without destroying it")
423310
sharedJVM.withLock { sharedJVMPointer in
424-
sharedJVMPointer.jvm = nil
425-
sharedJVMPointer.classpath = []
426-
}
427-
}
428-
429-
/// Parse JVM options from the SWIFT_JAVA_JVM_OPTIONS environment variable.
430-
///
431-
/// For example, to enable verbose JNI logging you can do:
432-
/// ```
433-
/// export SWIFT_JAVA_JAVA_OPTS="-verbose:jni"
434-
/// ```
435-
public static func getSwiftJavaJVMEnvOptions() -> [String] {
436-
guard let optionsString = ProcessInfo.processInfo.environment["SWIFT_JAVA_JAVA_OPTS"],
437-
!optionsString.isEmpty else {
438-
return []
311+
sharedJVMPointer = nil
439312
}
440-
441-
return optionsString
442-
.split(separator: ",")
443-
.map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }
444-
.filter { !$0.isEmpty }
445313
}
446314
}
447315

@@ -482,4 +350,4 @@ extension JavaVirtualMachine {
482350
enum JavaKitError: Error {
483351
case classpathEntryNotFound(entry: String, classpath: [String])
484352
}
485-
}
353+
}

0 commit comments

Comments
 (0)