From 7cc73593c62c90cb8e640fbeb986431e32c9961e Mon Sep 17 00:00:00 2001 From: Konrad Malawski Date: Fri, 19 Dec 2025 18:49:11 +0900 Subject: [PATCH 1/3] Revert "make `SwiftJava` a dynamic lib and add class lookup fallbacks (#493)" This reverts commit 0685f550d8c509fc60bbd605bed2c581ff2701dc. --- Package.swift | 1 - .../SwiftAndJavaJarSampleLib/Package.swift | 4 +- .../FFM/FFMSwift2JavaGenerator.swift | 2 - ...t2JavaGenerator+JavaBindingsPrinting.swift | 2 - ...ift2JavaGenerator+SwiftThunkPrinting.swift | 16 +++++- Sources/SwiftJava/AnyJavaObject.swift | 51 +++++++------------ .../SwiftJava/JVM/JavaVirtualMachine.swift | 2 +- .../JNI.swift | 35 ++++++++----- .../_JNIMethodIDCache.swift | 3 +- .../generated/JavaJNISwiftInstance.swift | 3 +- .../generated/JavaSwiftArena.swift | 8 --- .../generated/JavaSwiftMemoryManagement.swift | 23 --------- .../swift/swiftkit/core/SwiftLibraries.java | 2 - .../JNI/JNIClassTests.swift | 1 - .../JExtractSwiftTests/JNI/JNIEnumTests.swift | 1 - .../JNI/JNIModuleTests.swift | 1 - .../JNI/JNIProtocolTests.swift | 2 +- .../JNI/JNIStructTests.swift | 1 - 18 files changed, 59 insertions(+), 99 deletions(-) rename Sources/{SwiftJava => SwiftJavaRuntimeSupport}/JNI.swift (53%) delete mode 100644 Sources/SwiftJavaRuntimeSupport/generated/JavaSwiftMemoryManagement.swift diff --git a/Package.swift b/Package.swift index b6a18f90e..2c56a2870 100644 --- a/Package.swift +++ b/Package.swift @@ -100,7 +100,6 @@ 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 f501b984e..32ffbb287 100644 --- a/Samples/SwiftAndJavaJarSampleLib/Package.swift +++ b/Samples/SwiftAndJavaJarSampleLib/Package.swift @@ -63,9 +63,7 @@ let package = Package( .target( name: "MySwiftLibrary", dependencies: [ - .product(name: "SwiftJava", package: "swift-java"), - .product(name: "CSwiftJavaJNI", package: "swift-java"), - .product(name: "SwiftRuntimeFunctions", 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 a16ac8a89..a0f68ee00 100644 --- a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift +++ b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift @@ -195,7 +195,6 @@ 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; @@ -346,7 +345,6 @@ 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 28269ecd8..78c91376d 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift @@ -99,7 +99,6 @@ extension JNISwift2JavaGenerator { static final String LIB_NAME = "\(swiftModuleName)"; static { - System.loadLibrary(SwiftLibraries.LIB_NAME_SWIFT_JAVA); System.loadLibrary(LIB_NAME); } """ @@ -170,7 +169,6 @@ 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 8005485d2..6bd5cac69 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("JavaSwiftArena.defaultAutoArena") + upcallArguments.append("JNI.shared.defaultAutoArena") } let tryClause = function.originalFunctionSignature.isThrowing ? "try " : "" @@ -201,6 +201,8 @@ extension JNISwift2JavaGenerator { private func printGlobalSwiftThunkSources(_ printer: inout CodePrinter) throws { printHeader(&printer) + printJNIOnLoad(&printer) + for decl in analysis.importedGlobalFuncs { printSwiftFunctionThunk(&printer, decl) printer.println() @@ -212,6 +214,18 @@ 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 6106dbf71..dd32ef34d 100644 --- a/Sources/SwiftJava/AnyJavaObject.swift +++ b/Sources/SwiftJava/AnyJavaObject.swift @@ -102,41 +102,23 @@ extension AnyJavaObject { in environment: JNIEnvironment, _ body: (jclass) throws -> Result ) throws -> Result { - 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 - } - } + let resolvedClass = try environment.translatingJNIExceptions { + environment.interface.FindClass( + environment, + fullJavaClassNameWithSlashes + ) + }! + return try body(resolvedClass) } /// 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) - // OK to force unwrap, as classLoader will throw ClassNotFoundException - // if the class cannot be found. - return try body(resolvedClass!.javaThis) + let resolvedClass = try? classLoader.findClass(fullJavaClassName) + return try body(resolvedClass?.javaThis) } /// Retrieve the Java class for this type and execute body(). @@ -147,15 +129,16 @@ extension AnyJavaObject { ) throws -> Result { if let AnyJavaObjectWithCustomClassLoader = self as? AnyJavaObjectWithCustomClassLoader.Type, let customClassLoader = try AnyJavaObjectWithCustomClassLoader.getJavaClassLoader(in: environment) { - do { - return try _withJNIClassFromCustomClassLoader(customClassLoader, in: environment) { clazz in - return try body(clazz) + 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) } - } catch { - return try _withJNIClassFromDefaultClassLoader(in: environment, body) + return try body(clazz) } } else { - return try _withJNIClassFromDefaultClassLoader(in: environment, body) + try _withJNIClassFromDefaultClassLoader(in: environment, body) } } } diff --git a/Sources/SwiftJava/JVM/JavaVirtualMachine.swift b/Sources/SwiftJava/JVM/JavaVirtualMachine.swift index a68897c05..bb574c8ad 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. - static let sharedJVM: LockedState = .init(initialState: nil) + private static let sharedJVM: LockedState = .init(initialState: nil) /// Access the shared Java Virtual Machine instance. /// diff --git a/Sources/SwiftJava/JNI.swift b/Sources/SwiftJavaRuntimeSupport/JNI.swift similarity index 53% rename from Sources/SwiftJava/JNI.swift rename to Sources/SwiftJavaRuntimeSupport/JNI.swift index 161c520ae..4eac2242b 100644 --- a/Sources/SwiftJava/JNI.swift +++ b/Sources/SwiftJavaRuntimeSupport/JNI.swift @@ -12,6 +12,7 @@ // //===----------------------------------------------------------------------===// +import SwiftJava import CSwiftJavaJNI /// A type that represents the shared JNI environment @@ -20,28 +21,36 @@ import CSwiftJavaJNI /// This is initialized when the `JNI_OnLoad` is triggered, /// which happens when you call `System.loadLibrary(...)` /// from Java. -package final class JNI { +public final class JNI { /// The shared JNI object, initialized by `JNI_OnLoad` - /// - /// 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? + public fileprivate(set) static var shared: JNI! /// The default application class loader - package let applicationClassLoader: JavaClassLoader + public let applicationClassLoader: JavaClassLoader + + /// The default auto arena of SwiftKitCore + public let defaultAutoArena: JavaSwiftArena 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) } } -@_cdecl("JNI_OnLoad") -func SwiftJava_JNI_OnLoad(javaVM: JavaVMPointer, reserved: UnsafeMutableRawPointer) -> jint { +// Called by generated code, and not automatically by Java. +public func _JNI_OnLoad(_ javaVM: JavaVMPointer, _ reserved: UnsafeMutableRawPointer) { JNI.shared = JNI(fromVM: JavaVirtualMachine(adoptingJVM: javaVM)) - return JNI_VERSION_1_6 } diff --git a/Sources/SwiftJavaRuntimeSupport/_JNIMethodIDCache.swift b/Sources/SwiftJavaRuntimeSupport/_JNIMethodIDCache.swift index 54c3178e5..fbadf1916 100644 --- a/Sources/SwiftJavaRuntimeSupport/_JNIMethodIDCache.swift +++ b/Sources/SwiftJavaRuntimeSupport/_JNIMethodIDCache.swift @@ -55,8 +55,7 @@ public final class _JNIMethodIDCache: Sendable { // Clear any ClassNotFound exceptions from FindClass environment.interface.ExceptionClear(environment) - // OK to force unwrap, we are in a jextract environment. - if let javaClass = try? JNI.shared!.applicationClassLoader.loadClass( + 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 5efc4245e..fe413d505 100644 --- a/Sources/SwiftJavaRuntimeSupport/generated/JavaJNISwiftInstance.swift +++ b/Sources/SwiftJavaRuntimeSupport/generated/JavaJNISwiftInstance.swift @@ -20,7 +20,6 @@ public struct JavaJNISwiftInstance: AnyJavaObjectWithCustomClassLoader { public func memoryAddress() -> Int64 public static func getJavaClassLoader(in environment: JNIEnvironment) throws -> JavaClassLoader! { - // OK to force unwrap, we are in a jextract environment. - JNI.shared!.applicationClassLoader + JNI.shared.applicationClassLoader } } diff --git a/Sources/SwiftJavaRuntimeSupport/generated/JavaSwiftArena.swift b/Sources/SwiftJavaRuntimeSupport/generated/JavaSwiftArena.swift index 2fbe6b30a..98abbe0b0 100644 --- a/Sources/SwiftJavaRuntimeSupport/generated/JavaSwiftArena.swift +++ b/Sources/SwiftJavaRuntimeSupport/generated/JavaSwiftArena.swift @@ -16,11 +16,3 @@ 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 deleted file mode 100644 index 236d88616..000000000 --- a/Sources/SwiftJavaRuntimeSupport/generated/JavaSwiftMemoryManagement.swift +++ /dev/null @@ -1,23 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 2a138a408..7eccde749 100644 --- a/SwiftKitCore/src/main/java/org/swift/swiftkit/core/SwiftLibraries.java +++ b/SwiftKitCore/src/main/java/org/swift/swiftkit/core/SwiftLibraries.java @@ -28,7 +28,6 @@ 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. @@ -45,7 +44,6 @@ 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 673dc2182..c3102a623 100644 --- a/Tests/JExtractSwiftTests/JNI/JNIClassTests.swift +++ b/Tests/JExtractSwiftTests/JNI/JNIClassTests.swift @@ -66,7 +66,6 @@ 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 609c0bfbc..2188bcd62 100644 --- a/Tests/JExtractSwiftTests/JNI/JNIEnumTests.swift +++ b/Tests/JExtractSwiftTests/JNI/JNIEnumTests.swift @@ -50,7 +50,6 @@ 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 30ecc0e26..dddf11478 100644 --- a/Tests/JExtractSwiftTests/JNI/JNIModuleTests.swift +++ b/Tests/JExtractSwiftTests/JNI/JNIModuleTests.swift @@ -53,7 +53,6 @@ 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 8bd5b34a6..a15fdb2d1 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))), JavaSwiftArena.defaultAutoArena) else { + guard let unwrapped$ = _javaSomeProtocolInterface.withObject(cClass.wrapMemoryAddressUnsafe(Int64(Int(bitPattern: cPointer))), JNI.shared.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 58dfc6be4..f8830b644 100644 --- a/Tests/JExtractSwiftTests/JNI/JNIStructTests.swift +++ b/Tests/JExtractSwiftTests/JNI/JNIStructTests.swift @@ -53,7 +53,6 @@ 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; } From 4f885a32317b106a92b8f7b58a1328b95c443312 Mon Sep 17 00:00:00 2001 From: Konrad Malawski Date: Fri, 19 Dec 2025 18:49:19 +0900 Subject: [PATCH 2/3] Revert "Cleanup: Initializers come before member methods (#488)" This reverts commit 4fb6ca2e5efaeaa8116ab4a947c04ba37e95aaa5. --- .../Sources/MySwiftLibrary/ConcreteProtocolAB.swift | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/ConcreteProtocolAB.swift b/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/ConcreteProtocolAB.swift index 4f249609a..d83fbb28d 100644 --- a/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/ConcreteProtocolAB.swift +++ b/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/ConcreteProtocolAB.swift @@ -17,11 +17,6 @@ public class ConcreteProtocolAB: ProtocolA, ProtocolB { public let constantB: Int64 public var mutable: Int64 = 0 - public init(constantA: Int64, constantB: Int64) { - self.constantA = constantA - self.constantB = constantB - } - public func name() -> String { return "ConcreteProtocolAB" } @@ -29,4 +24,9 @@ public class ConcreteProtocolAB: ProtocolA, ProtocolB { public func makeClass() -> MySwiftClass { return MySwiftClass(x: 10, y: 50) } + + public init(constantA: Int64, constantB: Int64) { + self.constantA = constantA + self.constantB = constantB + } } From ed7fa8eb25b6b24e15caf48c93fb7241aaba4e4a Mon Sep 17 00:00:00 2001 From: Konrad Malawski Date: Fri, 19 Dec 2025 18:49:25 +0900 Subject: [PATCH 3/3] Revert "jextract: pass arena from Swift instead of default interface for callbacks (#489)" This reverts commit acff3eed150ec13f96909fdc468f18a7f5e1b5fb. --- .../java/com/example/swift/ProtocolTest.java | 2 +- ...Generator+InterfaceWrapperGeneration.swift | 4 +-- ...t2JavaGenerator+JavaBindingsPrinting.swift | 14 ++++++-- ...ISwift2JavaGenerator+JavaTranslation.swift | 4 --- ...ift2JavaGenerator+SwiftThunkPrinting.swift | 21 +++--------- Sources/SwiftJava/AnyJavaObject.swift | 5 --- Sources/SwiftJavaRuntimeSupport/JNI.swift | 33 +++---------------- .../generated/JavaSwiftArena.swift | 18 ---------- .../SwiftJavaRuntimeSupport/swift-java.config | 6 ---- .../swiftkit/core/SwiftMemoryManagement.java | 2 +- .../JNI/JNIProtocolTests.swift | 24 +++++++++++++- .../MemoryManagementModeTests.swift | 4 +-- 12 files changed, 47 insertions(+), 90 deletions(-) delete mode 100644 Sources/SwiftJavaRuntimeSupport/generated/JavaSwiftArena.swift delete mode 100644 Sources/SwiftJavaRuntimeSupport/swift-java.config diff --git a/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/ProtocolTest.java b/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/ProtocolTest.java index 42683ac96..b8159b8ad 100644 --- a/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/ProtocolTest.java +++ b/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/ProtocolTest.java @@ -77,7 +77,7 @@ void protocolMethod() { void protocolClassMethod() { try (var arena = SwiftArena.ofConfined()) { ProtocolA proto1 = ConcreteProtocolAB.init(10, 5, arena); - assertEquals(10, proto1.makeClass(arena).getX()); + assertEquals(10, proto1.makeClass().getX()); } } diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+InterfaceWrapperGeneration.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+InterfaceWrapperGeneration.swift index e5ba671d8..abf67f3e3 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+InterfaceWrapperGeneration.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+InterfaceWrapperGeneration.swift @@ -42,7 +42,6 @@ extension JNISwift2JavaGenerator { let protocolType: SwiftNominalType let functions: [Function] let variables: [Variable] - let importedType: ImportedNominalType var wrapperName: String { protocolType.nominalTypeDecl.javaInterfaceSwiftProtocolWrapperName @@ -100,8 +99,7 @@ extension JNISwift2JavaGenerator { return JavaInterfaceSwiftWrapper( protocolType: SwiftNominalType(nominalTypeDecl: type.swiftNominal), functions: functions, - variables: variables, - importedType: type + variables: variables ) } diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift index 78c91376d..bfa2ff12d 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift @@ -214,7 +214,7 @@ extension JNISwift2JavaGenerator { } public static \(decl.swiftNominal.name) wrapMemoryAddressUnsafe(long selfPointer) { - return new \(decl.swiftNominal.name)(selfPointer, SwiftMemoryManagement.DEFAULT_SWIFT_JAVA_AUTO_ARENA); + return new \(decl.swiftNominal.name)(selfPointer, SwiftMemoryManagement.GLOBAL_SWIFT_JAVA_ARENA); } """ ) @@ -531,9 +531,17 @@ extension JNISwift2JavaGenerator { // If we have enabled javaCallbacks we must emit default // arena methods for protocols, as this is what // Swift will call into, when you call a interface from Swift. - let shouldGenerateGlobalArenaVariation = config.effectiveMemoryManagementMode.requiresGlobalArena && translatedSignature.requiresSwiftArena + let shouldGenerateGlobalArenaVariation: Bool let isParentProtocol = importedFunc?.parentType?.asNominalType?.isProtocol ?? false + if config.effectiveMemoryManagementMode.requiresGlobalArena && translatedSignature.requiresSwiftArena { + shouldGenerateGlobalArenaVariation = true + } else if isParentProtocol, translatedSignature.requiresSwiftArena, config.effectiveEnableJavaCallbacks { + shouldGenerateGlobalArenaVariation = true + } else { + shouldGenerateGlobalArenaVariation = false + } + if shouldGenerateGlobalArenaVariation { if let importedFunc { printDeclDocumentation(&printer, importedFunc) @@ -546,7 +554,7 @@ extension JNISwift2JavaGenerator { } printer.printBraceBlock("\(annotationsStr)\(modifiers.joined(separator: " ")) \(resultType) \(translatedDecl.name)(\(parametersStr))\(throwsClause)") { printer in - let globalArenaName = "SwiftMemoryManagement.DEFAULT_SWIFT_JAVA_AUTO_ARENA" + let globalArenaName = "SwiftMemoryManagement.GLOBAL_SWIFT_JAVA_ARENA" let arguments = translatedDecl.translatedFunctionSignature.parameters.map(\.parameter.name) + [globalArenaName] let call = "\(translatedDecl.name)(\(arguments.joined(separator: ", ")))" if translatedDecl.translatedFunctionSignature.resultType.javaType.isVoid { diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift index c6f9713ba..a9369ccd7 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift @@ -1317,9 +1317,5 @@ extension JNISwift2JavaGenerator { // FIXME: Remove once we support protocol variables case protocolVariablesNotSupported - - /// We cannot generate interface wrappers for - /// protocols that we unable to be jextracted. - case protocolWasNotExtracted } } diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift index 6bd5cac69..99bd3dff0 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift @@ -124,9 +124,9 @@ extension JNISwift2JavaGenerator { printer.print("var \(translatedWrapper.javaInterfaceVariableName): \(translatedWrapper.javaInterfaceName) { get }") } printer.println() - try printer.printBraceBlock("extension \(translatedWrapper.wrapperName)") { printer in + printer.printBraceBlock("extension \(translatedWrapper.wrapperName)") { printer in for function in translatedWrapper.functions { - try printInterfaceWrapperFunctionImpl(&printer, function, inside: translatedWrapper) + printInterfaceWrapperFunctionImpl(&printer, function, inside: translatedWrapper) printer.println() } @@ -142,16 +142,9 @@ extension JNISwift2JavaGenerator { _ printer: inout CodePrinter, _ function: JavaInterfaceSwiftWrapper.Function, inside wrapper: JavaInterfaceSwiftWrapper - ) throws { - guard let protocolMethod = wrapper.importedType.methods.first(where: { $0.functionSignature == function.originalFunctionSignature }) else { - fatalError("Failed to find protocol method") - } - guard let translatedDecl = self.translatedDecl(for: protocolMethod) else { - throw JavaTranslationError.protocolWasNotExtracted - } - + ) { printer.printBraceBlock(function.swiftDecl.signatureString) { printer in - var upcallArguments = zip( + let upcallArguments = zip( function.originalFunctionSignature.parameters, function.parameterConversions ).map { param, conversion in @@ -159,12 +152,6 @@ extension JNISwift2JavaGenerator { conversion.render(&printer, param.parameterName!) } - // If the underlying translated method requires - // a SwiftArena, we pass in the global arena - if translatedDecl.translatedFunctionSignature.requiresSwiftArena { - upcallArguments.append("JNI.shared.defaultAutoArena") - } - let tryClause = function.originalFunctionSignature.isThrowing ? "try " : "" let javaUpcall = "\(tryClause)\(wrapper.javaInterfaceVariableName).\(function.swiftFunctionName)(\(upcallArguments.joined(separator: ", ")))" diff --git a/Sources/SwiftJava/AnyJavaObject.swift b/Sources/SwiftJava/AnyJavaObject.swift index dd32ef34d..ef0832b92 100644 --- a/Sources/SwiftJava/AnyJavaObject.swift +++ b/Sources/SwiftJava/AnyJavaObject.swift @@ -77,11 +77,6 @@ extension AnyJavaObject { return String(seq) } - /// The mangled name for this java class - public static var mangledName: String { - "L\(fullJavaClassNameWithSlashes);" - } - /// Initialize a Java object from its instance. public init(javaThis: jobject, environment: JNIEnvironment) { self.init(javaHolder: JavaObjectHolder(object: javaThis, environment: environment)) diff --git a/Sources/SwiftJavaRuntimeSupport/JNI.swift b/Sources/SwiftJavaRuntimeSupport/JNI.swift index 4eac2242b..b6f0117df 100644 --- a/Sources/SwiftJavaRuntimeSupport/JNI.swift +++ b/Sources/SwiftJavaRuntimeSupport/JNI.swift @@ -15,38 +15,13 @@ import SwiftJava import CSwiftJavaJNI -/// A type that represents the shared JNI environment -/// used to share any global JNI variables. -/// -/// This is initialized when the `JNI_OnLoad` is triggered, -/// which happens when you call `System.loadLibrary(...)` -/// from Java. -public final class JNI { - /// The shared JNI object, initialized by `JNI_OnLoad` - public fileprivate(set) static var shared: JNI! +final class JNI { + static var shared: JNI! - /// The default application class loader - public let applicationClassLoader: JavaClassLoader - - /// The default auto arena of SwiftKitCore - public let defaultAutoArena: JavaSwiftArena + let applicationClassLoader: JavaClassLoader init(fromVM javaVM: JavaVirtualMachine) { - 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) + self.applicationClassLoader = try! JavaClass(environment: javaVM.environment()).currentThread().getContextClassLoader() } } diff --git a/Sources/SwiftJavaRuntimeSupport/generated/JavaSwiftArena.swift b/Sources/SwiftJavaRuntimeSupport/generated/JavaSwiftArena.swift deleted file mode 100644 index 98abbe0b0..000000000 --- a/Sources/SwiftJavaRuntimeSupport/generated/JavaSwiftArena.swift +++ /dev/null @@ -1,18 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 - -@JavaInterface("org.swift.swiftkit.core.SwiftArena") -public struct JavaSwiftArena {} diff --git a/Sources/SwiftJavaRuntimeSupport/swift-java.config b/Sources/SwiftJavaRuntimeSupport/swift-java.config deleted file mode 100644 index 859a7407e..000000000 --- a/Sources/SwiftJavaRuntimeSupport/swift-java.config +++ /dev/null @@ -1,6 +0,0 @@ -{ - "classes" : { - "org.swift.swiftkit.core.JNISwiftInstance" : "JavaJNISwiftInstance", - "org.swift.swiftkit.core.SwiftArena" : "JavaSwiftArena" - } -} diff --git a/SwiftKitCore/src/main/java/org/swift/swiftkit/core/SwiftMemoryManagement.java b/SwiftKitCore/src/main/java/org/swift/swiftkit/core/SwiftMemoryManagement.java index 529bec42e..2b9a12092 100644 --- a/SwiftKitCore/src/main/java/org/swift/swiftkit/core/SwiftMemoryManagement.java +++ b/SwiftKitCore/src/main/java/org/swift/swiftkit/core/SwiftMemoryManagement.java @@ -15,5 +15,5 @@ package org.swift.swiftkit.core; public class SwiftMemoryManagement { - public static final SwiftArena DEFAULT_SWIFT_JAVA_AUTO_ARENA = SwiftArena.ofAuto(); + public static final SwiftArena GLOBAL_SWIFT_JAVA_ARENA = SwiftArena.ofAuto(); } diff --git a/Tests/JExtractSwiftTests/JNI/JNIProtocolTests.swift b/Tests/JExtractSwiftTests/JNI/JNIProtocolTests.swift index a15fdb2d1..3b47fd100 100644 --- a/Tests/JExtractSwiftTests/JNI/JNIProtocolTests.swift +++ b/Tests/JExtractSwiftTests/JNI/JNIProtocolTests.swift @@ -70,6 +70,28 @@ struct JNIProtocolTests { ]) } + @Test + func emitsDefault() throws { + try assertOutput( + input: source, + config: config, + .jni, .java, + detectChunkByInitialLines: 1, + expectedChunks: [ + """ + public interface SomeProtocol { + ... + public default SomeClass withObject(SomeClass c) { + return withObject(c, SwiftMemoryManagement.GLOBAL_SWIFT_JAVA_ARENA); + } + ... + public SomeClass withObject(SomeClass c, SwiftArena swiftArena$); + ... + } + """ + ]) + } + @Test func generatesJavaClassWithExtends() throws { try assertOutput( @@ -307,7 +329,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)))) else { fatalError("Upcall to withObject unexpectedly returned nil") } let result$MemoryAddress$ = unwrapped$.as(JavaJNISwiftInstance.self)!.memoryAddress() diff --git a/Tests/JExtractSwiftTests/MemoryManagementModeTests.swift b/Tests/JExtractSwiftTests/MemoryManagementModeTests.swift index 9d5d5fcae..2228aad88 100644 --- a/Tests/JExtractSwiftTests/MemoryManagementModeTests.swift +++ b/Tests/JExtractSwiftTests/MemoryManagementModeTests.swift @@ -63,7 +63,7 @@ struct MemoryManagementModeTests { expectedChunks: [ """ public static MyClass f() { - return f(SwiftMemoryManagement.DEFAULT_SWIFT_JAVA_AUTO_ARENA); + return f(SwiftMemoryManagement.GLOBAL_SWIFT_JAVA_ARENA); } """, """ @@ -95,7 +95,7 @@ struct MemoryManagementModeTests { expectedChunks: [ """ public default MyClass f() { - return f(SwiftMemoryManagement.DEFAULT_SWIFT_JAVA_AUTO_ARENA); + return f(SwiftMemoryManagement.GLOBAL_SWIFT_JAVA_ARENA); } """, """