Repository navigation
jextract: choose what to export with @JavaExport(false) and exportMode - #951
piercifani wants to merge 6 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>
|
I don't think this is quite right, I'll have to dive deeper here. We definitely would not spell it like that with a random "false". It's also not true that it's not possible to control importing per member level -- filterInclude and filterExclude can be combined to make that work. |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Thanks @ktoso, happy to change the spelling. The goal of this PR is to have an equivalent of Skip’s As far as I can tell, public final class Foo {
@JavaExport(.never)
public init() async throws { ... }
public static func create() async throws -> Foo { try await Foo() }
}Am I missing a combination? |
…binding-generator-core swiftlang#957 moved SwiftExtract out of this repository. This branch's analysis changes (ExtractDecider.mayExtractMembers(of:), isExported(_:) and isFromKnownModuleSource) now live in swift-binding-generator-core, so its edits to the deleted SwiftExtract files are dropped here. Until that change lands there, build with SWIFT_BINDING_GENERATOR_CORE_PATH pointing at a checkout that has it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Depends on swiftlang/swift-binding-generator-core#4, which adds the
ExtractDeciderhooks this uses (mayExtractMembers(of:),isExported(_:),isFromKnownModuleSource). SwiftExtract moved there in #957, so this PR no longer edits it. Until that PR is merged, build this branch withSWIFT_BINDING_GENERATOR_CORE_PATHpointing at a checkout of it.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.