Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

j2objc cannot compile code that can be compiled with Java 8 #825

Closed
zubchenok opened this issue Jan 6, 2017 · 22 comments
Closed

j2objc cannot compile code that can be compiled with Java 8 #825

zubchenok opened this issue Jan 6, 2017 · 22 comments

Comments

@zubchenok
Copy link

zubchenok commented Jan 6, 2017

I found this code pattern two times in RxJava library. It compiles and works in Java, but cannot be compiled/translated with j2objc.

public class Hello
{
    public static class Bar
    {
    }

    public static class Foo<T>
    {
    }

    public static void main(String[] args)
    {
        Foo<? extends Bar> first = null;
        Foo second = null;
        Foo<Bar> result = Hello.create1(first, second); // j2obc Compilation fails here
    }

    private static <T> Foo<T> create1(Foo<T> sourceA, Foo<T> sourceB)
    {
        return null;
    }
}

j2objc fails:

$ j2objc Hello.java
error: Hello.java:15: Type mismatch: cannot convert from Hello.Foo<capture#1-of ? extends Hello.Bar> to Hello.Foo<Hello.Bar>


References to RxJava sources that fail:

  1. https://github.com/ReactiveX/RxJava/blob/2.x/src/main/java/io/reactivex/internal/operators/flowable/FlowableReplay.java#L131
    FlowableReplay.java:131: error: Type mismatch: cannot convert from ConnectableFlowable<capture#22-of ? extends T> to ConnectableFlowable<T>
  2. https://github.com/ReactiveX/RxJava/blob/2.x/src/main/java/io/reactivex/internal/operators/observable/ObservableReplay.java#L126
    ObservableReplay.java:126: error: Type mismatch: cannot convert from ConnectableObservable<capture#19-of ? extends T> to ConnectableObservable<T>
@zubchenok
Copy link
Author

Any ideas how to fix it without modifications to RxJava?

@kstanger
Copy link
Collaborator

kstanger commented Jan 6, 2017

There's a good chance these errors will be fixed when we switch our parser front-end from Eclipse JDT to Javac. This is coming soon.

@zubchenok
Copy link
Author

I had seen Tom's message that javac front-end will be in a month later. It was on Dec 13. Do you have any update on the date? I'll be happy to run javac tests on my projects.

@tomball
Copy link
Collaborator

tomball commented Jan 6, 2017 via email

@zubchenok
Copy link
Author

$ export J2OBJC_FRONT_END=JAVAC
$ ./j2objc Hello.java
error: Hello.java:15: Type mismatch: cannot convert from Hello.Foo<capture#1-of ? extends Hello.Bar> to Hello.Foo<Hello.Bar>

Seems v1.2 is not compatible with JAVAC yet. So I have to pull and build, I will do it and update you soon.

@zubchenok
Copy link
Author

My test file has been successfully compiled with JAVAC.
I will try to translate the whole project and test how it works tomorrow.

@tomball
Copy link
Collaborator

tomball commented Jan 10, 2017

I just verified that both your test class and the RxJava project build with the javac front-end. To try this, pull or create your j2objc project clone to get the latest source, run "make -j4 dist" in the top-level directory, then run j2objc with the -Xuse-javac flag or the J2OBJC_FRONT_END=JAVAC environment variable. I did the latter, cloned the RxJava and reactive-streams-jvm projects, then ran the following build command in the RxJava top directory:

