Skip to content

JNI: bridge protocol requirement defaults to Java interface methods - #11

Merged
teamhimeh merged 1 commit into
qoncept-forkfrom
codex/bridge-jni-protocol-default-methods
Oct 23, 2026
Merged

teamhimeh merged 1 commit into
qoncept-forkfrom
codex/bridge-jni-protocol-default-methods

Conversation

@teamhimeh

Copy link
Copy Markdown
Collaborator

Problem

JNI callback interfaces currently leave every protocol requirement abstract, even when Swift supplies an unconditional implementation in an extension. Java implementors must repeat that implementation instead of inheriting the Swift behavior.

For example:

public protocol Greeter {
  func name() -> String
  func greeting() -> String
}

extension Greeter {
  public func greeting() -> String { "Hello, \(name())" }
}

With this change, greeting() is a Java interface default method that forwards to the Swift implementation. Java only needs to implement name():

Greeter greeter = () -> "World";
String message = greeter.greeting(); // "Hello, World"

The Swift body remains the implementation. Its calls to other requirements dispatch to the original Java receiver, preserving Java overrides of other defaulted methods.

Changes

  • Match requirement signatures to unconditional extension implementations while allowing different local parameter names.
  • Emit Java default methods and package-private native companion classes, since Java interfaces cannot declare native methods.
  • Generate one Swift receiver adapter per default. It inherits that specific Swift implementation and forwards every other requirement to the original receiver, avoiding recursive calls through the same Java default method.
  • Support defaults supplied by refined protocols for inherited requirements. Emit explicit callback forwarding witnesses where a refinement default would otherwise compete with a parent wrapper's witness.
  • Preserve qualified names for nested protocols and project inherited Java interfaces through their shared holder.
  • Handle Swift Int/Java long conversion and the generated Java method's exception declaration during callbacks.
  • Add generator regressions, Java runtime tests, and user documentation.

Scope

Default bridges require Java callbacks to be enabled and a callback-compatible protocol with synchronous, nongeneric instance-method requirements. Conditional implementations, attributed declarations (including global-actor isolation), property requirements, and extension-only convenience members remain outside this feature.

If every abstract method acquires a default, the Java interface is no longer functional; callers can use an anonymous implementation instead of a lambda. Existing Swift-backed boxes continue to dispatch their original witnesses.

The base branch must already distinguish protocol-body requirements from extension members and preserve inherited requirement names consistently.

Validation

  • Confirmed the new positive generator regressions fail before the fix; conditional implementations and disabled callbacks retain their existing behavior.
  • swift test --disable-experimental-prebuilts --disable-sandbox: full Swift test suite passed.
  • ./gradlew :Samples:SwiftJavaExtractJNISampleApp:test --no-daemon: all 290 Java tests passed with no failures or skips. New runtime coverage exercises inherited defaults, Java overrides of other defaults, exceptions, nested protocols, and refined protocols.
  • swift-format lint --strict on changed Swift files and git diff --check passed.

Tested with Apple Swift 6.4 and JetBrains Runtime 25 on macOS.

@teamhimeh
teamhimeh merged commit 4e1cac4 into qoncept-fork Oct 23, 2026
32 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