8160698: java --dry-run should not cause main class be initialized
authormchung
Mon, 04 Jul 2016 09:13:13 -0700
changeset 39345 344e8211e184
parent 39344 7a2263aa5b3d
child 39346 e2a569f3bb54
8160698: java --dry-run should not cause main class be initialized Reviewed-by: ksrini
jdk/src/java.base/share/classes/sun/launcher/LauncherHelper.java
jdk/src/java.base/share/native/libjli/java.c
jdk/test/tools/launcher/modules/dryrun/DryRunTest.java
jdk/test/tools/launcher/modules/dryrun/src/test/jdk/test/MainWithClinit.java
--- a/jdk/src/java.base/share/classes/sun/launcher/LauncherHelper.java	Mon Jul 04 16:25:11 2016 +0300
+++ b/jdk/src/java.base/share/classes/sun/launcher/LauncherHelper.java	Mon Jul 04 09:13:13 2016 -0700
@@ -584,15 +584,17 @@
         Class<?> mainClass = null;
         ClassLoader scl = ClassLoader.getSystemClassLoader();
         try {
-            mainClass = scl.loadClass(cn);
+            mainClass = Class.forName(cn, false, scl);
         } catch (NoClassDefFoundError | ClassNotFoundException cnfe) {
             if (System.getProperty("os.name", "").contains("OS X")
                     && Normalizer.isNormalized(cn, Normalizer.Form.NFD)) {
                 try {
-                    // On Mac OS X since all names with diacretic symbols are given as decomposed it
-                    // is possible that main class name comes incorrectly from the command line
-                    // and we have to re-compose it
-                    mainClass = scl.loadClass(Normalizer.normalize(cn, Normalizer.Form.NFC));
+                    // On Mac OS X since all names with diacretic symbols are
+                    // given as decomposed it is possible that main class name
+                    // comes incorrectly from the command line and we have
+                    // to re-compose it
+                    String ncn = Normalizer.normalize(cn, Normalizer.Form.NFC);
+                    mainClass = Class.forName(ncn, false, scl);
                 } catch (NoClassDefFoundError | ClassNotFoundException cnfe1) {
                     abort(cnfe, "java.launcher.cls.error1", cn);
                 }
--- a/jdk/src/java.base/share/native/libjli/java.c	Mon Jul 04 16:25:11 2016 +0300
+++ b/jdk/src/java.base/share/native/libjli/java.c	Mon Jul 04 09:13:13 2016 -0700
@@ -435,7 +435,8 @@
     ret = 1;
 
     /*
-     * Get the application's main class.
+     * Get the application's main class. It also checks if the main
+     * method exists.
      *
      * See bugid 5030265.  The Main-Class name has already been parsed
      * from the manifest, but not parsed properly for UTF-8 support.
@@ -467,6 +468,16 @@
      */
     appClass = GetApplicationClass(env);
     NULL_CHECK_RETURN_VALUE(appClass, -1);
+
+    /* Build platform specific argument array */
+    mainArgs = CreateApplicationArgs(env, argv, argc);
+    CHECK_EXCEPTION_NULL_LEAVE(mainArgs);
+
+    if (dryRun) {
+        ret = 0;
+        LEAVE();
+    }
+
     /*
      * PostJVMInit uses the class name as the application name for GUI purposes,
      * for example, on OSX this sets the application name in the menu bar for
@@ -476,6 +487,7 @@
      */
     PostJVMInit(env, appClass, vm);
     CHECK_EXCEPTION_LEAVE(1);
+
     /*
      * The LoadMainClass not only loads the main class, it will also ensure
      * that the main method's signature is correct, therefore further checking
@@ -486,22 +498,15 @@
                                        "([Ljava/lang/String;)V");
     CHECK_EXCEPTION_NULL_LEAVE(mainID);
 
-    /* Build platform specific argument array */
-    mainArgs = CreateApplicationArgs(env, argv, argc);
-    CHECK_EXCEPTION_NULL_LEAVE(mainArgs);
+    /* Invoke main method. */
+    (*env)->CallStaticVoidMethod(env, mainClass, mainID, mainArgs);
 
-    if (dryRun) {
-        ret = 0;
-    } else {
-        /* Invoke main method. */
-        (*env)->CallStaticVoidMethod(env, mainClass, mainID, mainArgs);
+    /*
+     * The launcher's exit code (in the absence of calls to
+     * System.exit) will be non-zero if main threw an exception.
+     */
+    ret = (*env)->ExceptionOccurred(env) == NULL ? 0 : 1;
 
-        /*
-         * The launcher's exit code (in the absence of calls to
-         * System.exit) will be non-zero if main threw an exception.
-         */
-        ret = (*env)->ExceptionOccurred(env) == NULL ? 0 : 1;
-    }
     LEAVE();
 }
 
--- a/jdk/test/tools/launcher/modules/dryrun/DryRunTest.java	Mon Jul 04 16:25:11 2016 +0300
+++ b/jdk/test/tools/launcher/modules/dryrun/DryRunTest.java	Mon Jul 04 09:13:13 2016 -0700
@@ -60,6 +60,7 @@
 
     // the module main class
     private static final String MAIN_CLASS = "jdk.test.Main";
+    private static final String MAIN_CLINIT_CLASS = "jdk.test.MainWithClinit";
 
 
     @BeforeTest
@@ -99,13 +100,27 @@
         String dir = MODS_DIR.toString();
         String mid = TEST_MODULE + "/" + MAIN_CLASS;
 
-        // java -modulepath mods -module $TESTMODULE/$MAINCLASS
         // no resolution failure
         int exitValue = exec("--dry-run", "-modulepath", dir, "-m", mid);
         assertTrue(exitValue == 0);
     }
 
     /**
+     * Test dryrun that does not invoke <clinit> of the main class
+     */
+    public void testMainClinit() throws Exception {
+        String dir = MODS_DIR.toString();
+        String mid = TEST_MODULE + "/" + MAIN_CLINIT_CLASS;
+
+        int exitValue = exec("--dry-run", "-modulepath", dir, "-m", mid);
+        assertTrue(exitValue == 0);
+
+        // expect the test to fail if main class is initialized
+        exitValue = exec("-modulepath", dir, "-m", mid);
+        assertTrue(exitValue != 0);
+    }
+
+    /**
      * Test non-existence module in -addmods
      */
     public void testNonExistAddModules() throws Exception {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/tools/launcher/modules/dryrun/src/test/jdk/test/MainWithClinit.java	Mon Jul 04 09:13:13 2016 -0700
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.test;
+
+public class MainWithClinit {
+    static {
+        fail();
+    }
+
+    private static void fail() {
+        throw new RuntimeException("MainWithClinit::<clinit> invoked");
+    }
+
+    public static void main(String[] args) {
+        System.out.println("hi");
+    }
+}