Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/pull_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ jobs:
strategy:
fail-fast: false
matrix:
swift_version: ['6.2', 'nightly']
swift_version: ['6.1.3', '6.2', 'nightly']
os_version: ['jammy']
jdk_vendor: ['corretto']
container:
Expand Down Expand Up @@ -182,7 +182,7 @@ jobs:
strategy:
fail-fast: false
matrix:
swift_version: ['6.2', 'nightly']
swift_version: ['6.1.3', '6.2', 'nightly']
os_version: ['jammy']
jdk_vendor: ['corretto']
sample_app: [ # TODO: use a reusable-workflow to generate those names
Expand Down
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// swift-tools-version: 6.2
// swift-tools-version: 6.1
// The swift-tools-version declares the minimum version of Swift required to build this package.

import CompilerPluginSupport
Expand Down
2 changes: 1 addition & 1 deletion Samples/SwiftJavaExtractJNISampleApp/Package.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// swift-tools-version: 6.2
// swift-tools-version: 6.1
// The swift-tools-version declares the minimum version of Swift required to build this package.

import CompilerPluginSupport
Expand Down
14 changes: 14 additions & 0 deletions Sources/JExtractSwiftLib/CodePrinter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,20 @@ public struct CodePrinter {
}
}

public mutating func printHashIfBlock(
_ header: Any,
function: String = #function,
file: String = #fileID,
line: UInt = #line,
body: (inout CodePrinter) throws -> ()
) rethrows {
print("#if \(header)")
indent()
try body(&self)
outdent()
print("#endif // end of \(header)", .sloc, function: function, file: file, line: line)
}

