Skip to content

jextract/jni: Support async closures (#834) - #913

Open
amanmaurya92 wants to merge 4 commits into
swiftlang:mainfrom
amanmaurya92:feature/issue-834-async-closures
Open

jextract/jni: Support async closures (#834)#913
amanmaurya92 wants to merge 4 commits into
swiftlang:mainfrom
amanmaurya92:feature/issue-834-async-closures

Conversation

@amanmaurya92

@amanmaurya92 amanmaurya92 commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Resolves #834.

Previously, async closures passed from Java to Swift were not supported in JNI mode. Passing an async closure either failed to translate or incorrectly matched synchronous functional interfaces like Runnable or Function.

This adds JNI support for @escaping (...) async -> T closures:

  • Exclude async closures from matching synchronous KnownJavaFunctionalInterfaces.
  • Generate synthetic @FunctionalInterface wrappers whose apply method returns CompletableFuture<T> (or SimpleCompletableFuture in legacy future mode).
  • Add JavaCompletableFuture and JavaSimpleCompletableFuture in SwiftJava with @JavaMethod public func get() throws -> JavaObject? to match JVM method descriptors during reflection.
  • Lower async closure invocations in the Swift thunk to await future$?.get() and unbox the result via T.fromJavaObject.
  • Add code generation tests in JNIClosureTests and real JVM runtime execution tests (void, primitive types, background completion, and exception propagation) in SwiftJavaExtractJNISampleApp.

- Exclude async closures from matching synchronous KnownJavaFunctionalInterfaces

- Generate CompletableFuture return type for async Java functional interfaces

- Lower async closure invocation in Swift thunk to await future.get()

- Add comprehensive unit tests in JNIClosureTests
@amanmaurya92
amanmaurya92 marked this pull request as ready for review September 6, 2026 08:49
@amanmaurya92
amanmaurya92 requested a review from ktoso as a code owner September 6, 2026 08:49
@ktoso

ktoso commented Sep 6, 2026

Copy link
Copy Markdown
Collaborator

Would you mind making the PR description less confusing? What does "classification" even mean.

And please add runtime tests.

The tests just use string but we need runtime tests for primitives as well.

I'll hold off reviewing until the test suite is more complete, this looks to me AI generated which we don't mind but please do spend more effort into validating the implementation is entirely correct, it takes much effort to review a partial PR like this.

@amanmaurya92
amanmaurya92 marked this pull request as draft September 6, 2026 08:54
…r async closures

- Add JavaCompletableFuture and JavaSimpleCompletableFuture bindings in SwiftJava with typed @JavaMethod get() matching JVM method descriptors

- Generate JavaCompletableFuture return types on synthetic @javainterface closure wrappers

- Lower async Swift closure invocation in native JNI thunk to futureTrue.get() and unbox the result via T.fromJavaObject

- Add unit tests for async closures with primitive parameter and return types in JNIClosureTests

- Add real JVM integration tests covering void, primitive (Long, Double), background-thread, and exceptional completions in SwiftJavaExtractJNISampleApp
Comment thread Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/Async.swift Outdated
Comment thread Sources/SwiftJava/JavaCompletableFuture.swift Outdated
Comment thread Tests/JExtractSwiftTests/JNI/JNIClosureTests.swift
Comment thread Sources/SwiftJava/JavaCompletableFuture.swift Outdated
outParameters: translatedResult.outParameters,
conversion: translatedResult.conversion
)
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh no, actually this has an issue -- try writing a test for async -> Int64? , we're missing conversions here.

Comment thread Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift Outdated
return failed;
});

assertThrows(ExecutionException.class, future::get);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, optimally we'd want to get the RuntimeException rethrown i guess... that might be hard to do, ok to do in a follow up PR

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will open the issue for this.

…sures

- Translate async closure return types without leaking downcall out-parameter discriminators

- Convert OptionalLong, OptionalInt, OptionalDouble, and Optional<T> in Swift async closure thunks

- Propagate exceptions from throwing async closures via try and fail-fast non-throwing closures with try! instead of swallowing errors with try?

- Add unit tests for () async -> Int64? and (Int64) async throws -> String in JNIClosureTests

- Add runtime integration tests in AsyncTest for optional returns and exceptional completion
Comment thread Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift Outdated
Comment thread Sources/SwiftJava/swift-java.config
…ync closures

- Add awaitFutureResult case to UpcallConversionStep to unify sync and async closure result lowering

- Guard that the returned future is non-nil and unbox primitive, optional, and object results

- Unify escapingClosureLowering in native translation by delegating async closure awaiting to UpcallConversionStep

- Update unit tests in JNIClosureTests to assert the non-nil future guard and awaiting logic
@amanmaurya92
amanmaurya92 marked this pull request as ready for review September 7, 2026 11:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

jextract: Support async closures

2 participants