Skip to content

fix: take Map method IDs from the interface instead of from HashMap - #290

Merged
devopvoid merged 1 commit into
mainfrom
fix/jni-map-method-id
Sep 19, 2026
Merged

devopvoid merged 1 commit into
mainfrom
fix/jni-map-method-id

Conversation

@devopvoid

Copy link
Copy Markdown
Owner

The bug

jni::JavaHashMap in the vendored jni-voithos helper resolved put() and entrySet() against java.util.HashMap and then called them on whatever map it held. One of its constructors takes any java.util.Map, and PeerConnectionFactory hands it the field trials map the application passed in, which is rarely a HashMap: Collections.singletonMap, Collections.emptyMap, Map.of and TreeMap are all other classes. RTCRtpCodecCapability and RTCRtpCodecParameters wrap caller-supplied maps through the same helper.

Using a method ID from one class on an object of another is undefined. HotSpot tolerates it in a normal run, so the tests pass, but the JVM aborts under -Xcheck:jni:

FATAL ERROR in native method: Wrong object class or methodID passed to JNI call
    at dev.onvoid.webrtc.PeerConnectionFactory.initialize(Native Method)

It aborted partway through PeerConnectionFactoryTests, which left mvn -pl webrtc test -Pjni-check unusable as a gate on any branch.

The fix

Both method IDs now come from the java.util.Map interface, where dispatch handles whichever implementation arrives. This is the way JavaMapIterator already resolves Set, Iterator and Map.Entry. The constructor ID stays on HashMap, since it is only used to create one.

Testing

The new createWithFieldTrialsOfAnyMapType builds the factory from a singleton map, an empty map, a TreeMap, a ConcurrentHashMap and an unmodifiable wrapper. None of them extends HashMap, and LinkedHashMap is deliberately left out because it does. The test passes either way on its own and only fails under -Pjni-check, which is where this class of bug is visible.

The full suite runs green under -Pjni-check on this branch: 151 tests, no fatal JNI errors. Before the fix the same run aborted in createWithInvalidFieldTrials.

This branch does not overlap with #289 and merges cleanly with it in either order.

jni::JavaHashMap resolved put() and entrySet() against java.util.HashMap and
then called them on whatever map it held. One of its constructors takes any
java.util.Map, and PeerConnectionFactory hands it the field trials map the
application passed in, which is rarely a HashMap. Collections.singletonMap,
Collections.emptyMap, Map.of and TreeMap are all other classes.

Using a method ID from one class on an object of another is undefined. It
happens to work on HotSpot, so the tests pass, but the JVM aborts under
-Xcheck:jni:

  FATAL ERROR in native method: Wrong object class or methodID passed to JNI call
      at dev.onvoid.webrtc.PeerConnectionFactory.initialize(Native Method)

It aborted partway through the suite, which left
"mvn -pl webrtc test -Pjni-check" unusable as a gate on any branch.

Both methods now come from the java.util.Map interface, where virtual dispatch
handles whichever implementation arrives, the way JavaMapIterator already
resolves Set, Iterator and Map.Entry. The class keeps its HashMap reference
for the constructor that creates one. RTCRtpCodecCapability,
RTCRtpCodecParameters and RTCStats wrap caller-supplied maps through the same
helper and are fixed along with it.

The new test builds the factory from several map implementations, none of them
a HashMap. It passes either way on its own and only fails the -Pjni-check
profile, which is where this class of bug is visible.
@devopvoid
devopvoid merged commit 315bb99 into main Sep 19, 2026
11 checks passed
@devopvoid
devopvoid deleted the fix/jni-map-method-id branch September 20, 2026 17:21
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