From cf72fce6cbac647b96b7fef5c5331b85be295c29 Mon Sep 17 00:00:00 2001 From: Mads Odgaard Date: Mon, 30 Jun 2025 12:57:54 +0200 Subject: [PATCH 1/4] add jni sample app --- .github/workflows/pull_request.yml | 1 + Samples/JExtractJNISampleApp/Package.swift | 79 +++++++ .../MySwiftLibrary/MySwiftLibrary.swift | 60 ++++++ .../Sources/MySwiftLibrary/swift-java.config | 4 + Samples/JExtractJNISampleApp/build.gradle | 195 ++++++++++++++++++ Samples/JExtractJNISampleApp/ci-validate.sh | 6 + Samples/JExtractJNISampleApp/gradlew | 1 + Samples/JExtractJNISampleApp/gradlew.bat | 1 + .../com/example/swift/HelloJava2Swift.java | 45 ++++ ...ift2JavaGenerator+SwiftThunkPrinting.swift | 2 +- .../JNI/JNISwift2JavaGenerator.swift | 108 +++++++--- .../JNI/JNIModuleTests.swift | 7 +- 12 files changed, 482 insertions(+), 27 deletions(-) create mode 100644 Samples/JExtractJNISampleApp/Package.swift create mode 100644 Samples/JExtractJNISampleApp/Sources/MySwiftLibrary/MySwiftLibrary.swift create mode 100644 Samples/JExtractJNISampleApp/Sources/MySwiftLibrary/swift-java.config create mode 100644 Samples/JExtractJNISampleApp/build.gradle create mode 100755 Samples/JExtractJNISampleApp/ci-validate.sh create mode 120000 Samples/JExtractJNISampleApp/gradlew create mode 120000 Samples/JExtractJNISampleApp/gradlew.bat create mode 100644 Samples/JExtractJNISampleApp/src/main/java/com/example/swift/HelloJava2Swift.java diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index c1af9ca7..c9d197e7 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -78,6 +78,7 @@ jobs: 'JavaSieve', 'SwiftAndJavaJarSampleLib', 'SwiftKitSampleApp', + 'JExtractJNISampleApp' ] container: image: ${{ (contains(matrix.swift_version, 'nightly') && 'swiftlang/swift') || 'swift' }}:${{ matrix.swift_version }}-${{ matrix.os_version }} diff --git a/Samples/JExtractJNISampleApp/Package.swift b/Samples/JExtractJNISampleApp/Package.swift new file mode 100644 index 00000000..8c7ce856 --- /dev/null +++ b/Samples/JExtractJNISampleApp/Package.swift @@ -0,0 +1,79 @@ +// swift-tools-version: 6.0 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import CompilerPluginSupport +import PackageDescription + +import class Foundation.FileManager +import class Foundation.ProcessInfo + +// Note: the JAVA_HOME environment variable must be set to point to where +// Java is installed, e.g., +// Library/Java/JavaVirtualMachines/openjdk-21.jdk/Contents/Home. +func findJavaHome() -> String { + if let home = ProcessInfo.processInfo.environment["JAVA_HOME"] { + return home + } + + // This is a workaround for envs (some IDEs) which have trouble with + // picking up env variables during the build process + let path = "\(FileManager.default.homeDirectoryForCurrentUser.path()).java_home" + if let home = try? String(contentsOfFile: path, encoding: .utf8) { + if let lastChar = home.last, lastChar.isNewline { + return String(home.dropLast()) + } + + return home + } + + fatalError("Please set the JAVA_HOME environment variable to point to where Java is installed.") +} +let javaHome = findJavaHome() + +let javaIncludePath = "\(javaHome)/include" +#if os(Linux) + let javaPlatformIncludePath = "\(javaIncludePath)/linux" +#elseif os(macOS) + let javaPlatformIncludePath = "\(javaIncludePath)/darwin" +#else + // TODO: Handle windows as well + #error("Currently only macOS and Linux platforms are supported, this may change in the future.") +#endif + +let package = Package( + name: "JExtractJNISampleApp", + platforms: [ + .macOS(.v15) + ], + products: [ + .library( + name: "MySwiftLibrary", + type: .dynamic, + targets: ["MySwiftLibrary"] + ) + + ], + dependencies: [ + .package(name: "swift-java", path: "../../") + ], + targets: [ + .target( + name: "MySwiftLibrary", + dependencies: [ + .product(name: "JavaKit", package: "swift-java"), + .product(name: "JavaRuntime", package: "swift-java"), + .product(name: "SwiftKitSwift", package: "swift-java"), + ], + exclude: [ + "swift-java.config" + ], + swiftSettings: [ + .swiftLanguageMode(.v5), + .unsafeFlags(["-I\(javaIncludePath)", "-I\(javaPlatformIncludePath)"]), + ], + plugins: [ + .plugin(name: "JExtractSwiftPlugin", package: "swift-java") + ] + ) + ] +) diff --git a/Samples/JExtractJNISampleApp/Sources/MySwiftLibrary/MySwiftLibrary.swift b/Samples/JExtractJNISampleApp/Sources/MySwiftLibrary/MySwiftLibrary.swift new file mode 100644 index 00000000..be04bc08 --- /dev/null +++ b/Samples/JExtractJNISampleApp/Sources/MySwiftLibrary/MySwiftLibrary.swift @@ -0,0 +1,60 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2025 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 +// +//===----------------------------------------------------------------------===// + +// This is a "plain Swift" file containing various types of declarations, +// that is exported to Java by using the `jextract-swift` tool. +// +// No annotations are necessary on the Swift side to perform the export. + +#if os(Linux) + import Glibc +#else + import Darwin.C +#endif + +public func helloWorld() { + p("\(#function)") +} + +public func globalTakeInt(i: Int64) { + p("i:\(i)") +} + +public func globalMakeInt() -> Int64 { + return 42 +} + +public func globalWriteString(string: String) -> Int64 { + return Int64(string.count) +} + +public func globalTakeIntInt(i: Int64, j: Int64) { + p("i:\(i), j:\(j)") +} + +// ==== Internal helpers + +func p(_ msg: String, file: String = #fileID, line: UInt = #line, function: String = #function) { + print("[swift][\(file):\(line)](\(function)) \(msg)") + fflush(stdout) +} + +#if os(Linux) + // FIXME: why do we need this workaround? + @_silgen_name("_objc_autoreleaseReturnValue") + public func _objc_autoreleaseReturnValue(a: Any) {} + + @_silgen_name("objc_autoreleaseReturnValue") + public func objc_autoreleaseReturnValue(a: Any) {} +#endif diff --git a/Samples/JExtractJNISampleApp/Sources/MySwiftLibrary/swift-java.config b/Samples/JExtractJNISampleApp/Sources/MySwiftLibrary/swift-java.config new file mode 100644 index 00000000..46bf1f1c --- /dev/null +++ b/Samples/JExtractJNISampleApp/Sources/MySwiftLibrary/swift-java.config @@ -0,0 +1,4 @@ +{ + "javaPackage": "com.example.swift", + "mode": "jni" +} diff --git a/Samples/JExtractJNISampleApp/build.gradle b/Samples/JExtractJNISampleApp/build.gradle new file mode 100644 index 00000000..647eaba9 --- /dev/null +++ b/Samples/JExtractJNISampleApp/build.gradle @@ -0,0 +1,195 @@ +//===----------------------------------------------------------------------===// +// +// 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 groovy.json.JsonSlurper +import org.swift.swiftkit.gradle.BuildUtils + +import java.nio.file.* +import kotlinx.serialization.json.* + +plugins { + id("build-logic.java-application-conventions") + id("me.champeau.jmh") version "0.7.2" +} + +group = "org.swift.swiftkit" +version = "1.0-SNAPSHOT" + +repositories { + mavenCentral() +} + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(24)) + } +} + +def swiftProductsWithJExtractPlugin() { + def stdout = new ByteArrayOutputStream() + def stderr = new ByteArrayOutputStream() + + def result = exec { + commandLine 'swift', 'package', 'describe', '--type', 'json' + standardOutput = stdout + errorOutput = stderr + ignoreExitValue = true + } + + def jsonOutput = stdout.toString() + + if (result.exitValue == 0) { + def json = new JsonSlurper().parseText(jsonOutput) + def products = json.targets + .findAll { target -> + target.product_dependencies?.contains("JExtractSwiftPlugin") + } + .collectMany { target -> + target.product_memberships ?: [] + } + return products + } else { + logger.warn("Command failed: ${stderr.toString()}") + return [] + } +} + + +def swiftCheckValid = tasks.register("swift-check-valid", Exec) { + commandLine "swift" + args("-version") +} + +def jextract = tasks.register("jextract", Exec) { + description = "Generate Java wrappers for swift target" + dependsOn swiftCheckValid + + // only because we depend on "live developing" the plugin while using this project to test it + inputs.file(new File(rootDir, "Package.swift")) + inputs.dir(new File(rootDir, "Sources")) + + // If the package description changes, we should execute jextract again, maybe we added jextract to new targets + inputs.file(new File(projectDir, "Package.swift")) + + // monitor all targets/products which depend on the JExtract plugin + swiftProductsWithJExtractPlugin().each { + logger.info("[swift-java:jextract (Gradle)] Swift input target: ${it}") + inputs.dir(new File(layout.projectDirectory.asFile, "Sources/${it}".toString())) + } + outputs.dir(layout.buildDirectory.dir("../.build/plugins/outputs/${layout.projectDirectory.asFile.getName().toLowerCase()}")) + + File baseSwiftPluginOutputsDir = layout.buildDirectory.dir("../.build/plugins/outputs/").get().asFile + if (!baseSwiftPluginOutputsDir.exists()) { + baseSwiftPluginOutputsDir.mkdirs() + } + Files.walk(layout.buildDirectory.dir("../.build/plugins/outputs/").get().asFile.toPath()).each { + // Add any Java sources generated by the plugin to our sourceSet + if (it.endsWith("JExtractSwiftPlugin/src/generated/java")) { + outputs.dir(it) + } + } + + workingDir = layout.projectDirectory + commandLine "swift" + args("build") // since Swift targets which need to be jextract-ed have the jextract build plugin, we just need to build + // If we wanted to execute a specific subcommand, we can like this: + // args("run",/* + // "swift-java", "jextract", + // "--swift-module", "MySwiftLibrary", + // // java.package is obtained from the swift-java.config in the swift module + // "--output-java", "${layout.buildDirectory.dir(".build/plugins/outputs/${layout.projectDirectory.asFile.getName().toLowerCase()}/JExtractSwiftPlugin/src/generated/java").get()}", + // "--output-swift", "${layout.buildDirectory.dir(".build/plugins/outputs/${layout.projectDirectory.asFile.getName().toLowerCase()}/JExtractSwiftPlugin/Sources").get()}", + // "--log-level", (logging.level <= LogLevel.INFO ? "debug" : */"info") + // ) +} + +// Add the java-swift generated Java sources +sourceSets { + main { + java { + srcDir(jextract) + } + } + test { + java { + srcDir(jextract) + } + } + jmh { + java { + srcDir(jextract) + } + } +} + +tasks.build { + dependsOn("jextract") +} + + +def cleanSwift = tasks.register("cleanSwift", Exec) { + workingDir = layout.projectDirectory + commandLine "swift" + args("package", "clean") +} +tasks.clean { + dependsOn("cleanSwift") +} + +dependencies { + implementation(project(':SwiftKit')) + + testImplementation(platform("org.junit:junit-bom:5.10.0")) + testImplementation("org.junit.jupiter:junit-jupiter") +} + +tasks.named('test', Test) { + useJUnitPlatform() +} + +application { + mainClass = "com.example.swift.HelloJava2Swift" + + applicationDefaultJvmArgs = [ + "--enable-native-access=ALL-UNNAMED", + + // Include the library paths where our dylibs are that we want to load and call + "-Djava.library.path=" + + (BuildUtils.javaLibraryPaths(rootDir) + + BuildUtils.javaLibraryPaths(project.projectDir)).join(":"), + + + // Enable tracing downcalls (to Swift) + "-Djextract.trace.downcalls=true" + ] +} + +String jmhIncludes = findProperty("jmhIncludes") + +jmh { + if (jmhIncludes != null) { + includes = [jmhIncludes] + } + + jvmArgsAppend = [ + "--enable-native-access=ALL-UNNAMED", + + "-Djava.library.path=" + + (BuildUtils.javaLibraryPaths(rootDir) + + BuildUtils.javaLibraryPaths(project.projectDir)).join(":"), + + // Enable tracing downcalls (to Swift) + "-Djextract.trace.downcalls=false" + ] +} diff --git a/Samples/JExtractJNISampleApp/ci-validate.sh b/Samples/JExtractJNISampleApp/ci-validate.sh new file mode 100755 index 00000000..07b42627 --- /dev/null +++ b/Samples/JExtractJNISampleApp/ci-validate.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +set -x +set -e + +./gradlew run \ No newline at end of file diff --git a/Samples/JExtractJNISampleApp/gradlew b/Samples/JExtractJNISampleApp/gradlew new file mode 120000 index 00000000..343e0d2c --- /dev/null +++ b/Samples/JExtractJNISampleApp/gradlew @@ -0,0 +1 @@ +../../gradlew \ No newline at end of file diff --git a/Samples/JExtractJNISampleApp/gradlew.bat b/Samples/JExtractJNISampleApp/gradlew.bat new file mode 120000 index 00000000..cb5a9464 --- /dev/null +++ b/Samples/JExtractJNISampleApp/gradlew.bat @@ -0,0 +1 @@ +../../gradlew.bat \ No newline at end of file diff --git a/Samples/JExtractJNISampleApp/src/main/java/com/example/swift/HelloJava2Swift.java b/Samples/JExtractJNISampleApp/src/main/java/com/example/swift/HelloJava2Swift.java new file mode 100644 index 00000000..3c7edf45 --- /dev/null +++ b/Samples/JExtractJNISampleApp/src/main/java/com/example/swift/HelloJava2Swift.java @@ -0,0 +1,45 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +package com.example.swift; + +// Import swift-extract generated sources + +// Import javakit/swiftkit support libraries + +import org.swift.swiftkit.SwiftKit; + +public class HelloJava2Swift { + + public static void main(String[] args) { + System.out.print("Property: java.library.path = " + SwiftKit.getJavaLibraryPath()); + + examples(); + } + + static void examples() { + MySwiftLibrary.helloWorld(); + + MySwiftLibrary.globalTakeInt(1337); + MySwiftLibrary.globalTakeIntInt(1337, 42); + + long cnt = MySwiftLibrary.globalWriteString("String from Java"); + SwiftKit.trace("count = " + cnt); + + long i = MySwiftLibrary.globalMakeInt(); + SwiftKit.trace("globalMakeInt() = " + i); + + System.out.println("DONE."); + } +} diff --git a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+SwiftThunkPrinting.swift b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+SwiftThunkPrinting.swift index 54ce0d72..d6b09b24 100644 --- a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+SwiftThunkPrinting.swift +++ b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+SwiftThunkPrinting.swift @@ -27,7 +27,7 @@ extension FFMSwift2JavaGenerator { var printer = CodePrinter() printer.print("// Empty file generated on purpose") - try printer.writeContents( + _ = try printer.writeContents( outputDirectory: self.swiftOutputDirectory, javaPackagePath: nil, filename: expectedFileName) diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator.swift index 8ec4b437..0f16e697 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator.swift @@ -28,6 +28,10 @@ package class JNISwift2JavaGenerator: Swift2JavaGenerator { var thunkNameRegistry = ThunkNameRegistry() + /// Because we need to write empty files for SwiftPM, keep track which files we didn't write yet, + /// and write an empty file for those. + var expectedOutputSwiftFiles: Set + package init( translator: Swift2JavaTranslator, javaPackage: String, @@ -40,11 +44,31 @@ package class JNISwift2JavaGenerator: Swift2JavaGenerator { self.javaPackage = javaPackage self.swiftOutputDirectory = swiftOutputDirectory self.javaOutputDirectory = javaOutputDirectory + + // If we are forced to write empty files, construct the expected outputs + if translator.config.writeEmptyFiles ?? false { + self.expectedOutputSwiftFiles = Set(translator.inputs.compactMap { (input) -> String? in + guard let filePathPart = input.filePath.split(separator: "/\(translator.swiftModuleName)/").last else { + return nil + } + + return String(filePathPart.replacing(".swift", with: "+SwiftJava.swift")) + }) + self.expectedOutputSwiftFiles.insert("\(translator.swiftModuleName)Module+SwiftJava.swift") + } else { + self.expectedOutputSwiftFiles = [] + } } func generate() throws { try writeSwiftThunkSources() try writeExportedJavaSources() + + let pendingFileCount = self.expectedOutputSwiftFiles.count + if pendingFileCount > 0 { + print("[swift-java] Write empty [\(pendingFileCount)] 'expected' files in: \(swiftOutputDirectory)/") + try writeSwiftExpectedEmptySources() + } } } @@ -73,6 +97,19 @@ extension JNISwift2JavaGenerator { try writeSwiftThunkSources(&printer) } + package func writeSwiftExpectedEmptySources() throws { + for expectedFileName in self.expectedOutputSwiftFiles { + logger.trace("Write empty file: \(expectedFileName) ...") + + var printer = CodePrinter() + printer.print("// Empty file generated on purpose") + _ = try printer.writeContents( + outputDirectory: self.swiftOutputDirectory, + javaPackagePath: nil, + filename: expectedFileName) + } + } + package func writeSwiftThunkSources(_ printer: inout CodePrinter) throws { let moduleFilenameBase = "\(self.swiftModuleName)Module+SwiftJava" let moduleFilename = "\(moduleFilenameBase).swift" @@ -84,10 +121,11 @@ extension JNISwift2JavaGenerator { if let outputFile = try printer.writeContents( outputDirectory: self.swiftOutputDirectory, - javaPackagePath: javaPackagePath, + javaPackagePath: nil, filename: moduleFilename ) { print("[swift-java] Generated: \(moduleFilenameBase.bold).swift (at \(outputFile))") + self.expectedOutputSwiftFiles.remove(moduleFilename) } } catch { logger.warning("Failed to write to Swift thunks: \(moduleFilename)") @@ -113,18 +151,22 @@ extension JNISwift2JavaGenerator { private func printSwiftFunctionThunk(_ printer: inout CodePrinter, _ decl: ImportedFunc) { // TODO: Replace swiftModuleName with class name if non-global - let cName = "Java_" + self.javaPackage.replacingOccurrences(of: ".", with: "_") + "_\(swiftModuleName)_" + decl.name + let cName = + "Java_" + self.javaPackage.replacingOccurrences(of: ".", with: "_") + "_\(swiftModuleName)_" + + decl.name let thunkName = thunkNameRegistry.functionThunkName(decl: decl) let translatedParameters = decl.functionSignature.parameters.enumerated().map { idx, param in (param.parameterName ?? "arg\(idx)", param.type.javaType) } - let thunkParameters = [ - "environment: UnsafeMutablePointer!", - "thisClass: jclass" - ] + translatedParameters.map { "\($0.0): \($0.1.jniTypeName)"} + let thunkParameters = + [ + "environment: UnsafeMutablePointer!", + "thisClass: jclass", + ] + translatedParameters.map { "\($0.0): \($0.1.jniTypeName)" } let swiftReturnType = decl.functionSignature.result.type - let thunkReturnType = !swiftReturnType.isVoid ? " -> \(swiftReturnType.javaType.jniTypeName)" : "" + let thunkReturnType = + !swiftReturnType.isVoid ? " -> \(swiftReturnType.javaType.jniTypeName)" : "" printer.printBraceBlock( """ @@ -132,24 +174,28 @@ extension JNISwift2JavaGenerator { func \(thunkName)(\(thunkParameters.joined(separator: ", ")))\(thunkReturnType) """ ) { printer in - let downcallParameters = zip(decl.functionSignature.parameters, translatedParameters).map { originalParam, translatedParam in - let label = originalParam.argumentLabel.map { "\($0): "} ?? "" + let downcallParameters = zip(decl.functionSignature.parameters, translatedParameters).map { + originalParam, translatedParam in + let label = originalParam.argumentLabel.map { "\($0): " } ?? "" return "\(label)\(originalParam.type)(fromJNI: \(translatedParam.0), in: environment!)" } let tryClause: String = decl.isThrowing ? "try " : "" - let functionDowncall = "\(tryClause)\(swiftModuleName).\(decl.name)(\(downcallParameters.joined(separator: ", ")))" + let functionDowncall = + "\(tryClause)\(swiftModuleName).\(decl.name)(\(downcallParameters.joined(separator: ", ")))" - let innerBody = if swiftReturnType.isVoid { - functionDowncall - } else { - """ - let result = \(functionDowncall) - return result.getJNIValue(in: environment)") - """ - } + let innerBody = + if swiftReturnType.isVoid { + functionDowncall + } else { + """ + let result = \(functionDowncall) + return result.getJNIValue(in: environment) + """ + } if decl.isThrowing { - let dummyReturn = !swiftReturnType.isVoid ? "return \(swiftReturnType).jniPlaceholderValue" : "" + let dummyReturn = + !swiftReturnType.isVoid ? "return \(swiftReturnType).jniPlaceholderValue" : "" printer.print( """ do { @@ -173,6 +219,16 @@ extension JNISwift2JavaGenerator { printPackage(&printer) printModuleClass(&printer) { printer in + printer.print( + """ + static final String LIB_NAME = "\(swiftModuleName)"; + + static { + System.loadLibrary(LIB_NAME); + } + """ + ) + for decl in analysis.importedGlobalFuncs { self.logger.trace("Print global function: \(decl)") printFunctionBinding(&printer, decl) @@ -223,7 +279,9 @@ extension JNISwift2JavaGenerator { */ """ ) - printer.print("public static native \(returnType) \(decl.name)(\(params.joined(separator: ", ")))\(throwsClause);") + printer.print( + "public static native \(returnType) \(decl.name)(\(params.joined(separator: ", ")))\(throwsClause);" + ) } } @@ -253,7 +311,7 @@ extension SwiftStandardLibraryTypeKind { var javaType: JavaType? { switch self { case .bool: .boolean - case .int: .long // TODO: Handle 32-bit or 64-bit + case .int: .long // TODO: Handle 32-bit or 64-bit case .int8: .byte case .uint16: .char case .int16: .short @@ -264,10 +322,10 @@ extension SwiftStandardLibraryTypeKind { case .void: .void case .string: .javaLangString case .uint, .uint8, .uint32, .uint64, - .unsafeRawPointer, .unsafeMutableRawPointer, - .unsafePointer, .unsafeMutablePointer, - .unsafeRawBufferPointer, .unsafeMutableRawBufferPointer, - .unsafeBufferPointer, .unsafeMutableBufferPointer: + .unsafeRawPointer, .unsafeMutableRawPointer, + .unsafePointer, .unsafeMutablePointer, + .unsafeRawBufferPointer, .unsafeMutableRawBufferPointer, + .unsafeBufferPointer, .unsafeMutableBufferPointer: nil } } diff --git a/Tests/JExtractSwiftTests/JNI/JNIModuleTests.swift b/Tests/JExtractSwiftTests/JNI/JNIModuleTests.swift index 2f73cca4..e483a4dd 100644 --- a/Tests/JExtractSwiftTests/JNI/JNIModuleTests.swift +++ b/Tests/JExtractSwiftTests/JNI/JNIModuleTests.swift @@ -44,6 +44,11 @@ struct JNIModuleTests { package com.example.swift; public final class SwiftModule { + static final String LIB_NAME = "SwiftModule"; + + static { + System.loadLibrary(LIB_NAME); + } """ ]) } @@ -104,7 +109,7 @@ struct JNIModuleTests { @_cdecl("Java_com_example_swift_SwiftModule_takeIntegers") func swiftjava_SwiftModule_takeIntegers_i1_i2_i3_i4(environment: UnsafeMutablePointer!, thisClass: jclass, i1: jbyte, i2: jshort, i3: jint, i4: jlong) -> jchar { let result = SwiftModule.takeIntegers(i1: Int8(fromJNI: i1, in: environment!), i2: Int16(fromJNI: i2, in: environment!), i3: Int32(fromJNI: i3, in: environment!), i4: Int(fromJNI: i4, in: environment!)) - return result.getJNIValue(in: environment)") + return result.getJNIValue(in: environment) } """, """ From e1771d7f7fa54eff8749d4a157beaa4ccde1f424 Mon Sep 17 00:00:00 2001 From: Mads Odgaard Date: Mon, 30 Jun 2025 15:27:56 +0200 Subject: [PATCH 2/4] Update Samples/JExtractJNISampleApp/src/main/java/com/example/swift/HelloJava2Swift.java Co-authored-by: Konrad `ktoso` Malawski --- .../src/main/java/com/example/swift/HelloJava2Swift.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Samples/JExtractJNISampleApp/src/main/java/com/example/swift/HelloJava2Swift.java b/Samples/JExtractJNISampleApp/src/main/java/com/example/swift/HelloJava2Swift.java index 3c7edf45..36bad93c 100644 --- a/Samples/JExtractJNISampleApp/src/main/java/com/example/swift/HelloJava2Swift.java +++ b/Samples/JExtractJNISampleApp/src/main/java/com/example/swift/HelloJava2Swift.java @@ -20,7 +20,7 @@ import org.swift.swiftkit.SwiftKit; -public class HelloJava2Swift { +public class HelloJava2SwiftJNI { public static void main(String[] args) { System.out.print("Property: java.library.path = " + SwiftKit.getJavaLibraryPath()); From 881b18cccf68208bcea9120577ff64bec00e18ff Mon Sep 17 00:00:00 2001 From: Mads Odgaard Date: Mon, 30 Jun 2025 16:38:03 +0200 Subject: [PATCH 3/4] rename file --- .../swift/{HelloJava2Swift.java => HelloJava2SwiftJNI.java} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Samples/JExtractJNISampleApp/src/main/java/com/example/swift/{HelloJava2Swift.java => HelloJava2SwiftJNI.java} (100%) diff --git a/Samples/JExtractJNISampleApp/src/main/java/com/example/swift/HelloJava2Swift.java b/Samples/JExtractJNISampleApp/src/main/java/com/example/swift/HelloJava2SwiftJNI.java similarity index 100% rename from Samples/JExtractJNISampleApp/src/main/java/com/example/swift/HelloJava2Swift.java rename to Samples/JExtractJNISampleApp/src/main/java/com/example/swift/HelloJava2SwiftJNI.java From c4da6da383b9d179c3f4a65aed606417e97b9845 Mon Sep 17 00:00:00 2001 From: Mads Odgaard Date: Mon, 30 Jun 2025 16:44:48 +0200 Subject: [PATCH 4/4] fix main sample class --- Samples/JExtractJNISampleApp/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Samples/JExtractJNISampleApp/build.gradle b/Samples/JExtractJNISampleApp/build.gradle index 647eaba9..d0e32857 100644 --- a/Samples/JExtractJNISampleApp/build.gradle +++ b/Samples/JExtractJNISampleApp/build.gradle @@ -159,7 +159,7 @@ tasks.named('test', Test) { } application { - mainClass = "com.example.swift.HelloJava2Swift" + mainClass = "com.example.swift.HelloJava2SwiftJNI" applicationDefaultJvmArgs = [ "--enable-native-access=ALL-UNNAMED",