Problem
The jni backend's trait-bridge registration is a stub: the generated Java_*_nativeRegister<Trait> shim creates a global reference to the impl object and then discards it — the generated code says so itself ("this accepts the registration but does not marshal trait method calls" / "Full method marshalling will be added in a follow-up phase", src/backends/jni/templates/trait_register_shim.rs.jinja). Meanwhile the Java/Kotlin side emits a complete host surface: IOcrBackend with a dozen methods, adapters, and a registry that accepts implementations.
Concretely in kreuzberg: a Kotlin Android or Java host can implement IOcrBackend, call OcrBackendBridge.register(impl), and everything appears to succeed — but no extraction will ever dispatch to the object, because the Rust side never wires the global ref into an OcrBackend implementation. The typed surface advertises a plugin system that doesn't exist yet on this backend, which is how the gap ships unnoticed.
This is also why the defaulted-method forwarding work (#167) excludes jni: there is nothing to forward through.
Solution
Implement the jni trait bridge the way the other backends do: a generated wrapper struct holding the GlobalRef + cached name, an impl Trait whose methods attach to the JVM and call the host object's methods (marshalling params/returns the same way the existing JNI function shims do), Plugin lifecycle via the interface's default methods, and registration that hands the wrapper to the host crate's register_fn. Once dispatch exists, the defaulted-method forwarding from #167 applies: presence can be decided per method at registration via reflection (Method.getDeclaringClass() != I<Trait>.class), falling back to the Rust default body through the shared delegate mechanism.
Problem
The jni backend's trait-bridge registration is a stub: the generated
Java_*_nativeRegister<Trait>shim creates a global reference to the impl object and then discards it — the generated code says so itself ("this accepts the registration but does not marshal trait method calls" / "Full method marshalling will be added in a follow-up phase",src/backends/jni/templates/trait_register_shim.rs.jinja). Meanwhile the Java/Kotlin side emits a complete host surface:IOcrBackendwith a dozen methods, adapters, and a registry that accepts implementations.Concretely in kreuzberg: a Kotlin Android or Java host can implement
IOcrBackend, callOcrBackendBridge.register(impl), and everything appears to succeed — but no extraction will ever dispatch to the object, because the Rust side never wires the global ref into anOcrBackendimplementation. The typed surface advertises a plugin system that doesn't exist yet on this backend, which is how the gap ships unnoticed.This is also why the defaulted-method forwarding work (#167) excludes jni: there is nothing to forward through.
Solution
Implement the jni trait bridge the way the other backends do: a generated wrapper struct holding the
GlobalRef+ cached name, animpl Traitwhose methods attach to the JVM and call the host object's methods (marshalling params/returns the same way the existing JNI function shims do),Pluginlifecycle via the interface'sdefaultmethods, and registration that hands the wrapper to the host crate'sregister_fn. Once dispatch exists, the defaulted-method forwarding from #167 applies: presence can be decided per method at registration via reflection (Method.getDeclaringClass() != I<Trait>.class), falling back to the Rust default body through the shared delegate mechanism.