Seth, this is not so much "compiler semantics" vs "launcher semantics" ... it's just "ClassLoader semantics", combined with the fact that an empty class path defaults to the current directory.

In the source launcher, the compiler is not worried about the classes in the current directory.

But the classes are executed in a class loader, in which the normal semantics[1] are to check the parent class loader first, before checking for a local definition. Using those normal semantics, any classes on the application class path (defaulting to the current directory) are found before those defined to the class loader. That's the problem that is being encountered here.

-- Jon

[1]https://docs.oracle.com/javase/10/docs/api/java/lang/ClassLoader.html#loadClass(java.lang.String,boolean)

On 9/14/18 3:00 PM, seth lytle wrote:
ah - compiler semantics vs launcher semantics, and the source code launcher lives in both worlds

javac refuses to compile two sources for the same class
javac will compile a source file for a class that exists on the classpath (and even overwrite a class on the classpath) java accepts two class files for the same class on the classpath (favors the first) java -jar accepts a jar containing classes that also exist on the classpath (favors the jar) java refuses to launch a source file for a class that exists on the classpath

the source code launcher "feels" like an outlier to me, but it could certainly go either way

and i guess the source code launcher is an outlier anyway, as it decouples the first toplevel class name from the file name












On Fri, Sep 14, 2018 at 1:49 PM, Jonathan Gibbons <jonathan.gibb...@oracle.com <mailto:jonathan.gibb...@oracle.com>> wrote:

    Seth,

    The reason for reporting an error is to prevent the "wrong" class
    being used!

    -- Jon


    On 09/14/2018 10:14 AM, seth lytle wrote:

        this behavior of throwing an error if the class is found twice
        on the
        classpath strikes me as unusual - afaik, in all other cases,
        java is fine
        with finding multiple implementations on the classpath and
        uses the first
        found (similar to the unix path, which also allows overriding
        by providing
        an explicit path) and this is really one of the powerful
        features of java -
        allowing you to swap out one class for another seamlessly. and
        for rapid
        prototyping, "java -cp target/classes:$cp
        src/myPackage/Stuff.java" runs
        much faster than "mvn package
        -Dexec.mainClass=myPackage.Stuff", and doubly
        so for mvnDebug










        On Fri, Sep 14, 2018 at 3:33 AM, Peter Levart
        <peter.lev...@gmail.com <mailto:peter.lev...@gmail.com>>
        wrote:

            Hi Jaikiran,

            Forwarding to compiler-dev as the core of source file
            launcher feature is
            produced there...

            The check for main class is performed after compilation
            (which actually
            produces the main class name).

            I think it would be possible to check for all classes
            compiled from the
            source file after compilation without to much
            complication. The compilation
            produces classes and stores them into a Map<String,
            byte[]>. The keySet()
            of that map is a Set of compiled class names. Each of them
            could be tested
            via .getResource() invoked upon the application class
            loader. The error
            could even point to the URL of the conflicting class file
            that way...

            Regards, Peter


            On 09/14/2018 07:36 AM, Jaikiran Pai wrote:

            Please consider this trivial code C.java:

            public class C {
                 public static void main(String[] args) throws Exception {
                     System.out.println("main() execution started");
                 }
            }



            ls

            C.java

            Similar to a previous discussion[1] while doing random
            testing, I ended
            up compiling C.java explicitly using javac:


            javac C.java
            ls

            C.java C.class

            and then at a later date tried to use the source file
            launcher feature
            of Java 11 (without realizing C.class was already present
            in the dir):


            java C.java

            This threw the error:

            error: class found on application class path: C

            Although the error isn't that clear for the reason I note
            in [2], having
            run into this before, I was aware what this meant and
            deleted the
            C.class and moved on. The important part here is that the
            source
            launcher noticed this condition and aborted even before it
            auto
            compiled(?) and launched and executed the main() of the
            program.

            Now consider a slight modification to that source file:

            public class C {
                 public static void main(String[] args) throws Exception {
                     System.out.println("main() execution started");
                     final B b = new B();
                     System.out.println("Done");
                 }

                 private static class B {

                 }
            }

            Again at some point I compiled this explicitly using
            javac, so my
            directory is (among other things):


            ls

            C$B.class    C.class        C.java

            Then ran the source file launcher feature:


            java C.java

            error: class found on application class path: C

            As expected, ran into the same previous error. As before,
            in order to
            move on, deleted C.class:


            rm C.class

            but forgot to delete the nested static class that belonged
            to it. So the
            directory now contained:


            ls

            C$B.class    C.java

            Now used the source launcher feature again:


            java C.java

            This time it failed with:

            main() execution started
            Exception in thread "main" java.lang.IllegalAccessError:
            failed to
            access class C$B from class C (C$B is in unnamed module of
            loader 'app';
            C is in unnamed module of loader
            com.sun.tools.javac.launcher.Main$MemoryClassLoader @1b1473ab)
                 at C.main(C.java:4)

            The error message isn't clear to pinpoint the issue, but
            at least the
            reference to MemoryClassLoader was a hint that was enough
            for me to
            understand where to look. It did take me a few minutes to
            realize that
            C$B.class was lying around which I needed to remove too.

            However, IMO, the important part here is that unlike in
            the first case
            where the program itself wasn't launched and instead was
            aborted early,
            in this case the program did get executed (notice the
            System.out.println
            "main() execution started" message that got printed) and
            failed at runtime.

            Would it be possible to make these two behaviours
            consistent and detect
            such cases and abort early here too? Or would that add too
            much
            complexity to this feature?

            Finally, any thoughts on the error messages for this
            feature to make it
            a bit easier in terms of debugging (classloading) issues
            like these?

            [1]
            http://mail.openjdk.java.net/pipermail/jdk-dev/2018-June/001425.html
            
<http://mail.openjdk.java.net/pipermail/jdk-dev/2018-June/001425.html>
            [2]
            http://mail.openjdk.java.net/pipermail/jdk-dev/2018-June/001438.html
            
<http://mail.openjdk.java.net/pipermail/jdk-dev/2018-June/001438.html>

            -Jaikiran







Reply via email to