RFR: 8329420: Java 22 (and 23) launcher calls default constructor although main() is static [v3]

Jan Lahoda jlahoda at openjdk.org
Wed Apr 17 06:34:25 UTC 2024


> Consider code like:
> 
> public class MainClass {
>     public MainClass() {
>         System.out.println("Constructor called!");
>     }
>     public static void main() {
>         System.out.println("main called!");
>     }
> }
> 
> and compile and run it, with preview enabled, like:
> 
> $ javac /tmp/MainClass.java 
> $ java --enable-preview -classpath /tmp MainClass
> Constructor called!
> main called!
> 
> 
> That is wrong, as the `main` method is static, and there is no need to create a new instance of the class.
> 
> The reason is that as launcher attempts to invoke the main method, it goes in the following order: 1) static-main-with-args; 2) instance-main-with-args; 3) static-main-without-args; 4) instance-main-without-args. But, for the instance variants, the code first creates a new instance of the given class, and only then attempts to lookup the `main` method, and will pass to the next option when the `main` method lookup fails. So, when invoking static-main-without-args, the current class instance may be created for instance-main-with-args, which will then fail due to the missing `main(String[])` method.
> 
> The proposed solution to this problem is to simply first do a lookup for the `main` method (skipping to the next variant when the given main method does not exist, without instantiating the current class).
> 
> There is also a relatively closely related problem: what happens when the constructor throws an exception?
> 
> public class MainClass {
>     public MainClass() {
>         if (true) throw new RuntimeException();
>     }
>     public void main() {
>         System.out.println("main called!");
>     }
> }
> 
> 
> when compiled an run, this produces no output whatsoever:
> 
> $ javac /tmp/MainClass.java 
> $ java --enable-preview -classpath /tmp MainClass
> $
> 
> 
> This is because any exceptions thrown from the constructor are effectively ignored, and the launcher will continue with the next variant. This seems wrong - the exception should be printed for the user, like:
> 
> $ java --enable-preview -classpath /tmp/ MainClass 
> Exception in thread "main" java.lang.RuntimeException
>         at MainClass.<init>(MainClass.java:3)
> 
> 
> This patch proposes to do that by not consuming the exceptions thrown from the constructor, and stop the propagation to the next variant.

Jan Lahoda has updated the pull request incrementally with three additional commits since the last revision:

 - Reflecting code formatting suggestion.
 - First lookup the main method, and only then the constructor.
 - Attempting to solve JDK-8329581 by only ignoring j.l.NoSuchMethodError

-------------

Changes:
  - all: https://git.openjdk.org/jdk/pull/18753/files
  - new: https://git.openjdk.org/jdk/pull/18753/files/2022aa5a..c007f61e

Webrevs:
 - full: https://webrevs.openjdk.org/?repo=jdk&pr=18753&range=02
 - incr: https://webrevs.openjdk.org/?repo=jdk&pr=18753&range=01-02

  Stats: 117 lines in 2 files changed: 73 ins; 5 del; 39 mod
  Patch: https://git.openjdk.org/jdk/pull/18753.diff
  Fetch: git fetch https://git.openjdk.org/jdk.git pull/18753/head:pull/18753

PR: https://git.openjdk.org/jdk/pull/18753


More information about the core-libs-dev mailing list