public mutating func printBraceBlock(
_ header: Any,
function: String = #function,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -825,7 +825,7 @@ extension JNISwift2JavaGenerator {

func printDo(printer: inout CodePrinter) {
printer.print("let swiftResult$ = await \(placeholder)")
printer.print("environment = try JavaVirtualMachine.shared().environment()")
printer.print("environment = try! JavaVirtualMachine.shared().environment()")
let inner = inner.render(&printer, "swiftResult$")
if swiftFunctionResultType.isVoid {
printer.print("environment.interface.CallBooleanMethodA(environment, globalFuture, _JNIMethodIDCache.CompletableFuture.complete, [jvalue(l: nil)])")
Expand All @@ -839,7 +839,7 @@ extension JNISwift2JavaGenerator {
}
}

func printTask(printer: inout CodePrinter) {
func printTaskBody(printer: inout CodePrinter) {
printer.printBraceBlock("defer") { printer in
// Defer might on any thread, so we need to attach environment.
printer.print("let deferEnvironment = try! JavaVirtualMachine.shared().environment()")
Expand Down Expand Up @@ -867,18 +867,22 @@ extension JNISwift2JavaGenerator {
}
}

printer.printBraceBlock("if #available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, *)") { printer in
printer.printBraceBlock("Task.immediate") { printer in
// Immediate runs on the caller thread, so we don't need to attach the environment again.
printer.print("var environment = environment!") // this is to ensure we always use the same environment name, even though we are rebinding it.
printTask(printer: &printer)
printer.print("var task: Task<Void, Never>? = nil")
printer.printHashIfBlock("swift(>=6.2)") { printer in
printer.printBraceBlock("if #available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, *)") { printer in
printer.printBraceBlock("task = Task.immediate") { printer in
// Immediate runs on the caller thread, so we don't need to attach the environment again.
printer.print("var environment = environment!") // this is to ensure we always use the same environment name, even though we are rebinding it.
printTaskBody(printer: &printer)
}
}
}
printer.printBraceBlock("else") { printer in
printer.printBraceBlock("Task") { printer in

printer.printBraceBlock("if task == nil") { printer in
printer.printBraceBlock("task = Task") { printer in
// We can be on any thread, so we need to attach the thread.
printer.print("var environment = try! JavaVirtualMachine.shared().environment()")
printTask(printer: &printer)
printTaskBody(printer: &printer)
}
}

Expand Down
22 changes: 10 additions & 12 deletions Tests/JExtractSwiftTests/Asserts/TextAssertions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,9 @@ func assertOutput(
print("==== ---------------------------------------------------------------")
print("Expected output:")
for (n, e) in expectedLines.enumerated() {
print("\(n): \(e)".yellow(if: diffLineNumbers.map({$0 - matchingOutputOffset}).contains(n)))
let isMismatch = diffLineNumbers.map({$0 - matchingOutputOffset}).contains(n)
let marker = isMismatch ? " // <<<<<<<<<<< mismatch" : ""
print("\(n): \(e)\(marker)".yellow(if: isMismatch))
}
}

Expand All @@ -188,15 +190,7 @@ func assertOutput(
let printFromLineNo = matchingOutputOffset
for (n, g) in gotLines.enumerated() where n >= printFromLineNo {
let baseLine = "\(n): \(g)"
var line = baseLine
if diffLineNumbers.contains(n) {
line += "\n"
let leadingCount = "\(n): ".count
let message = "\(String(repeating: " ", count: leadingCount))\(String(repeating: "^", count: 8)) EXPECTED MATCH OR SEARCHING FROM HERE "
line += "\(message)\(String(repeating: "^", count: max(0, line.count - message.count)))"
line = line.red
}
print(line)
print(baseLine)
}
print("==== ---------------------------------------------------------------\n")
}
Expand Down Expand Up @@ -248,14 +242,18 @@ func assertOutput(
print("==== ---------------------------------------------------------------")
print("Expected output:")
for (n, e) in expectedLines.enumerated() {
print("\(e)".yellow(if: diffLineNumbers.contains(n)))
let isMismatch = diffLineNumbers.contains(n)
let marker = isMismatch ? " // <<<<<<<< error: mismatch" : ""
print("\(e)\(marker)".yellow(if: isMismatch))
}
}

print("==== ---------------------------------------------------------------")
print("Got output:")
for (n, g) in gotLines.enumerated() {
print("\(g)".red(if: diffLineNumbers.contains(n)))
let isMismatch = diffLineNumbers.contains(n)
let marker = isMismatch ? "// <<<<<<<< error: mismatch" : ""
print("\(g)\(marker)".red(if: isMismatch))
}
print("==== ---------------------------------------------------------------\n")
}
Expand Down
122 changes: 67 additions & 55 deletions Tests/JExtractSwiftTests/JNI/JNIAsyncTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -59,27 +59,30 @@ struct JNIAsyncTests {
@_cdecl("Java_com_example_swift_SwiftModule__00024asyncVoid__Ljava_util_concurrent_CompletableFuture_2")
func Java_com_example_swift_SwiftModule__00024asyncVoid__Ljava_util_concurrent_CompletableFuture_2(environment: UnsafeMutablePointer<JNIEnv?>!, thisClass: jclass, result_future: jobject?) {
let globalFuture = environment.interface.NewGlobalRef(environment, result_future)
if #available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, *) {
Task.immediate {
var environment = environment!
defer {
let deferEnvironment = try! JavaVirtualMachine.shared().environment()
environment.interface.DeleteGlobalRef(deferEnvironment, globalFuture)
var task: Task<Void, Never>? = nil
#if swift(>=6.2)
if #available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, *) {
task = Task.immediate {
var environment = environment!
defer {
let deferEnvironment = try! JavaVirtualMachine.shared().environment()
environment.interface.DeleteGlobalRef(deferEnvironment, globalFuture)
}
let swiftResult$ = await SwiftModule.asyncVoid()
environment = try! JavaVirtualMachine.shared().environment()
environment.interface.CallBooleanMethodA(environment, globalFuture, _JNIMethodIDCache.CompletableFuture.complete, [jvalue(l: nil)])
}
let swiftResult$ = await SwiftModule.asyncVoid()
environment = try JavaVirtualMachine.shared().environment()
environment.interface.CallBooleanMethodA(environment, globalFuture, _JNIMethodIDCache.CompletableFuture.complete, [jvalue(l: nil)])
}
}
else {
Task {
#endif
if task == nil {
task = Task {
var environment = try! JavaVirtualMachine.shared().environment()
defer {
let deferEnvironment = try! JavaVirtualMachine.shared().environment()
environment.interface.DeleteGlobalRef(deferEnvironment, globalFuture)
}
let swiftResult$ = await SwiftModule.asyncVoid()
environment = try JavaVirtualMachine.shared().environment()
environment = try! JavaVirtualMachine.shared().environment()
environment.interface.CallBooleanMethodA(environment, globalFuture, _JNIMethodIDCache.CompletableFuture.complete, [jvalue(l: nil)])
}
}
Expand Down Expand Up @@ -130,35 +133,38 @@ struct JNIAsyncTests {
@_cdecl("Java_com_example_swift_SwiftModule__00024async__Ljava_util_concurrent_CompletableFuture_2")
func Java_com_example_swift_SwiftModule__00024async__Ljava_util_concurrent_CompletableFuture_2(environment: UnsafeMutablePointer<JNIEnv?>!, thisClass: jclass, result_future: jobject?) {
let globalFuture = environment.interface.NewGlobalRef(environment, result_future)
if #available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, *) {
Task.immediate {
var environment = environment!
defer {
let deferEnvironment = try! JavaVirtualMachine.shared().environment()
environment.interface.DeleteGlobalRef(deferEnvironment, globalFuture)
}
do {
let swiftResult$ = await try SwiftModule.async()
environment = try JavaVirtualMachine.shared().environment()
environment.interface.CallBooleanMethodA(environment, globalFuture, _JNIMethodIDCache.CompletableFuture.complete, [jvalue(l: nil)])
}
catch {
let catchEnvironment = try! JavaVirtualMachine.shared().environment()
let exception = catchEnvironment.interface.NewObjectA(catchEnvironment, _JNIMethodIDCache.Exception.class, _JNIMethodIDCache.Exception.constructWithMessage, [String(describing: error).getJValue(in: catchEnvironment)])
catchEnvironment.interface.CallBooleanMethodA(catchEnvironment, globalFuture, _JNIMethodIDCache.CompletableFuture.completeExceptionally, [jvalue(l: exception)])
var task: Task<Void, Never>? = nil
#if swift(>=6.2)
if #available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, *) {
task = Task.immediate {
var environment = environment!
defer {
let deferEnvironment = try! JavaVirtualMachine.shared().environment()
environment.interface.DeleteGlobalRef(deferEnvironment, globalFuture)
}
do {
let swiftResult$ = await try SwiftModule.async()
environment = try! JavaVirtualMachine.shared().environment()
environment.interface.CallBooleanMethodA(environment, globalFuture, _JNIMethodIDCache.CompletableFuture.complete, [jvalue(l: nil)])
}
catch {
let catchEnvironment = try! JavaVirtualMachine.shared().environment()
let exception = catchEnvironment.interface.NewObjectA(catchEnvironment, _JNIMethodIDCache.Exception.class, _JNIMethodIDCache.Exception.constructWithMessage, [String(describing: error).getJValue(in: catchEnvironment)])
catchEnvironment.interface.CallBooleanMethodA(catchEnvironment, globalFuture, _JNIMethodIDCache.CompletableFuture.completeExceptionally, [jvalue(l: exception)])
}
}
}
}
else {
Task {
#endif
if task == nil {
task = Task {
var environment = try! JavaVirtualMachine.shared().environment()
defer {
let deferEnvironment = try! JavaVirtualMachine.shared().environment()
environment.interface.DeleteGlobalRef(deferEnvironment, globalFuture)
}
do {
let swiftResult$ = await try SwiftModule.async()
environment = try JavaVirtualMachine.shared().environment()
environment = try! JavaVirtualMachine.shared().environment()
environment.interface.CallBooleanMethodA(environment, globalFuture, _JNIMethodIDCache.CompletableFuture.complete, [jvalue(l: nil)])
}
catch {
Expand Down Expand Up @@ -215,28 +221,31 @@ struct JNIAsyncTests {
@_cdecl("Java_com_example_swift_SwiftModule__00024async__JLjava_util_concurrent_CompletableFuture_2")
func Java_com_example_swift_SwiftModule__00024async__JLjava_util_concurrent_CompletableFuture_2(environment: UnsafeMutablePointer<JNIEnv?>!, thisClass: jclass, i: jlong, result_future: jobject?) {
let globalFuture = environment.interface.NewGlobalRef(environment, result_future)
var task: Task<Void, Never>? = nil
#if swift(>=6.2)
if #available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, *) {
Task.immediate {
task = Task.immediate {
var environment = environment!
defer {
let deferEnvironment = try! JavaVirtualMachine.shared().environment()
environment.interface.DeleteGlobalRef(deferEnvironment, globalFuture)
}
let swiftResult$ = await SwiftModule.async(i: Int64(fromJNI: i, in: environment))
environment = try JavaVirtualMachine.shared().environment()
environment = try! JavaVirtualMachine.shared().environment()
let boxedResult$ = SwiftJavaRuntimeSupport._JNIBoxedConversions.box(swiftResult$.getJNIValue(in: environment), in: environment)
environment.interface.CallBooleanMethodA(environment, globalFuture, _JNIMethodIDCache.CompletableFuture.complete, [jvalue(l: boxedResult$)])
}
}
else {
Task {
#endif // end of swift(>=6.2)
if task == nil {
task = Task {
var environment = try! JavaVirtualMachine.shared().environment()
defer {
let deferEnvironment = try! JavaVirtualMachine.shared().environment()
environment.interface.DeleteGlobalRef(deferEnvironment, globalFuture)
}
let swiftResult$ = await SwiftModule.async(i: Int64(fromJNI: i, in: environment))
environment = try JavaVirtualMachine.shared().environment()
environment = try! JavaVirtualMachine.shared().environment()
let boxedResult$ = SwiftJavaRuntimeSupport._JNIBoxedConversions.box(swiftResult$.getJNIValue(in: environment), in: environment)
environment.interface.CallBooleanMethodA(environment, globalFuture, _JNIMethodIDCache.CompletableFuture.complete, [jvalue(l: boxedResult$)])
}
Expand Down Expand Up @@ -303,31 +312,34 @@ struct JNIAsyncTests {
fatalError("c memory address was null in call to \\(#function)!")
}
let globalFuture = environment.interface.NewGlobalRef(environment, result_future)
if #available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, *) {
Task.immediate {
var environment = environment!
defer {
let deferEnvironment = try! JavaVirtualMachine.shared().environment()
environment.interface.DeleteGlobalRef(deferEnvironment, globalFuture)
var task: Task<Void, Never>? = nil
#if swift(>=6.2)
if #available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, *) {
task = Task.immediate {
var environment = environment!
defer {
let deferEnvironment = try! JavaVirtualMachine.shared().environment()
environment.interface.DeleteGlobalRef(deferEnvironment, globalFuture)
}
let swiftResult$ = await SwiftModule.async(c: c$.pointee)
environment = try! JavaVirtualMachine.shared().environment()
let result$ = UnsafeMutablePointer<MyClass>.allocate(capacity: 1)
result$.initialize(to: swiftResult$)
let resultBits$ = Int64(Int(bitPattern: result$))
let boxedResult$ = SwiftJavaRuntimeSupport._JNIBoxedConversions.box(resultBits$.getJNIValue(in: environment), in: environment)
environment.interface.CallBooleanMethodA(environment, globalFuture, _JNIMethodIDCache.CompletableFuture.complete, [jvalue(l: boxedResult$)])
}
let swiftResult$ = await SwiftModule.async(c: c$.pointee)
environment = try JavaVirtualMachine.shared().environment()
let result$ = UnsafeMutablePointer<MyClass>.allocate(capacity: 1)
result$.initialize(to: swiftResult$)
let resultBits$ = Int64(Int(bitPattern: result$))
let boxedResult$ = SwiftJavaRuntimeSupport._JNIBoxedConversions.box(resultBits$.getJNIValue(in: environment), in: environment)
environment.interface.CallBooleanMethodA(environment, globalFuture, _JNIMethodIDCache.CompletableFuture.complete, [jvalue(l: boxedResult$)])
}
}
else {
Task {
#endif
if task == nil {
task = Task {
var environment = try! JavaVirtualMachine.shared().environment()
defer {
let deferEnvironment = try! JavaVirtualMachine.shared().environment()
environment.interface.DeleteGlobalRef(deferEnvironment, globalFuture)
}
let swiftResult$ = await SwiftModule.async(c: c$.pointee)
environment = try JavaVirtualMachine.shared().environment()
environment = try! JavaVirtualMachine.shared().environment()
let result$ = UnsafeMutablePointer<MyClass>.allocate(capacity: 1)
result$.initialize(to: swiftResult$)
let resultBits$ = Int64(Int(bitPattern: result$))
Expand Down
2 changes: 1 addition & 1 deletion docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ ENV LANGUAGE=en_US.UTF-8
# JDK dependency
RUN curl -s "https://get.sdkman.io" | bash
RUN bash -c "source /root/.sdkman/bin/sdkman-init.sh && sdk install java 25.0.1-amzn"
ENV JAVA_HOME="$(sdk home java current)"

RUN curl -O https://download.swift.org/swiftly/linux/swiftly-$(uname -m).tar.gz && \
tar zxf swiftly-$(uname -m).tar.gz && \
./swiftly init --quiet-shell-followup --assume-yes && \
. "${SWIFTLY_HOME_DIR:-$HOME/.local/share/swiftly}/env.sh" && \
hash -r

Loading