cincheo / jsweet

A Java to JavaScript transpiler.
http://www.jsweet.org
Other
1.45k stars 159 forks source link

Iterable interface cannot be extended #731

Open vorth opened 1 year ago

vorth commented 1 year ago

This is a regression, as it was working in 2.3.7.

I suspect the default methods in Iterable are the issue, but it is just a guess.

Paste this code into the live sandbox to demonstrate the spurious compile error:

package org.jsweet;

import static def.dom.Globals.*;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;

public class HelloWorld {
    public static void main(String[] args) {
        WrappedList list = new WrappedListImpl();  // WrappedList interface inherits the iterable method, and yet...
        for( int num : list ) { // TSC fails with "property 'iterator' does not exist on type 'WrappedList'"
            System.out.println( num );
        }
    }
}

public interface WrappedList extends Iterable<Integer> {

    public void doSomething();
}

public class WrappedListImpl implements WrappedList {

    private List<Integer> nums = new ArrayList<>();

    public WrappedListImpl() {
        this.nums.add( 3 );
        this.nums.add( 5 );
        this.nums.add( 8 );
    }

        public Iterator<Integer> iterator() {
        return this.nums.iterator();
    }

    public void doSomething() {
    }
}
vorth commented 1 year ago

It seems that the extends Iterable<Integer> gets erased by design:

https://github.com/cincheo/jsweet/blob/ac8ef70abdd7665bb61fa82af53c09baa9c789f5/transpiler/src/main/java/org/jsweet/transpiler/Java2TypeScriptTranslator.java#L1722

I don't understand why this is designed in this way. Can someone explain? If the interface extension is erased, shouldn't its methods be inlined?

lgrignon commented 1 year ago

Standard JSweet behavior (without j4ts) is to remove java references and replace it with a JS implementation. Since Iterable is not a JS type, it won't appear at runtime.

What you see in the code you linked above is a call to an adapter, which allows to switch behavior when necessary, for instance if you need to keep some type in a specific project. By default, the RemoveJavaDependenciesAdapter is used, but any implementation can be used.

Is it a problem that this super interface gets erased in TypeScript? I honestly doubt this is related to this issue?

vorth commented 1 year ago

I believe the error comes from the TypeScript compiler:

Line 11: property 'iterator' does not exist on type 'WrappedList'

If you look at the generated TypeScript, you can see that the problem is a TS source issue with the missing iterator() method.

I'll take a look at RemoveJavaDependenciesAdapter. It seems wrong to remove superinterfaces without inlining them, in general. I suspect many of my JSweet transpile errors are a result of this.

lgrignon commented 1 year ago

Yes, methods are supposed to be inlined indeed. This sounds like a bug.

You will find somewhere in the translator some code to handle inlining of default methods specifically.