diff --git a/Package.swift b/Package.swift index 2c56a2870..b6a18f90e 100644 --- a/Package.swift +++ b/Package.swift @@ -100,6 +100,7 @@ let package = Package( // ==== SwiftJava (i.e. calling Java directly Swift utilities) .library( name: "SwiftJava", + type: .dynamic, targets: ["SwiftJava"] ), diff --git a/Samples/SwiftAndJavaJarSampleLib/Package.swift b/Samples/SwiftAndJavaJarSampleLib/Package.swift index 32ffbb287..f501b984e 100644 --- a/Samples/SwiftAndJavaJarSampleLib/Package.swift +++ b/Samples/SwiftAndJavaJarSampleLib/Package.swift @@ -63,7 +63,9 @@ let package = Package( .target( name: "MySwiftLibrary", dependencies: [ - .product(name: "SwiftRuntimeFunctions", package: "swift-java"), + .product(name: "SwiftJava", package: "swift-java"), + .product(name: "CSwiftJavaJNI", package: "swift-java"), + .product(name: "SwiftRuntimeFunctions", package: "swift-java") ], exclude: [ "swift-java.config", diff --git a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift index a0f68ee00..a16ac8a89 100644 --- a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift +++ b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift @@ -195,6 +195,7 @@ extension FFMSwift2JavaGenerator { private static final boolean INITIALIZED_LIBS = initializeLibs(); static boolean initializeLibs() { System.loadLibrary(SwiftLibraries.LIB_NAME_SWIFT_CORE); + System.loadLibrary(SwiftLibraries.LIB_NAME_SWIFT_JAVA); System.loadLibrary(SwiftLibraries.LIB_NAME_SWIFT_RUNTIME_FUNCTIONS); System.loadLibrary(LIB_NAME); return true; @@ -345,6 +346,7 @@ extension FFMSwift2JavaGenerator { private static SymbolLookup getSymbolLookup() { if (SwiftLibraries.AUTO_LOAD_LIBS) { System.loadLibrary(SwiftLibraries.LIB_NAME_SWIFT_CORE); + System.loadLibrary(SwiftLibraries.LIB_NAME_SWIFT_JAVA); System.loadLibrary(SwiftLibraries.LIB_NAME_SWIFT_RUNTIME_FUNCTIONS); System.loadLibrary(LIB_NAME); } diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift index 78c91376d..28269ecd8 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift @@ -99,6 +99,7 @@ extension JNISwift2JavaGenerator { static final String LIB_NAME = "\(swiftModuleName)"; static { + System.loadLibrary(SwiftLibraries.LIB_NAME_SWIFT_JAVA); System.loadLibrary(LIB_NAME); } """ @@ -169,6 +170,7 @@ extension JNISwift2JavaGenerator { @SuppressWarnings("unused") private static final boolean INITIALIZED_LIBS = initializeLibs(); static boolean initializeLibs() { + System.loadLibrary(SwiftLibraries.LIB_NAME_SWIFT_JAVA); System.loadLibrary(LIB_NAME); return true; } diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift index 6bd5cac69..8005485d2 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift @@ -162,7 +162,7 @@ extension JNISwift2JavaGenerator { // If the underlying translated method requires // a SwiftArena, we pass in the global arena if translatedDecl.translatedFunctionSignature.requiresSwiftArena { - upcallArguments.append("JNI.shared.defaultAutoArena") + upcallArguments.append("JavaSwiftArena.defaultAutoArena") } let tryClause = function.originalFunctionSignature.isThrowing ? "try " : "" @@ -201,8 +201,6 @@ extension JNISwift2JavaGenerator { private func printGlobalSwiftThunkSources(_ printer: inout CodePrinter) throws { printHeader(&printer) - printJNIOnLoad(&printer) - for decl in analysis.importedGlobalFuncs { printSwiftFunctionThunk(&printer, decl) printer.println() @@ -214,18 +212,6 @@ extension JNISwift2JavaGenerator { } } - private func printJNIOnLoad(_ printer: inout CodePrinter) { - printer.print( - """ - @_cdecl("JNI_OnLoad") - func JNI_OnLoad(javaVM: JavaVMPointer, reserved: UnsafeMutableRawPointer) -> jint { - SwiftJavaRuntimeSupport._JNI_OnLoad(javaVM, reserved) - return JNI_VERSION_1_6 - } - """ - ) - } - private func printNominalTypeThunks(_ printer: inout CodePrinter, _ type: ImportedNominalType) throws { printHeader(&printer) diff --git a/Sources/SwiftJava/AnyJavaObject.swift b/Sources/SwiftJava/AnyJavaObject.swift index dd32ef34d..6106dbf71 100644 --- a/Sources/SwiftJava/AnyJavaObject.swift +++ b/Sources/SwiftJava/AnyJavaObject.swift @@ -102,23 +102,41 @@ extension AnyJavaObject { in environment: JNIEnvironment, _ body: (jclass) throws -> Result ) throws -> Result { - let resolvedClass = try environment.translatingJNIExceptions { - environment.interface.FindClass( - environment, - fullJavaClassNameWithSlashes - ) - }! - return try body(resolvedClass) + do { + let resolvedClass = try environment.translatingJNIExceptions { + environment.interface.FindClass( + environment, + fullJavaClassNameWithSlashes + ) + }! + return try body(resolvedClass) + } catch { + // If we are in a Java environment where we have loaded + // SwiftJava dynamically, we have access to the application class loader + // so lets try that as as a fallback + if let applicationClassLoader = JNI.shared?.applicationClassLoader { + return try _withJNIClassFromCustomClassLoader( + applicationClassLoader, + in: environment + ) { applicationLoadedClass in + return try body(applicationLoadedClass) + } + } else { + throw error + } + } } /// Retrieve the Java class for this type using a specific class loader. private static func _withJNIClassFromCustomClassLoader( _ classLoader: JavaClassLoader, in environment: JNIEnvironment, - _ body: (jclass?) throws -> Result + _ body: (jclass) throws -> Result ) throws -> Result { - let resolvedClass = try? classLoader.findClass(fullJavaClassName) - return try body(resolvedClass?.javaThis) + let resolvedClass = try classLoader.findClass(fullJavaClassName) + // OK to force unwrap, as classLoader will throw ClassNotFoundException + // if the class cannot be found. + return try body(resolvedClass!.javaThis) } /// Retrieve the Java class for this type and execute body(). @@ -129,16 +147,15 @@ extension AnyJavaObject { ) throws -> Result { if let AnyJavaObjectWithCustomClassLoader = self as? AnyJavaObjectWithCustomClassLoader.Type, let customClassLoader = try AnyJavaObjectWithCustomClassLoader.getJavaClassLoader(in: environment) { - try _withJNIClassFromCustomClassLoader(customClassLoader, in: environment) { clazz in - guard let clazz else { - // If the custom class loader did not find the class - // let's look in the default class loader. - return try _withJNIClassFromDefaultClassLoader(in: environment, body) + do { + return try _withJNIClassFromCustomClassLoader(customClassLoader, in: environment) { clazz in + return try body(clazz) } - return try body(clazz) + } catch { + return try _withJNIClassFromDefaultClassLoader(in: environment, body) } } else { - try _withJNIClassFromDefaultClassLoader(in: environment, body) + return try _withJNIClassFromDefaultClassLoader(in: environment, body) } } } diff --git a/Sources/SwiftJavaRuntimeSupport/JNI.swift b/Sources/SwiftJava/JNI.swift similarity index 53% rename from Sources/SwiftJavaRuntimeSupport/JNI.swift rename to Sources/SwiftJava/JNI.swift index 4eac2242b..161c520ae 100644 --- a/Sources/SwiftJavaRuntimeSupport/JNI.swift +++ b/Sources/SwiftJava/JNI.swift @@ -12,7 +12,6 @@ // //===----------------------------------------------------------------------===// -import SwiftJava import CSwiftJavaJNI /// A type that represents the shared JNI environment @@ -21,36 +20,28 @@ import CSwiftJavaJNI /// This is initialized when the `JNI_OnLoad` is triggered, /// which happens when you call `System.loadLibrary(...)` /// from Java. -public final class JNI { +package final class JNI { /// The shared JNI object, initialized by `JNI_OnLoad` - public fileprivate(set) static var shared: JNI! + /// + /// This may be `nil` in the case where `SwiftJava` is not loaded as a dynamic lib + /// by the Java sources. + package fileprivate(set) static var shared: JNI? /// The default application class loader - public let applicationClassLoader: JavaClassLoader - - /// The default auto arena of SwiftKitCore - public let defaultAutoArena: JavaSwiftArena + package let applicationClassLoader: JavaClassLoader init(fromVM javaVM: JavaVirtualMachine) { + // Update the global JavaVM + JavaVirtualMachine.sharedJVM.withLock { + $0 = javaVM + } let environment = try! javaVM.environment() - self.applicationClassLoader = try! JavaClass(environment: environment).currentThread().getContextClassLoader() - - // Find global arena - let swiftMemoryClass = environment.interface.FindClass(environment, "org/swift/swiftkit/core/SwiftMemoryManagement")! - let arenaFieldID = environment.interface.GetStaticFieldID( - environment, - swiftMemoryClass, - "DEFAULT_SWIFT_JAVA_AUTO_ARENA", - JavaSwiftArena.mangledName - ) - let localObject = environment.interface.GetStaticObjectField(environment, swiftMemoryClass, arenaFieldID)! - self.defaultAutoArena = JavaSwiftArena(javaThis: localObject, environment: environment) - environment.interface.DeleteLocalRef(environment, localObject) } } -// Called by generated code, and not automatically by Java. -public func _JNI_OnLoad(_ javaVM: JavaVMPointer, _ reserved: UnsafeMutableRawPointer) { +@_cdecl("JNI_OnLoad") +func SwiftJava_JNI_OnLoad(javaVM: JavaVMPointer, reserved: UnsafeMutableRawPointer) -> jint { JNI.shared = JNI(fromVM: JavaVirtualMachine(adoptingJVM: javaVM)) + return JNI_VERSION_1_6 } diff --git a/Sources/SwiftJava/JVM/JavaVirtualMachine.swift b/Sources/SwiftJava/JVM/JavaVirtualMachine.swift index bb574c8ad..a68897c05 100644 --- a/Sources/SwiftJava/JVM/JavaVirtualMachine.swift +++ b/Sources/SwiftJava/JVM/JavaVirtualMachine.swift @@ -220,7 +220,7 @@ extension JavaVirtualMachine { /// TODO: If the use of the lock itself ends up being slow, we could /// use an atomic here instead because our access pattern is fairly /// simple. - private static let sharedJVM: LockedState = .init(initialState: nil) + static let sharedJVM: LockedState = .init(initialState: nil) /// Access the shared Java Virtual Machine instance. /// diff --git a/Sources/SwiftJavaRuntimeSupport/_JNIMethodIDCache.swift b/Sources/SwiftJavaRuntimeSupport/_JNIMethodIDCache.swift index fbadf1916..54c3178e5 100644 --- a/Sources/SwiftJavaRuntimeSupport/_JNIMethodIDCache.swift +++ b/Sources/SwiftJavaRuntimeSupport/_JNIMethodIDCache.swift @@ -55,7 +55,8 @@ public final class _JNIMethodIDCache: Sendable { // Clear any ClassNotFound exceptions from FindClass environment.interface.ExceptionClear(environment) - if let javaClass = try? JNI.shared.applicationClassLoader.loadClass( + // OK to force unwrap, we are in a jextract environment. + if let javaClass = try? JNI.shared!.applicationClassLoader.loadClass( className.replacingOccurrences(of: "/", with: ".") ) { clazz = javaClass.javaThis diff --git a/Sources/SwiftJavaRuntimeSupport/generated/JavaJNISwiftInstance.swift b/Sources/SwiftJavaRuntimeSupport/generated/JavaJNISwiftInstance.swift index fe413d505..5efc4245e 100644 --- a/Sources/SwiftJavaRuntimeSupport/generated/JavaJNISwiftInstance.swift +++ b/Sources/SwiftJavaRuntimeSupport/generated/JavaJNISwiftInstance.swift @@ -20,6 +20,7 @@ public struct JavaJNISwiftInstance: AnyJavaObjectWithCustomClassLoader { public func memoryAddress() -> Int64 public static func getJavaClassLoader(in environment: JNIEnvironment) throws -> JavaClassLoader! { - JNI.shared.applicationClassLoader + // OK to force unwrap, we are in a jextract environment. + JNI.shared!.applicationClassLoader } } diff --git a/Sources/SwiftJavaRuntimeSupport/generated/JavaSwiftArena.swift b/Sources/SwiftJavaRuntimeSupport/generated/JavaSwiftArena.swift index 98abbe0b0..2fbe6b30a 100644 --- a/Sources/SwiftJavaRuntimeSupport/generated/JavaSwiftArena.swift +++ b/Sources/SwiftJavaRuntimeSupport/generated/JavaSwiftArena.swift @@ -16,3 +16,11 @@ import SwiftJava @JavaInterface("org.swift.swiftkit.core.SwiftArena") public struct JavaSwiftArena {} + +extension JavaSwiftArena { + /// A cache for the default auto arena found in SwiftKitCore + public static internal(set) var defaultAutoArena: JavaSwiftArena = { + let swiftMemoryClass = try! JavaClass() + return swiftMemoryClass.defaultSwiftJavaAutoArena + }() +} diff --git a/Sources/SwiftJavaRuntimeSupport/generated/JavaSwiftMemoryManagement.swift b/Sources/SwiftJavaRuntimeSupport/generated/JavaSwiftMemoryManagement.swift new file mode 100644 index 000000000..236d88616 --- /dev/null +++ b/Sources/SwiftJavaRuntimeSupport/generated/JavaSwiftMemoryManagement.swift @@ -0,0 +1,23 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift.org project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift.org project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import SwiftJava + +@JavaClass("org.swift.swiftkit.core.SwiftMemoryManagement") +public class JavaSwiftMemoryManagement: JavaObject {} + +extension JavaClass { + @JavaStaticField("DEFAULT_SWIFT_JAVA_AUTO_ARENA", isFinal: true) + var defaultSwiftJavaAutoArena: JavaSwiftArena! +} diff --git a/SwiftKitCore/src/main/java/org/swift/swiftkit/core/SwiftLibraries.java b/SwiftKitCore/src/main/java/org/swift/swiftkit/core/SwiftLibraries.java index 7eccde749..2a138a408 100644 --- a/SwiftKitCore/src/main/java/org/swift/swiftkit/core/SwiftLibraries.java +++ b/SwiftKitCore/src/main/java/org/swift/swiftkit/core/SwiftLibraries.java @@ -28,6 +28,7 @@ public final class SwiftLibraries { public static final String LIB_NAME_SWIFT_CORE = "swiftCore"; public static final String LIB_NAME_SWIFT_CONCURRENCY = "swift_Concurrency"; public static final String LIB_NAME_SWIFT_RUNTIME_FUNCTIONS = "SwiftRuntimeFunctions"; + public static final String LIB_NAME_SWIFT_JAVA = "SwiftJava"; /** * Allows for configuration if jextracted types should automatically attempt to load swiftCore and the library type is from. @@ -44,6 +45,7 @@ public final class SwiftLibraries { public static boolean loadLibraries(boolean loadSwiftRuntimeFunctions) { System.loadLibrary(LIB_NAME_SWIFT_CORE); + System.loadLibrary(LIB_NAME_SWIFT_JAVA); if (loadSwiftRuntimeFunctions) { System.loadLibrary(LIB_NAME_SWIFT_RUNTIME_FUNCTIONS); } diff --git a/Tests/JExtractSwiftTests/JNI/JNIClassTests.swift b/Tests/JExtractSwiftTests/JNI/JNIClassTests.swift index c3102a623..673dc2182 100644 --- a/Tests/JExtractSwiftTests/JNI/JNIClassTests.swift +++ b/Tests/JExtractSwiftTests/JNI/JNIClassTests.swift @@ -66,6 +66,7 @@ struct JNIClassTests { @SuppressWarnings("unused") private static final boolean INITIALIZED_LIBS = initializeLibs(); static boolean initializeLibs() { + System.loadLibrary(SwiftLibraries.LIB_NAME_SWIFT_JAVA); System.loadLibrary(LIB_NAME); return true; } diff --git a/Tests/JExtractSwiftTests/JNI/JNIEnumTests.swift b/Tests/JExtractSwiftTests/JNI/JNIEnumTests.swift index 2188bcd62..609c0bfbc 100644 --- a/Tests/JExtractSwiftTests/JNI/JNIEnumTests.swift +++ b/Tests/JExtractSwiftTests/JNI/JNIEnumTests.swift @@ -50,6 +50,7 @@ struct JNIEnumTests { @SuppressWarnings("unused") private static final boolean INITIALIZED_LIBS = initializeLibs(); static boolean initializeLibs() { + System.loadLibrary(SwiftLibraries.LIB_NAME_SWIFT_JAVA); System.loadLibrary(LIB_NAME); return true; } diff --git a/Tests/JExtractSwiftTests/JNI/JNIModuleTests.swift b/Tests/JExtractSwiftTests/JNI/JNIModuleTests.swift index dddf11478..30ecc0e26 100644 --- a/Tests/JExtractSwiftTests/JNI/JNIModuleTests.swift +++ b/Tests/JExtractSwiftTests/JNI/JNIModuleTests.swift @@ -53,6 +53,7 @@ struct JNIModuleTests { static final String LIB_NAME = "SwiftModule"; static { + System.loadLibrary(SwiftLibraries.LIB_NAME_SWIFT_JAVA); System.loadLibrary(LIB_NAME); } """ diff --git a/Tests/JExtractSwiftTests/JNI/JNIProtocolTests.swift b/Tests/JExtractSwiftTests/JNI/JNIProtocolTests.swift index a15fdb2d1..8bd5b34a6 100644 --- a/Tests/JExtractSwiftTests/JNI/JNIProtocolTests.swift +++ b/Tests/JExtractSwiftTests/JNI/JNIProtocolTests.swift @@ -307,7 +307,7 @@ struct JNIProtocolTests { let cClass = try! JavaClass(environment: JavaVirtualMachine.shared().environment()) let cPointer = UnsafeMutablePointer.allocate(capacity: 1) cPointer.initialize(to: c) - guard let unwrapped$ = _javaSomeProtocolInterface.withObject(cClass.wrapMemoryAddressUnsafe(Int64(Int(bitPattern: cPointer))), JNI.shared.defaultAutoArena) else { + guard let unwrapped$ = _javaSomeProtocolInterface.withObject(cClass.wrapMemoryAddressUnsafe(Int64(Int(bitPattern: cPointer))), JavaSwiftArena.defaultAutoArena) else { fatalError("Upcall to withObject unexpectedly returned nil") } let result$MemoryAddress$ = unwrapped$.as(JavaJNISwiftInstance.self)!.memoryAddress() diff --git a/Tests/JExtractSwiftTests/JNI/JNIStructTests.swift b/Tests/JExtractSwiftTests/JNI/JNIStructTests.swift index f8830b644..58dfc6be4 100644 --- a/Tests/JExtractSwiftTests/JNI/JNIStructTests.swift +++ b/Tests/JExtractSwiftTests/JNI/JNIStructTests.swift @@ -53,6 +53,7 @@ struct JNIStructTests { @SuppressWarnings("unused") private static final boolean INITIALIZED_LIBS = initializeLibs(); static boolean initializeLibs() { + System.loadLibrary(SwiftLibraries.LIB_NAME_SWIFT_JAVA); System.loadLibrary(LIB_NAME); return true; }