Repository navigation
JNI: choose what jextract exports with @JavaExport(false) and exportMode - #26
Open
piercifani wants to merge 4 commits into
Open
piercifani wants to merge 4 commits into
piercifani wants to merge 4 commits into
Conversation
`@JavaExport(false)` keeps a declaration, or every member of a type or extension, out of the Java bindings. `"exportMode": "annotated"` exports only declarations marked `@JavaExport` and the members of marked types and extensions. Both are decided in JavaExtractDecider, so declarations that are not exported are never translated and can't be skipped, miscompiled or crash jextract. An extension of an unresolved type whose members can't be exported is no longer reported as skipped. Prefix and postfix operators are now recognized by their modifier: the name-token check let `prefix static func -` through to the thunk printer, which crashed. The JavaExport macro moves to a new SwiftJavaAnnotations module without the JNI dependency; SwiftJava re-exports it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rted types Two gaps in `exportMode: annotated`: - The `Data`, `Date` and `URL` wrapper types jextract extracts when a module uses them come from SwiftExtract's own description of Foundation, which has no `@JavaExport` markers, so annotated mode dropped them while exported declarations still referred to them. Declarations from SwiftExtract's known module sources are no longer filtered by export mode. - A declaration whose signature used a type that isn't exported, from this module or a dependency, produced Java referring to a class that doesn't exist. ExtractDecider.isExported(_:) now answers that, using each dependency's `exportMode` from its swift-java.config, and such declarations are skipped with "uses 'T', which is not exported". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stack 22/22 · base:
jni/21-error-values· previous: #25Problem
jextract exports every declaration that passes the access level, and the only way to leave one out is a file or type filter in
swift-java.config. A large module can't use jextract until it handles all of that module's public API, including Swift-only code that was never meant for Java:failOnSkippedDeclarations(JExtract: report every skipped declaration; add failOnSkippedDeclarations #3) can't be turned on.There's also no way to export only what a module chooses, member by member.
Measured on a production Skip Fuse app (9 Skip modules), with jextract run on all public declarations:
Separately,
JavaExtractDecidermeant to skip prefix and postfix operators but checked the name token.prefix static func -has a binary-operator name token and aprefixmodifier, so it got through and crashed the thunk printer (Binary operator must have exactly 2 arguments).Changes
@JavaExport(false)keeps a declaration out of the bindings. On a type or an extension it applies to all their members and nested types.@JavaExportkeeps working as before; the macro now takes an optionalBool."exportMode": "annotated"inswift-java.configexports only declarations marked@JavaExport, and the members of a type or extension marked@JavaExport. An unmarked type is still exported, with only its marked members, when one of its members is marked. The default,all, keeps today's behavior.JavaExtractDecider. The nearest marked enclosing declaration is found by walking up the syntax tree, so extensions and nested types need no extra plumbing. Declarations that aren't exported are never translated: they can't be reported as skipped, produce broken Java or crash.ExtractDecider.mayExtractMembers(of:), defaulttrue. An extension of a type jextract can't resolve, such as a Foundation type, is no longer reported as skipped when none of its members can be exported.SwiftJavaAnnotationsmodule and product holding theJavaExportmacro, with no JNI dependency, so code that's also built where there's no Java can carry the marker.SwiftJavare-exports it, so existing@JavaExportuses compile unchanged.Kotlin in this PR
ExportControlTest.ktwithExportControl.swift. The sample builds withfailOnSkippedDeclarations, so the opted-out tuple and[String: Any]members, and the prefix operator, would fail the build if they were still extracted:Why Skip needs it
Skip lets every module choose its Kotlin API:
// SKIP @bridge/@bridgeMembersto opt in,// SKIP @nobridgeto opt out, and a per-module default. The app measured above uses about 1,400 opt-ins and 1,600 opt-outs. With jextract run in annotated mode on a copy of that app where those comments became@JavaExportmarkers, skipped declarations fell from 2,870 to 994, and no module crashed jextract. What remains is code the app does export: the jextract gaps that still need fixing.Testing
JNIExportControlTests(4 tests):swift test: all suites pass../gradlew :Samples:SwiftJavaExtractJNIKotlinSampleApp:testpasses (51 tests, includingExportControlTest).swift run generate-config-docs;SwiftJavaJextract.mddocuments both features.🤖 Generated with Claude Code