Skip to content

JNI: align protocol parameter wrapper names - #16

Merged
teamhimeh merged 1 commit into
qoncept-forkfrom
codex/fix-jni-protocol-setter
Oct 27, 2026
Merged

teamhimeh merged 1 commit into
qoncept-forkfrom
codex/fix-jni-protocol-setter

Conversation

@teamhimeh

@teamhimeh teamhimeh commented Oct 27, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

With Java callbacks enabled, JNI bindings for a public protocol-typed property can generate a Swift setter that references a wrapper class under a different name from its declaration. The generated Swift code then fails to compile.

For example, the JNI sample contains this API:

public protocol OverloadedCallback {
  func value() -> Int64
}

public final class ProtocolPropertyService {
  public var callback: any OverloadedCallback

  public init(callback: some OverloadedCallback) {
    self.callback = callback
  }

  public func read() -> Int64 {
    callback.value()
  }
}

Before this change, the wrapper declaration used the Swift property name, while the setter's argument conversion used the Java setter name:

// Generated wrapper declaration
final class _ProtocolPropertyService_callback_newValue_Wrapper: SwiftJavaOverloadedCallbackWrapper {
  // ...
}

// Generated setter argument conversion
newValueswiftObject$ = _ProtocolPropertyService_setCallback_newValue_Wrapper(
  _javaOverloadedCallbackInterface: JavaOverloadedCallback(javaThis: newValue!, environment: environment)
)

After this change, both the declaration and the setter conversion use _ProtocolPropertyService_setCallback_newValue_Wrapper. A Java implementation can therefore be assigned through the generated setter and called from Swift:

try (var arena = SwiftArena.ofConfined()) {
    var service = ProtocolPropertyService.init(() -> 10, arena);
    service.setCallback(() -> 42);
    assertEquals(42, service.read());
}

The same mismatch can occur when Java method names differ from Swift names for label-disambiguated overloads or Java keywords.

Changes

  • Use a shared protocol adapter naming rule for the Swift wrapper declaration and native argument conversion. Methods and setters use the translated Java method name; initializers retain the Swift init name so overloads can share an identical adapter even when their Java names are disambiguated.
  • Keep the Java method name used for closure translation unchanged.
  • Add generator regression coverage for existential property setters, label-disambiguated overloads, Java keywords, and initializer overloads. The tests check both wrapper declarations and references, including that shared initializer adapters are emitted only once.
  • Add the JNI sample above and a Java test that assigns a callback through the setter and calls it from Swift after assignment.

Validation

Tested on macOS 27.0.1 with Apple Swift 6.4.2 and JetBrains Runtime 25.0.2.

  • swift test --disable-experimental-prebuilts --disable-sandbox (full Swift suite).
  • ./gradlew :Samples:SwiftJavaExtractJNISampleApp:test --no-daemon (generated Swift and Java bindings compiled; all 297 Java tests passed, including the setter callback and existing initializer overload tests).
  • swift-format lint --strict on changed Swift files and git diff --check.

The callback was exercised with Java on macOS; an Android or Kotlin runtime was not tested.

Use the same wrapper name in generated declarations and JNI conversions when Java method names differ from Swift names. Preserve shared adapters for initializer overloads, and cover protocol property setters and Java callbacks.
@teamhimeh
teamhimeh merged commit 68258fa into qoncept-fork Oct 27, 2026
33 of 42 checks passed
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.

1 participant