eclipse-jdt / eclipse-jdt/eclipse.jdt.core

ECJ synthesizes bridge method referencing java.util.SequencedMap when compiling -8/-source 8 on a JDK 21+ host, unlike javac

Open
#5,389 1 comment 0 reactions 1 assignee Claimed by @jarthana View on GitHub
Dominant language
Java
Stars
237
Forks
195
Avg merge
1d 10h
Merged PRs (30d)
49

Description

When compiling a class that implements `java.util.SortedMap` (without overriding `reversed()`) at `-source 8 -target 8` (or `-8`) while the compiler itself is hosted on a JDK 21+ runtime, **without** `--release`/an explicit bootclasspath restriction, ECJ 3.46.200 synthesizes an extra bridge method:

```
public java.util.SequencedMap reversed();
aload_0
invokeinterface java/util/SortedMap.reversed:()Ljava/util/SortedMap;
areturn
```

`java.util.SequencedMap` does not exist before JDK 21, so any class file emitted in Java-8 format (major version 52) containing this method fails with `ClassNotFoundException`/`NoClassDefFoundError: java/util/SequencedMap` the moment `reversed()` is invoked (or at verification time, depending on the VM) on any JDK 8-20 runtime.

**Importantly, `javac` given the exact same flags on the exact same JDK 21 host does *not* synthesize this bridge.** It correctly omits it and produces a clean Java-8-compatible class. This shows the behavior is an ECJ-specific inconsistency, not merely an unavoidable consequence of compiling `-source 8` without `--release` on a newer JDK.

### Real-world impact

This was discovered via `org.apache.felix.resolver.util.OpenHashMap` (vendored in Eclipse Equinox's `org.eclipse.osgi` bundle), which implements `SortedMap` without overriding `reversed()`. Built by Tycho's `tycho-compiler-plugin` with `useJDK=BREE` (falls back to running JVM's API when no explicit `--release`/bootclasspath is set) on a JDK 21 build host, the resulting `org.eclipse.osgi` jar throws `ClassNotFoundException: java.util.SequencedMap` at runtime under JDK 17, breaking OSGi TCK test container startup. See https://github.com/eclipse-equinox/equinox/pull/295 for context.

### Standalone reproducer (no Maven/Eclipse needed)

A copy of the source / script is inline below; you can also download `org.eclipse.jdt.core.compiler.batch` from Maven Central (`org.eclipse.jdt:ecj`, matching build version) to compile MyMap.java with ECJ directly.

`MyMap.java`:
```java
import java.util.AbstractMap;
import java.util.Comparator;
import java.util.SortedMap;
import java.util.Set;

public class MyMap extends AbstractMap implements SortedMap {
public Comparator comparator() { return null; }
public SortedMap subMap(K fromKey, K toKey) { return null; }
public SortedMap headMap(K toKey) { return null; }
public SortedMap tailMap(K fromKey) { return null; }
public K firstKey() { return null; }
public K lastKey() { return null; }
public Set> entrySet() { return null; }
}
```

Compile with javac (JDK 21, no `--release`):
```
javac -source 8 -target 8 -nowarn -d out-javac MyMap.java
javap -p out-javac/MyMap.class
```
→ no `reversed()` method present at all, clean.

Compile with ECJ (same version Tycho uses, `org.eclipse.jdt.core.compiler.batch-3.46.200`):
```
java -jar org.eclipse.jdt.core.compiler.batch-3.46.200.v20260907-1115.jar -8 -nowarn -d out-ecj MyMap.java
javap -p -c out-ecj/MyMap.class
```

```
public java.util.SequencedMap reversed();
Code:
0: aload_0
1: invokeinterface #33, 1 // InterfaceMethod java/util/SortedMap.reversed:()Ljava/util/SortedMap;
6: areturn
```

### Expected behavior

ECJ should either:
- not synthesize this bridge at all when the effective target/source level is below 21 (matching javac's behavior), or
- refuse to compile / emit a clear error/warning if it cannot guarantee `-source`/`-target 8` compatibility without a bootclasspath restriction.

### Proposed test

I've drafted a regression test (`SortedMapReversedBridgeTest`) for `org.eclipse.jdt.core.tests.compiler`, see PR (linked below).

### Environment

- ECJ version: `3.46.200.v20260907-1115`
- Host JDK: 21 (Temurin)
- Reproduced with plain command-line ECJ, no Maven/Eclipse/Tycho involved.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.