Skip to content

[jvm] fix self-recursing Object bridge for closures with non-Object typed return#12908

Open
kLabz wants to merge 1 commit into
developmentfrom
jvm/SAM-fix-2
Open

[jvm] fix self-recursing Object bridge for closures with non-Object typed return#12908
kLabz wants to merge 1 commit into
developmentfrom
jvm/SAM-fix-2

Conversation

@kLabz
Copy link
Copy Markdown
Contributor

@kLabz kLabz commented May 22, 2026

When a closure's typed return is a specific reference type (e.g. SyncStats) and a functional interface in the conversion set has an Object-erased abstract method also named invoke — the shape of Function0, Callable after erasure — the FI loop in jvmFunctions.generate_invoke synthesized an Object invoke() bridge but pointed it at (meth.name, meth.dargs, meth.dret). At that program point meth came from register_signature with dret already declassified to Object, so the bridge body emitted invokevirtual invoke()Object against itself. The later loop meth_typed block that would have produced the proper Object→typed bridge was then short-circuited by has_method (the slot was already taken). First invocation StackOverflowError'd.

Forward to (meth.name, arg_sigs, ret) — the typed invoke that was actually spawned earlier in generate_invoke — so the Object-returning bridge dispatches to the real implementation and casts the result on return.

Repro shape (also added as a misc/jvm test extending StructuralSam):

  public interface GenericInvoke<T> { T invoke(); }
  Listeners.runGenericInvoke(() -> new Boxed(7));

…yped return

When a closure's typed return is a specific reference type (e.g. SyncStats)
and a functional interface in the conversion set has an Object-erased
abstract method also named `invoke` — the shape of Function0<T>,
Callable<V> after erasure — the FI loop in jvmFunctions.generate_invoke
synthesized an `Object invoke()` bridge but pointed it at
(meth.name, meth.dargs, meth.dret). At that program point meth came from
register_signature with dret already declassified to Object, so the bridge
body emitted `invokevirtual invoke()Object` against itself. The later
`loop meth_typed` block that would have produced the proper Object→typed
bridge was then short-circuited by has_method (the slot was already taken).
First invocation StackOverflowError'd.

Forward to (meth.name, arg_sigs, ret) — the typed invoke that was actually
spawned earlier in generate_invoke — so the Object-returning bridge
dispatches to the real implementation and casts the result on return.

Repro shape (also added as a misc/jvm test extending StructuralSam):
  public interface GenericInvoke<T> { T invoke(); }
  Listeners.runGenericInvoke(() -> new Boxed(7));
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