konsoletyper / teavm

Compiles Java bytecode to JavaScript, WebAssembly and C
https://teavm.org
Apache License 2.0
2.55k stars 261 forks source link

Issues with method references in teavm #861

Closed Ihromant closed 7 months ago

Ihromant commented 7 months ago

I found this issue on very simple case, but I suppose that something in method references support in teavm is broken. Consider following test:

@Test
    public void testComparator() {
        Map<Integer, Double> distances = new HashMap<>();
        distances.put(1, 5.0);
        distances.put(2, 2.0);
        distances.put(3, 3.0);
        distances.put(4, 4.0);
        assertEquals(Integer.valueOf(2), IntStream.rangeClosed(1, 4).boxed().min((i, j) -> Double.compare(distances.get(i), distances.get(j))).orElseThrow());
        assertEquals(Integer.valueOf(2), IntStream.rangeClosed(1, 4).boxed().min(Comparator.comparingDouble(distances::get)).orElseThrow());
    }

It fails in the second line despite implementation in TComparator is same as in unwrapped in first line. Firstly I thought that there is minor misprint in classlib, but I suppose that it's more complex issue that should be fixed globally.

konsoletyper commented 7 months ago

Did you try to debug it?

Ihromant commented 7 months ago

No, I assumed that it's somewhere deep after reviewing TComparator implementation (the onlyp lace where issue could be in classlib). Will take a look on generated JS, maybe there is a hint.

konsoletyper commented 7 months ago

But what about placing a breakpoint on an arbitrary line, stepping, etc?

Ihromant commented 7 months ago

In JS or where?

konsoletyper commented 7 months ago

On JS, for example

Ihromant commented 7 months ago

I'm usually trying to avoid debugging JS. But ok, will try to debug this issue.

Ihromant commented 7 months ago

I was able to locate what causes the issue. As I suspected, it is something wrong with method references.

int[] arr = new int[]{1, 2, 3};
IntUnaryOperator f3 = i -> arr[i];
// ok
assertEquals(2, f3.applyAsInt(1));
List<Integer> ints = List.of(1, 2, 3);
IntUnaryOperator f2 = ints::get;
// java.lang.RuntimeException: java.lang.AssertionError: expected:<2> but was:<0>
assertEquals(2, f2.applyAsInt(1));
List<Double> doubles = List.of(1.0, 2.0, 3.0);
ToDoubleFunction<Integer> f = doubles::get;
// java.lang.RuntimeException: java.lang.AssertionError: expected:<2.0> but was:<NaN>
assertEquals(Double.valueOf(2.0), (Double) f.applyAsDouble(1));
List<Integer> integers = List.of(1, 2, 3);
ToIntFunction<Integer> f1 = integers::get;
// java.lang.RuntimeException: java.lang.AssertionError: expected:<2> but was:< > (here is some strange symbol)
assertEquals(Integer.valueOf(2), (Integer) f1.applyAsInt(1));
Ihromant commented 7 months ago

Also I think that this fix should be merged with 0.9.0 and released as 0.9.1.