@@ -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
246217extension 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