$ j2objc -l -d /tmp/reactive -sourcepath src/main/java:../reactive-streams-jvm/api/src/main/java \
`find src/main/java ../reactive-streams-jvm/api/src/main/java -name *.java

We'll close this issue when the javac front-end becomes the default.

@zubchenok
Copy link
Author

zubchenok commented Jan 13, 2017

After a busy start of the week, I've finally done tests. There were too many differences if I compare the latest 1.2 release with the latest j2objc version. So I first translated all code with the latest j2objc version and Eclipse JDT, and then started comparison with javac.

My findings:

  1. yes, differences in format of comment
  2. many classes have changed modifiers of J2ObjcClassInfo: 0x8008 to 0x8018 (did not find what is 0x10 bit for, could you tell?)
  3. [!] we have several modules, and when I use javac front-end, base module compiles, but others - don't. Dependent modules have sources of base module in class path only. this is a real my problem now.
  4. sometimes another order of methods
  5. several local variables defined in single line are all translated each in its own line
  6. original parameter names in constructors (was param0, now real variable name)
  7. found differences in comparators implementation/names
  8. differences in casting: when you set value to a field of base class:
B extends A, A has a field
method(B b);
b.field = 123; -- here is casting and nil check, javac casts to B, Eclipse JDT casts to class A
  1. [!] missed initialization in anonymous class like
    private static final Map<String, String> test = new HashMap<String, String>()
    {{
        put("123", "123");
    }};

... to be continued :)

Summary: the real problems are points 3 & 9.
I would be happy to provide more details on any case on your request.
If I can compile and run, I'll probably tell you more.

@zubchenok
Copy link
Author

UPDATE:
3. point 3 is solved: I had the base module source folders in the CLASSPATH of the dependent module (I don't know exactly a reason but It works with Eclipse JDT), so the issue had gone when I added the base module sources to the SOURCEPATH of the dependent module. Please let me know if it is not correct.

Now I have it all translated and I continue... :-)

@tomball
Copy link
Collaborator

tomball commented Jan 13, 2017 via email

@tomball
Copy link
Collaborator

tomball commented Jan 14, 2017

The 0x10 modifier is FINAL. I ran j2objc over the test class below with both front-ends and compared their .h and .m files. The only difference was that the javac anonymous class is marked as FINAL, while the JDT one isn't (all other classes had identical declarations, implementations, and metadata).

Since anonymous classes can't be extended, they are final whether their modifiers have the FINAL flag or not. Since anonymous class files are marked FINAL (use javap -v to see a class's flags), I think this is a harmless JDT bug and the javac front-end is correct. I'd like to therefore close point 2.

import java.io.Serializable;

class ClassTypes {
  static class InnerClass {}
  static interface InnerInterface {}
  static enum InnerEnum { FOO; }
  static @interface InnerAnnotation {}
  static final Serializable anonymousClass = new Serializable() {};
}

@tomball
Copy link
Collaborator

tomball commented Jan 14, 2017

I can replicate point 9 (thanks for the example), and will fix it this weekend. Although we tested literally thousands of lines of code, this is probably the first double-brace initialization run through the new front-end. Even though that's considered an anti-pattern, we'll still fix it before switching the default. Even nasty code that compiled before needs to be supported! :-)

@zubchenok
Copy link
Author

zubchenok commented Jan 15, 2017

  1. It's my wrong configuration issue. As you suggested Tom, I checked javac with my old configuration - it does not compile when I add source folders to classpath. As well as new j2objc doesn't translates with the -Xuse-javac flag. However j2objc with Eclipse JDT translates successfully in this case.
    (I'm not sure if you guys are interested in this minor issue with Eclipse JDT)

A few my notes more:

  1. Implementation of a private method was previously sometimes translated with usage of a static method, now is better. 👍

  2. Now we have parameter names in constructors, I have a constructor which has a parameter with name 'selector' which looks like reserved in ios. Probably the same note could be about parameter names for methods.
    I suspect and note it because you asked to write about what I find :), and AppCode highlights this parameter as reserved which actually could be an issue of AppCode. I guess it will compile without problems, but the general question is about valid/invalid reserved words for parameter names.

@zubchenok
Copy link
Author

zubchenok commented Jan 15, 2017

Currently I'm facing the issue with my new Java code, it translates to objective c successfully, but I cannot compile it. Trying to get what is going on...

Update 1: I have the same problem with j2objc 1.2

Update 2: Heh... got 'j2objc: Argument list too long' error. Is there a way to get recursively for translation all java files in source folder? Or specify list of java files in file? What is the best way to handle long list of files for translation?

Update 3: Got my sources translated & compiled after modification of java code. Trying to get a sample for you...

Update 4: To avoid 'Argument list too long' I tried to use relative path to java sources, but j2objc hangs and uses 100% in some infinite loop... This looks like an issue. Reverting back to absolute paths for now.
--- UPDATE for Update 4: No, this is not real. I found later that there is a huge performance impact when javac translation is enabled. Maximum impact is when with --batch-translate-max is not specified at all, so I guessed an infinite loop. Check below for the details and workaround - point 13.

@zubchenok
Copy link
Author

zubchenok commented Jan 15, 2017

  1. [!] Ok, finally I have the sample for you, 3 public classes:
    public class Base extends Factory { }
    public class Item extends Base { }
    public class Factory { public class FactoryItem extends Item { } }

The case can be simplified a little bit more and it will fail too. This is not exactly my case, but probably will help you:
public class Base extends Factory { }
public class Factory { public class FactoryItem extends Base { } }

This translates, but cannot compile, I tried:
j2objc 1.2
j2objc 1.3 Eclipse JDT front-end
j2objc 1.3 javac front-end

Any ideas?

@zubchenok zubchenok reopened this Jan 15, 2017
@tomball
Copy link
Collaborator

tomball commented Jan 15, 2017 via email

@zubchenok
Copy link
Author

  1. [!!!] There is a huge performance impact when javac translation is enabled. A little of statistics:
  • My test with Eclipse JDT front-end completes in 1:36 (with --batch-translate-max=4, more than 4 has no more boost of performance, I have 4 cores CPU at my build machine (2 core with hyperthreading))

  • With javac front-end translation is finished in 11 minutes (with --batch-translate-max=4). However when I set value of --batch-translate-max parameter equal to more than number of files to translate, like --batch-translate-max=65536, I got performance boost and all my test translates are completed just in 1:13.

So obviously there must be an issue that depends on number of files to translate. I believe now it will be easy to find a bug. :)

@tomball
Copy link
Collaborator

tomball commented Jan 18, 2017 via email

@kstanger
Copy link
Collaborator

Regarding item #12, it looks like your example has circular inheritance dependencies that would cause #include cycles in your headers. Do you use the --no-segmented-headers flag? (more info)

@kstanger
Copy link
Collaborator

Regarding #11, this doesn't appear to be a regression between JDT and JAVAC front ends. If there are specific reserved words that we're not renaming, could you file a separate issue?

@zubchenok
Copy link
Author

zubchenok commented Jan 19, 2017

  1. Yes, we use --no-segmented-headers.

  2. No, I have nothing to file. :) As I noted, this is an issue of AppCode.

@kstanger
Copy link
Collaborator

--no-segmented-headers can result in compile errors if your Java code has class hierarchies that create circular include patterns. Try removing the flag if this is an issue for you. (or reorganize your Java code)

Thanks very much for your help with testing our Javac front end! I'm going to close this issue because I believe we've resolved all the items.

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

No branches or pull requests

3 participants