Repository navigation
jextract: choose what to export with @JavaExport(false) and exportMode - #951
Open
piercifani wants to merge 4 commits into
Open
piercifani wants to merge 4 commits into
piercifani wants to merge 4 commits into
Conversation
JavaExtractDecider meant to skip prefix and postfix operators by checking the name token for .prefixOperator or .postfixOperator. In a declaration such as `public static prefix func - (value: Number) -> Number` the name is lexed as a binary operator, and only the `prefix` modifier tells it apart. So the operator was extracted as a binary operator, and in JNI mode the Swift thunk printer stopped with `Binary operator must have exactly 2 arguments`. Check the modifiers instead. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
jextract exports every declaration that passes the access level, and the only way to leave one out is a file or type pattern in swift-java.config. - `@JavaExport(false)` keeps a declaration out of the bindings. On a type or an extension it applies to all their members and nested types. - `"exportMode": "annotated"` exports only declarations marked `@JavaExport` and the members of marked types and extensions; an unmarked type is still exported, with only its marked members, when one of them is marked. The default, `all`, keeps today's behavior. Both are decided in JavaExtractDecider, so declarations that aren't exported are never translated. ExtractDecider gains mayExtractMembers(of:), so an extension of an unresolved type is not reported as skipped when none of its members can be exported. The JavaExport macro moves to a new SwiftJavaAnnotations module without the JNI dependency, so code that is also built without Java can carry the marker. SwiftJava re-exports it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nexported 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.
Problem
jextract exports every declaration that passes the access level. To leave something out, the only option is a file or type pattern in
swift-java.config(swiftFilterExclude). You can't leave out a single member, and a module can't export only the few declarations it means to.In a large codebase this gets in the way:
[String: Any], Combine publishers orCodableplumbing, is reported as skipped on every run.Running jextract over every public declaration of a production codebase with about 2,900 skipped declarations showed the scale. About 2,000 of those skips, and three quarters of the declarations whose generated Java didn't compile, were in code the codebase doesn't need in Java.
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.Both are decided in
JavaExtractDecider. The nearest marked enclosing declaration is found by walking up the syntax tree, so extensions and nested types need nothing extra. Declarations that aren't exported never reach translation.ExtractDecider.mayExtractMembers(of:), defaulting totrue. An extension of a type that doesn't resolve, such as a Foundation type, is no longer reported as skipped when none of its members can be exported. Without this, annotated mode still warned about every such extension.Unexported types. A declaration whose signature uses a type that isn't exported is skipped with "uses 'T', which is not exported", instead of generating Java that refers to a missing class.
ExtractDecider.isExported(_:)answers this for types from the module and from its dependencies, using each dependency'sexportModefrom itsswift-java.config.Foundation types in annotated mode. The
Data,DateandURLtypes jextract extracts when a module uses them are never filtered out by export mode, since SwiftExtract describes them itself and they carry no markers.A new
SwiftJavaAnnotationsmodule and product holds theJavaExportmacro, with no dependency on the JNI runtime. Code that is also built for platforms without Java can then carry the marker.SwiftJavare-exports it, so existing@JavaExportuses compile unchanged.Docs: the new option is in
SwiftJavaConfigFile.md(regenerated withgenerate-config-docs), andSwiftJavaJextract.mdgains a "Choosing what to export" section.Open to other spellings, for example
@JavaExport(.never)or a separate@JavaIgnore, or a different name forexportMode.Testing
JNIExportControlTests(7 tests):DateandURLtypes;swift testpasses.🤖 Generated with Claude Code