Skip to content

JNI: support consumers with C++ interoperability enabled - #927

Open
NakaokaRei wants to merge 7 commits into
swiftlang:mainfrom
NakaokaRei:fix/jni-cxx-interoperability
Open

NakaokaRei wants to merge 7 commits into
swiftlang:mainfrom
NakaokaRei:fix/jni-cxx-interoperability

Conversation

@NakaokaRei

Copy link
Copy Markdown
Contributor

Fixes #391. Follow-up to #463.

Problem

Enabling .interoperabilityMode(.Cxx) in a consumer changes how JNI headers are imported. In particular, Android exposes different JNIEnv and reference types in C++ mode, while SwiftJava's runtime uses their C definitions. Generated JNI entry points and calls then fail with type mismatches.

Solution

  • Generate entry points using JNIEnvironment and runtime-owned JNITypes aliases. Add typed JNI function-table accessors and an object-value factory to preserve C signatures in C++ consumers, without unsafeBitCast.
  • Update generator and macro test expectations.
  • Enable C++ mode only on the JavaKit and JNI sample consumer targets via CXX_INTEROP=1. Following the feedback on fix: .interoperabilityMode(.Cxx) build issue #463, reuse existing stable Swift 6.3 CI jobs on Linux/macOS and one Android configuration, retaining verbose output. Rerun Gradle tests across configurations and avoid stale toolchain daemons.

Testing

Locally verified before rebasing onto current main (the patch is unchanged):

  • 820 package tests; JavaKit and JNI samples in normal and C++ modes on macOS and Linux ARM64.
  • Android JNI sample build; hello-cpp-swift built and ran on an emulator with C++ interoperability enabled and extern "C" removed.
  • Scoped act formatting, ShellCheck, and sample checks using local workflow adaptations. The full unmodified workflow and Linux AMD64 execution were not verified locally; GitHub CI remains to be confirmed.

Preserve the runtime's C JNI types in generated entry points and calls.
Expose typed JNI interface getters and an object-value factory so Android
consumers do not reimport JNI signatures using the NDK's C++ wrappers.
Update generator and macro expectations for the preserved types.

Allow the JavaKit and JNI extraction samples to opt into C++ interoperability
on consumer targets only. Exercise this mode in existing stable Swift CI
jobs, including one Android configuration, and rerun Gradle tests across
configurations without reusing a daemon from another Swift toolchain.

Validation: 820 package tests; normal and C++ sample tests on macOS and
Linux ARM64; Android JNI sample build; hello-cpp-swift emulator execution.
Scoped act format, shell, and sample checks passed; the full GitHub Actions
workflow and Linux AMD64 execution remain unverified.
@NakaokaRei
NakaokaRei requested a review from ktoso as a code owner October 5, 2026 10:51
Preserve main's Java exception conversion while keeping C-compatible JNI
calls in generated async thunks. Use JNITypes.jthrowable for the new error
conformances and update closure expectations added since the original fix.

Validated with 839 Swift tests and 270 macOS C++ JNI sample tests.

@ktoso ktoso left a comment

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.

Overall supportive of this but I had a few notes

Comment thread Sources/SwiftJava/JNITypes.swift Outdated
///
/// Use these aliases in generated code so C++-enabled consumers share the
/// runtime's types instead of importing Android's C++ JNI wrapper types.
public enum JNITypes {

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.

Hmmm, shouldn't these be used from swift-java jni core?

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.

Thanks. Moved the helpers to swift-java-jni-core in swiftlang/swift-java-jni-core#25, with a C++ interop test.

Comment thread .github/workflows/pull_request.yml Outdated
chmod 500 install-and-build-with-sdk.sh
./install-and-build-with-sdk.sh --android --build-command="swift build" --android-sdk-triple="${{ matrix.sdk_triple }}" --android-ndk-version="${{ matrix.ndk_version }}" ${{ matrix.swift_version }}
- name: Verify Android JNI consumer with C++ interoperability
if: matrix.swift_version == '6.3' && matrix.sdk_triple == 'aarch64-unknown-linux-android28' && matrix.ndk_version == 'r27d'

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.

Suggested change
if: matrix.swift_version == '6.3' && matrix.sdk_triple == 'aarch64-unknown-linux-android28' && matrix.ndk_version == 'r27d'
if: matrix.sdk_triple == 'aarch64-unknown-linux-android28' && matrix.ndk_version == 'r27d'

I don't think we need to lock this to 6.3, do we?

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.

Removed the Swift 6.3 restriction.

Comment thread .github/workflows/pull_request.yml Outdated
if: matrix.swift_version == '6.3' && (matrix.sample_app == 'JavaKitSampleApp' || matrix.sample_app == 'SwiftJavaExtractJNISampleApp')
env:
CXX_INTEROP: '1'
SWIFT_JAVA_VERBOSE: true

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.

do we need to keep this in CI?

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.

Removed SWIFT_JAVA_VERBOSE from the added steps.

// Enable C++ interoperability only in the consumer targets, not their dependencies.
let sampleSwiftSettings: [SwiftSetting] =
[.swiftLanguageMode(.v5)]
+ (Context.environment["CXX_INTEROP"] == "1" ? [.interoperabilityMode(.Cxx)] : [])

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.

Hmm, I wonder if this should be SWIFT_JAVA_CXX_INTEROP?

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.

Renamed to SWIFT_JAVA_CXX_INTEROP in both samples and CI.

@ktoso ktoso left a comment

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.

primarily because the JNITypes question

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.

JavaKitSampleApp can't build with .interoperabilityMode(.Cxx) in swiftSettings

2 participants