diff --git a/src/java.base/share/classes/java/lang/Class.java b/src/java.base/share/classes/java/lang/Class.java index c95aa764ee6..766509f1f23 100644 --- a/src/java.base/share/classes/java/lang/Class.java +++ b/src/java.base/share/classes/java/lang/Class.java @@ -190,30 +190,21 @@ import sun.reflect.misc.ReflectUtil; * a class or interface is hidden has no bearing on the characteristics * exposed by the methods of class {@code Class}. * - *

Unnamed Classes

- * - * A {@code class} file representing an {@linkplain #isUnnamedClass unnamed class} - * is generated by a Java compiler from a source file for an unnamed class. - * The {@code Class} object representing an unnamed class is top-level, - * {@linkplain #isSynthetic synthetic}, and {@code final}. While an - * unnamed class does not have a name in its Java source - * form, several of the name-related methods of {@code java.lang.Class} - * do return non-null and non-empty results for the {@code Class} - * object representing an unnamed class. + *

Implicit Classes

* * Conventionally, a Java compiler, starting from a source file for an - * unnamed class, say {@code HelloWorld.java}, creates a + * implicit class, say {@code HelloWorld.java}, creates a * similarly-named {@code class} file, {@code HelloWorld.class}, where * the class stored in that {@code class} file is named {@code * "HelloWorld"}, matching the base names of the source and {@code * class} files. * - * For the {@code Class} object of an unnamed class {@code + * For the {@code Class} object of an implicit class {@code * HelloWorld}, the methods to get the {@linkplain #getName name} and * {@linkplain #getTypeName type name} return results * equal to {@code "HelloWorld"}. The {@linkplain #getSimpleName - * simple name} of such an unnamed class is the empty string and the - * {@linkplain #getCanonicalName canonical name} is {@code null}. + * simple name} of such an implicit class is {@code "HelloWorld"} and the + * {@linkplain #getCanonicalName canonical name} is {@code "HelloWorld"}. * * @param the type of the class modeled by this {@code Class} * object. For example, the type of {@code String.class} is {@code @@ -1809,7 +1800,7 @@ public final class Class implements java.io.Serializable, /** * Returns the simple name of the underlying class as given in the * source code. An empty string is returned if the underlying class is - * {@linkplain #isAnonymousClass() anonymous} or {@linkplain #isUnnamedClass() unnamed}. + * {@linkplain #isAnonymousClass() anonymous}. * A {@linkplain #isSynthetic() synthetic class}, one not present * in source code, can have a non-empty name including special * characters, such as "{@code $}". @@ -1822,9 +1813,6 @@ public final class Class implements java.io.Serializable, * @since 1.5 */ public String getSimpleName() { - if (isUnnamedClass()) { - return ""; - } ReflectionData rd = reflectionData(); String simpleName = rd.simpleName; if (simpleName == null) { @@ -1874,7 +1862,6 @@ public final class Class implements java.io.Serializable, *
    *
  • a {@linkplain #isLocalClass() local class} *
  • a {@linkplain #isAnonymousClass() anonymous class} - *
  • an {@linkplain #isUnnamedClass() unnamed class} *
  • a {@linkplain #isHidden() hidden class} *
  • an array whose component type does not have a canonical name
  • *
@@ -1894,9 +1881,6 @@ public final class Class implements java.io.Serializable, * @since 1.5 */ public String getCanonicalName() { - if (isUnnamedClass()) { - return null; - } ReflectionData rd = reflectionData(); String canonicalName = rd.canonicalName; if (canonicalName == null) { @@ -1931,33 +1915,12 @@ public final class Class implements java.io.Serializable, } } - /** - * {@return {@code true} if and only if the underlying class - * is an unnamed class} - * - * @apiNote - * An unnamed class is not an {@linkplain #isAnonymousClass anonymous class}. - * - * @since 21 - * - * @jls 7.3 Compilation Units - */ - @PreviewFeature(feature=PreviewFeature.Feature.UNNAMED_CLASSES, - reflective=true) - public boolean isUnnamedClass() { - return PreviewFeatures.isEnabled() && isSynthetic() - && isTopLevelClass() - && Modifier.isFinal(getModifiers()); - } - - /** * Returns {@code true} if and only if the underlying class * is an anonymous class. * * @apiNote * An anonymous class is not a {@linkplain #isHidden() hidden class}. - * An anonymous class is not an {@linkplain #isUnnamedClass() unnamed class}. * * @return {@code true} if and only if this class is an anonymous class. * @since 1.5 @@ -2922,6 +2885,21 @@ public final class Class implements java.io.Serializable, return result; } + /** + * Returns the most specific {@code Method} object of this class, super class or + * interface that have the specified method name and parameter types. + * + * @param publicOnly true if only public methods are examined, otherwise all methods + * @param name the name of the method + * @param parameterTypes the parameter array + * @return the {@code Method} object for the method found from this class matching + * the specified name and parameters, or null if not found + */ + Method findMethod(boolean publicOnly, String name, Class... parameterTypes) { + PublicMethods.MethodList res = getMethodsRecursive(name, parameterTypes, true, publicOnly); + return res == null ? null : getReflectionFactory().copyMethod(res.getMostSpecific()); + } + /** * Returns a {@code Constructor} object that reflects the specified * constructor of the class represented by this @@ -3750,7 +3728,7 @@ public final class Class implements java.io.Serializable, PublicMethods.MethodList res = getMethodsRecursive( name, parameterTypes == null ? EMPTY_CLASS_ARRAY : parameterTypes, - /* includeStatic */ true); + /* includeStatic */ true, /* publicOnly */ true); return res == null ? null : res.getMostSpecific(); } @@ -3759,9 +3737,10 @@ public final class Class implements java.io.Serializable, // via ReflectionFactory.copyMethod. private PublicMethods.MethodList getMethodsRecursive(String name, Class[] parameterTypes, - boolean includeStatic) { - // 1st check declared public methods - Method[] methods = privateGetDeclaredMethods(/* publicOnly */ true); + boolean includeStatic, + boolean publicOnly) { + // 1st check declared methods + Method[] methods = privateGetDeclaredMethods(publicOnly); PublicMethods.MethodList res = PublicMethods.MethodList .filter(methods, name, parameterTypes, includeStatic); // if there is at least one match among declared methods, we need not @@ -3775,15 +3754,14 @@ public final class Class implements java.io.Serializable, // we must consult the superclass (if any) recursively... Class sc = getSuperclass(); if (sc != null) { - res = sc.getMethodsRecursive(name, parameterTypes, includeStatic); + res = sc.getMethodsRecursive(name, parameterTypes, includeStatic, publicOnly); } // ...and coalesce the superclass methods with methods obtained // from directly implemented interfaces excluding static methods... for (Class intf : getInterfaces(/* cloneArray */ false)) { res = PublicMethods.MethodList.merge( - res, intf.getMethodsRecursive(name, parameterTypes, - /* includeStatic */ false)); + res, intf.getMethodsRecursive(name, parameterTypes, /* includeStatic */ false, publicOnly)); } return res; diff --git a/src/java.base/share/classes/java/lang/System.java b/src/java.base/share/classes/java/lang/System.java index 6c7de7c7798..0c100793167 100644 --- a/src/java.base/share/classes/java/lang/System.java +++ b/src/java.base/share/classes/java/lang/System.java @@ -2345,6 +2345,9 @@ public final class System { public List getDeclaredPublicMethods(Class klass, String name, Class... parameterTypes) { return klass.getDeclaredPublicMethods(name, parameterTypes); } + public Method findMethod(Class klass, boolean publicOnly, String name, Class... parameterTypes) { + return klass.findMethod(publicOnly, name, parameterTypes); + } public jdk.internal.reflect.ConstantPool getConstantPool(Class klass) { return klass.getConstantPool(); } diff --git a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java index f3ea43f17aa..93815009a54 100644 --- a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java +++ b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java @@ -65,6 +65,11 @@ public interface JavaLangAccess { */ List getDeclaredPublicMethods(Class klass, String name, Class... parameterTypes); + /** + * Return most specific method that matches name and parameterTypes. + */ + Method findMethod(Class klass, boolean publicOnly, String name, Class... parameterTypes); + /** * Return the constant pool for a class. */ diff --git a/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java b/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java index e9a9fdeb138..f7ce449b981 100644 --- a/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java +++ b/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java @@ -69,8 +69,10 @@ public @interface PreviewFeature { FOREIGN, @JEP(number=459, title="String Templates", status="Second Preview") STRING_TEMPLATES, - @JEP(number=445, title="Unnamed Classes and Instance Main Methods") + @JEP(number=445, title="Unnamed Classes and Instance Main Methods", status="Deprecated") UNNAMED_CLASSES, + @JEP(number=463, title="Implicit Classes and Instance Main Methods", status="Preview") + IMPLICIT_CLASSES, @JEP(number=446, title="Scoped Values", status="Preview") SCOPED_VALUES, @JEP(number=453, title="Structured Concurrency", status="Preview") diff --git a/src/java.base/share/classes/jdk/internal/misc/MainMethodFinder.java b/src/java.base/share/classes/jdk/internal/misc/MainMethodFinder.java deleted file mode 100644 index be4ebc38238..00000000000 --- a/src/java.base/share/classes/jdk/internal/misc/MainMethodFinder.java +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright (c) 2023, 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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.internal.misc; - -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.List; - -public class MainMethodFinder { - private static boolean correctArgs(Method method) { - int argc = method.getParameterCount(); - - return argc == 0 || argc == 1 && method.getParameterTypes()[0] == String[].class; - } - - /** - * Gather all the "main" methods in the class hierarchy. - * - * @param refc the main class or super class - * @param mains accumulated main methods - * @param isMainClass the class is the main class and not a super class - */ - private static void gatherMains(Class refc, List mains, boolean isMainClass) { - if (refc != null && refc != Object.class) { - for (Method method : refc.getDeclaredMethods()) { - int mods = method.getModifiers(); - // Must be named "main", public|protected|package-private, not synthetic (bridge) and either - // no arguments or one string array argument. Only statics in the Main class are acceptable. - if ("main".equals(method.getName()) && - !method.isSynthetic() && - !Modifier.isPrivate(mods) && - correctArgs(method) && - (isMainClass || !Modifier.isStatic(mods))) - { - mains.add(method); - } - } - - gatherMains(refc.getSuperclass(), mains, false); - } - } - - /** - * Comparator for two methods. - * Priority order is; - * sub-class < super-class. - * static < non-static, - * string arg < no arg and - * - * @param a first method - * @param b second method - * - * @return -1, 0 or 1 to represent higher priority. equals priority or lesser priority. - */ - private static int compareMethods(Method a, Method b) { - Class aClass = a.getDeclaringClass(); - Class bClass = b.getDeclaringClass(); - - if (aClass != bClass) { - if (bClass.isAssignableFrom(aClass)) { - return -1; - } else { - return 1; - } - } - - int aMods = a.getModifiers(); - int bMods = b.getModifiers(); - boolean aIsStatic = Modifier.isStatic(aMods); - boolean bIsStatic = Modifier.isStatic(bMods); - - if (aIsStatic && !bIsStatic) { - return -1; - } else if (!aIsStatic && bIsStatic) { - return 1; - } - - int aCount = a.getParameterCount(); - int bCount = b.getParameterCount(); - - if (bCount < aCount) { - return -1; - } else if (aCount < bCount) { - return 1; - } - - return 0; - } - - /** - * Return the traditional main method or null if not found. - * - * @param mainClass main class - * - * @return main method or null - */ - private static Method getTraditionalMain(Class mainClass) { - try { - Method traditionalMain = mainClass.getMethod("main", String[].class); - int mods = traditionalMain.getModifiers(); - - if (Modifier.isStatic(mods) && Modifier.isPublic(mods) && traditionalMain.getReturnType() == void.class) { - return traditionalMain; - } - } catch (NoSuchMethodException ex) { - // not found - } - - return null; - } - - /** - * {@return priority main method if none found} - * - * @param mainClass main class - * - * @throws NoSuchMethodException when not preview and no method found - */ - public static Method findMainMethod(Class mainClass) throws NoSuchMethodException { - boolean isTraditionMain = !PreviewFeatures.isEnabled(); - if (isTraditionMain) { - return mainClass.getMethod("main", String[].class); - } - - List mains = new ArrayList<>(); - gatherMains(mainClass, mains, true); - - if (mains.isEmpty()) { - throw new NoSuchMethodException("No main method found"); - } - - if (1 < mains.size()) { - mains.sort(MainMethodFinder::compareMethods); - } - - Method mainMethod = mains.get(0); - Method traditionalMain = getTraditionalMain(mainClass); - - if (traditionalMain != null && !traditionalMain.equals(mainMethod)) { - System.err.println("WARNING: \"" + mains.get(0) + "\" chosen over \"" + traditionalMain + "\""); - } - - return mains.get(0); - } -} diff --git a/src/java.base/share/classes/jdk/internal/misc/MethodFinder.java b/src/java.base/share/classes/jdk/internal/misc/MethodFinder.java new file mode 100644 index 00000000000..970cebe74ed --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/misc/MethodFinder.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2023, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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.internal.misc; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +import jdk.internal.access.JavaLangAccess; +import jdk.internal.access.SharedSecrets; + +/** + * A collection of static methods that return specific method objects of interest. + */ +public class MethodFinder { + + private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); + + private MethodFinder() { + throw new AssertionError("private constructor"); + } + + /** + * Return the first method that meets the requirements of an application main method + * {@jls 12.1.4}. The method must: + *
    + *
  • be declared in this class's hierarchy
  • + *
  • have the name "main"
  • + *
  • have a single argument of type {@code String[]}, {@code String...} or no argument
  • + *
  • have the return type of void
  • + *
  • be public, protected or package private
  • + *
  • not be abstract
  • + *
+ * + * The method returned would be used by a launcher to initiate the execution of an + * application. + * + * Searching continues until a main method is found or the search is exhausted. The + * primary search occurs in two phases, once for a main method with a {@code + * String[]} or {@code String...} argument and failing that, once for a main method + * with a no arguments. The search itself uses recursion to first look at methods + * in this class, then default methods in this class's interface hierarchy and + * then repeating these steps with the class's super class. + * + * @apiNote The method returned may be declared in this class, a super class + * or as a default method of an interface that the class or super class + * implements. + *

It is not possible to declare a static main method and instance main + * method with the same signature in the same class. {@jls 8.4.2} states that + * "It is a compile-time error to declare two methods with override-equivalent + * signatures in a class." + *

{@link SecurityException SecurityExceptions} can halt + * the search. In this case, a null is returned. + * + * @return the main method if a method found or null if no method is found + * + * @jls 8.2 Class Members + * @jls 8.4 Method Declarations + * @jls 8.4.2 Method Signature + * @jls 12.1.4 Invoke a main method + */ + public static Method findMainMethod(Class cls) { + boolean isPreview = PreviewFeatures.isEnabled(); + Method mainMethod = JLA.findMethod(cls, !isPreview, "main", String[].class); + + if (isPreview && mainMethod == null) { + mainMethod = JLA.findMethod(cls, false, "main"); + } + + if (mainMethod == null) { + return null; + } + + int mods = mainMethod.getModifiers(); + + if (Modifier.isAbstract(mods) || + mainMethod.getReturnType() != void.class || + (isPreview && Modifier.isPrivate(mods)) || + (!isPreview && !Modifier.isStatic(mods))) { + return null; + } + + return mainMethod; + } + +} diff --git a/src/java.base/share/classes/sun/launcher/LauncherHelper.java b/src/java.base/share/classes/sun/launcher/LauncherHelper.java index 05f797da70f..5cda08f6009 100644 --- a/src/java.base/share/classes/sun/launcher/LauncherHelper.java +++ b/src/java.base/share/classes/sun/launcher/LauncherHelper.java @@ -71,7 +71,7 @@ import java.util.jar.Manifest; import java.util.stream.Collectors; import java.util.stream.Stream; -import jdk.internal.misc.MainMethodFinder; +import jdk.internal.misc.MethodFinder; import jdk.internal.misc.PreviewFeatures; import jdk.internal.misc.VM; import jdk.internal.module.ModuleBootstrap; @@ -709,7 +709,7 @@ public final class LauncherHelper { /** * This method: * 1. Loads the main class from the module or class path - * 2. Checks the public static void main method. + * 2. Checks for a valid main method. * 3. If the main class extends FX Application then call on FXHelper to * perform the launch. * @@ -753,7 +753,7 @@ public final class LauncherHelper { mainClass = FXHelper.class; } - validateMainClass(mainClass); + validateMainMethod(mainClass); return mainClass; } @@ -859,7 +859,7 @@ public final class LauncherHelper { } } } catch (LinkageError le) { - abort(le, "java.launcher.cls.error6", cn, + abort(le, "java.launcher.cls.error4", cn, le.getClass().getName() + ": " + le.getLocalizedMessage()); } return mainClass; @@ -890,54 +890,28 @@ public final class LauncherHelper { return false; } - /* - * main type flags - */ - private static final int MAIN_WITHOUT_ARGS = 1; - private static final int MAIN_NONSTATIC = 2; - private static int mainType = 0; - - /* - * Return type so that launcher invokes the correct main - */ - public static int getMainType() { - return mainType; - } - - private static void setMainType(Method mainMethod) { - int mods = mainMethod.getModifiers(); - boolean isStatic = Modifier.isStatic(mods); - boolean noArgs = mainMethod.getParameterCount() == 0; - mainType = (isStatic ? 0 : MAIN_NONSTATIC) | (noArgs ? MAIN_WITHOUT_ARGS : 0); - } - - // Check the existence and signature of main and abort if incorrect - static void validateMainClass(Class mainClass) { + // Check the existence and signature of main and abort if incorrect. + private static void validateMainMethod(Class mainClass) { Method mainMethod = null; try { - mainMethod = MainMethodFinder.findMainMethod(mainClass); - } catch (NoSuchMethodException nsme) { - // invalid main or not FX application, abort with an error - abort(null, "java.launcher.cls.error4", mainClass.getName(), - JAVAFX_APPLICATION_CLASS_NAME); + mainMethod = MethodFinder.findMainMethod(mainClass); + + if (mainMethod == null) { + // invalid main or not FX application, abort with an error + abort(null, "java.launcher.cls.error2", mainClass.getName(), + JAVAFX_APPLICATION_CLASS_NAME); + } } catch (Throwable e) { if (mainClass.getModule().isNamed()) { - abort(e, "java.launcher.module.error5", + abort(e, "java.launcher.module.error3", mainClass.getName(), mainClass.getModule().getName(), e.getClass().getName(), e.getLocalizedMessage()); } else { - abort(e, "java.launcher.cls.error7", mainClass.getName(), + abort(e, "java.launcher.cls.error5", mainClass.getName(), e.getClass().getName(), e.getLocalizedMessage()); } } - setMainType(mainMethod); - - /* - * findMainMethod (above) will choose the correct method, based - * on its name and parameter type, however, we still have to - * ensure that the method is static (non-preview) and returns a void. - */ int mods = mainMethod.getModifiers(); boolean isStatic = Modifier.isStatic(mods); boolean isPublic = Modifier.isPublic(mods); @@ -945,32 +919,26 @@ public final class LauncherHelper { if (!PreviewFeatures.isEnabled()) { if (!isStatic || !isPublic || noArgs) { - abort(null, "java.launcher.cls.error2", "static", - mainMethod.getDeclaringClass().getName()); + abort(null, "java.launcher.cls.error2", mainClass.getName(), + JAVAFX_APPLICATION_CLASS_NAME); } + return; } if (!isStatic) { + String className = mainMethod.getDeclaringClass().getName(); if (mainClass.isMemberClass() && !Modifier.isStatic(mainClass.getModifiers())) { - abort(null, "java.launcher.cls.error9", - mainMethod.getDeclaringClass().getName()); + abort(null, "java.launcher.cls.error7", className); } try { Constructor constructor = mainClass.getDeclaredConstructor(); if (Modifier.isPrivate(constructor.getModifiers())) { - abort(null, "java.launcher.cls.error8", - mainMethod.getDeclaringClass().getName()); + abort(null, "java.launcher.cls.error6", className); } } catch (Throwable ex) { - abort(null, "java.launcher.cls.error8", - mainMethod.getDeclaringClass().getName()); + abort(null, "java.launcher.cls.error6", className); } } - - if (mainMethod.getReturnType() != java.lang.Void.TYPE) { - abort(null, "java.launcher.cls.error3", - mainMethod.getDeclaringClass().getName()); - } } private static final String encprop = "sun.jnu.encoding"; @@ -1122,13 +1090,13 @@ public final class LauncherHelper { // find the module with the FX launcher Optional om = ModuleLayer.boot().findModule(JAVAFX_GRAPHICS_MODULE_NAME); if (om.isEmpty()) { - abort(null, "java.launcher.cls.error5"); + abort(null, "java.launcher.cls.error3"); } // load the FX launcher class fxLauncherClass = Class.forName(om.get(), JAVAFX_LAUNCHER_CLASS_NAME); if (fxLauncherClass == null) { - abort(null, "java.launcher.cls.error5"); + abort(null, "java.launcher.cls.error3"); } try { @@ -1149,7 +1117,7 @@ public final class LauncherHelper { abort(null, "java.launcher.javafx.error1"); } } catch (NoSuchMethodException ex) { - abort(ex, "java.launcher.cls.error5", ex); + abort(ex, "java.launcher.cls.error3", ex); } fxLaunchName = what; diff --git a/src/java.base/share/classes/sun/launcher/resources/launcher.properties b/src/java.base/share/classes/sun/launcher/resources/launcher.properties index 219f0d977b5..6bea2e1f51f 100644 --- a/src/java.base/share/classes/sun/launcher/resources/launcher.properties +++ b/src/java.base/share/classes/sun/launcher/resources/launcher.properties @@ -238,29 +238,22 @@ java.launcher.cls.error1=\ Error: Could not find or load main class {0}\n\ Caused by: {1}: {2} java.launcher.cls.error2=\ - Error: Main method is not {0} in class {1}, please define the main method as:\n\ -\ public static void main(String[] args) -java.launcher.cls.error3=\ - Error: Main method must return a value of type void in class {0}, please \n\ - define the main method as:\n\ -\ public static void main(String[] args) -java.launcher.cls.error4=\ Error: Main method not found in class {0}, please define the main method as:\n\ \ public static void main(String[] args)\n\ or a JavaFX application class must extend {1} -java.launcher.cls.error5=\ +java.launcher.cls.error3=\ Error: JavaFX runtime components are missing, and are required to run this application -java.launcher.cls.error6=\ +java.launcher.cls.error4=\ Error: LinkageError occurred while loading main class {0}\n\ \t{1} -java.launcher.cls.error7=\ +java.launcher.cls.error5=\ Error: Unable to initialize main class {0}\n\ Caused by: {1}: {2} -java.launcher.cls.error8=\ +java.launcher.cls.error6=\ Error: no non-private zero argument constructor found in class {0}\n\ remove private from existing constructor or define as:\n\ \ public {0}() -java.launcher.cls.error9=\ +java.launcher.cls.error7=\ Error: non-static inner class {0} constructor can not be invoked \n\ make inner class static or move inner class out to separate source file java.launcher.jar.error1=\ @@ -280,7 +273,7 @@ java.launcher.module.error2=\ Error: Could not find or load main class {0} in module {1} java.launcher.module.error3=\ Error: Unable to load main class {0} in module {1}\n\ - \t{2} + Caused by: {2} java.launcher.module.error4=\ {0} not found java.launcher.module.error5=\ diff --git a/src/java.base/share/native/libjli/java.c b/src/java.base/share/native/libjli/java.c index 2f9d0718e93..fbc9ecb1c4e 100644 --- a/src/java.base/share/native/libjli/java.c +++ b/src/java.base/share/native/libjli/java.c @@ -387,6 +387,84 @@ JLI_Launch(int argc, char ** argv, /* main argc, argv */ } \ } while (JNI_FALSE) +#define CHECK_EXCEPTION_FAIL() \ + do { \ + if ((*env)->ExceptionOccurred(env)) { \ + (*env)->ExceptionClear(env); \ + return 0; \ + } \ + } while (JNI_FALSE) + + +#define CHECK_EXCEPTION_NULL_FAIL(mainObject) \ + do { \ + if ((*env)->ExceptionOccurred(env)) { \ + (*env)->ExceptionClear(env); \ + return 0; \ + } else if (mainObject == NULL) { \ + return 0; \ + } \ + } while (JNI_FALSE) + +/* + * Invoke a static main with arguments. Returns 1 (true) if successful otherwise + * processes the pending exception from GetStaticMethodID and returns 0 (false). + */ +int +invokeStaticMainWithArgs(JNIEnv *env, jclass mainClass, jobjectArray mainArgs) { + jmethodID mainID = (*env)->GetStaticMethodID(env, mainClass, "main", + "([Ljava/lang/String;)V"); + CHECK_EXCEPTION_FAIL(); + (*env)->CallStaticVoidMethod(env, mainClass, mainID, mainArgs); + return 1; +} + +/* + * Invoke an instance main with arguments. Returns 1 (true) if successful otherwise + * processes the pending exception from GetMethodID and returns 0 (false). + */ +int +invokeInstanceMainWithArgs(JNIEnv *env, jclass mainClass, jobjectArray mainArgs) { + jmethodID constructor = (*env)->GetMethodID(env, mainClass, "", "()V"); + CHECK_EXCEPTION_FAIL(); + jobject mainObject = (*env)->NewObject(env, mainClass, constructor); + CHECK_EXCEPTION_NULL_FAIL(mainObject); + jmethodID mainID = (*env)->GetMethodID(env, mainClass, "main", + "([Ljava/lang/String;)V"); + CHECK_EXCEPTION_FAIL(); + (*env)->CallVoidMethod(env, mainObject, mainID, mainArgs); + return 1; + } + +/* + * Invoke a static main without arguments. Returns 1 (true) if successful otherwise + * processes the pending exception from GetStaticMethodID and returns 0 (false). + */ +int +invokeStaticMainWithoutArgs(JNIEnv *env, jclass mainClass) { + jmethodID mainID = (*env)->GetStaticMethodID(env, mainClass, "main", + "()V"); + CHECK_EXCEPTION_FAIL(); + (*env)->CallStaticVoidMethod(env, mainClass, mainID); + return 1; +} + +/* + * Invoke an instance main without arguments. Returns 1 (true) if successful otherwise + * processes the pending exception from GetMethodID and returns 0 (false). + */ +int +invokeInstanceMainWithoutArgs(JNIEnv *env, jclass mainClass) { + jmethodID constructor = (*env)->GetMethodID(env, mainClass, "", "()V"); + CHECK_EXCEPTION_FAIL(); + jobject mainObject = (*env)->NewObject(env, mainClass, constructor); + CHECK_EXCEPTION_NULL_FAIL(mainObject); + jmethodID mainID = (*env)->GetMethodID(env, mainClass, "main", + "()V"); + CHECK_EXCEPTION_FAIL(); + (*env)->CallVoidMethod(env, mainObject, mainID); + return 1; +} int JavaMain(void* _args) @@ -403,9 +481,6 @@ JavaMain(void* _args) jclass mainClass = NULL; jclass appClass = NULL; // actual application class being launched jobjectArray mainArgs; - jmethodID mainID; - jmethodID constructor; - jobject mainObject; int ret = 0; jlong start = 0, end = 0; @@ -491,9 +566,6 @@ JavaMain(void* _args) ret = 1; /* - * 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. * Hence the code here ignores the value previously extracted and @@ -523,7 +595,7 @@ JavaMain(void* _args) * consistent in the UI we need to track and report the application main class. */ appClass = GetApplicationClass(env); - NULL_CHECK_RETURN_VALUE(appClass, -1); + CHECK_EXCEPTION_NULL_LEAVE(appClass); /* Build platform specific argument array */ mainArgs = CreateApplicationArgs(env, argv, argc); @@ -545,59 +617,15 @@ JavaMain(void* _args) 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 - * is not required. The main method is invoked here so that extraneous java - * stacks are not in the application stack trace. + * The main method is invoked here so that extraneous java stacks are not in + * the application stack trace. */ -#define MAIN_WITHOUT_ARGS 1 -#define MAIN_NONSTATIC 2 - - jclass helperClass = GetLauncherHelperClass(env); - jmethodID getMainType = (*env)->GetStaticMethodID(env, helperClass, - "getMainType", - "()I"); - CHECK_EXCEPTION_NULL_LEAVE(getMainType); - int mainType = (*env)->CallStaticIntMethod(env, helperClass, getMainType); - CHECK_EXCEPTION_LEAVE(mainType); - - switch (mainType) { - case 0: { - mainID = (*env)->GetStaticMethodID(env, mainClass, "main", - "([Ljava/lang/String;)V"); - CHECK_EXCEPTION_NULL_LEAVE(mainID); - (*env)->CallStaticVoidMethod(env, mainClass, mainID, mainArgs); - break; - } - case MAIN_WITHOUT_ARGS: { - mainID = (*env)->GetStaticMethodID(env, mainClass, "main", - "()V"); - CHECK_EXCEPTION_NULL_LEAVE(mainID); - (*env)->CallStaticVoidMethod(env, mainClass, mainID); - break; - } - case MAIN_NONSTATIC: { - constructor = (*env)->GetMethodID(env, mainClass, "", "()V"); - CHECK_EXCEPTION_NULL_LEAVE(constructor); - mainObject = (*env)->NewObject(env, mainClass, constructor); - CHECK_EXCEPTION_NULL_LEAVE(mainObject); - mainID = (*env)->GetMethodID(env, mainClass, "main", - "([Ljava/lang/String;)V"); - CHECK_EXCEPTION_NULL_LEAVE(mainID); - (*env)->CallVoidMethod(env, mainObject, mainID, mainArgs); - break; - } - case MAIN_NONSTATIC | MAIN_WITHOUT_ARGS: { - constructor = (*env)->GetMethodID(env, mainClass, "", "()V"); - CHECK_EXCEPTION_NULL_LEAVE(constructor); - mainObject = (*env)->NewObject(env, mainClass, constructor); - CHECK_EXCEPTION_NULL_LEAVE(mainObject); - mainID = (*env)->GetMethodID(env, mainClass, "main", - "()V"); - CHECK_EXCEPTION_NULL_LEAVE(mainID); - (*env)->CallVoidMethod(env, mainObject, mainID); - break; - } + if (!invokeStaticMainWithArgs(env, mainClass, mainArgs) && + !invokeInstanceMainWithArgs(env, mainClass, mainArgs) && + !invokeStaticMainWithoutArgs(env, mainClass) && + !invokeInstanceMainWithoutArgs(env, mainClass)) { + ret = 1; + LEAVE(); } /* @@ -1593,8 +1621,9 @@ NewPlatformStringArray(JNIEnv *env, char **strv, int strc) } /* - * Loads a class and verifies that the main class is present and it is ok to - * call it for more details refer to the java implementation. + * Calls LauncherHelper::checkAndLoadMain to verify that the main class + * is present, it is ok to load the main class and then load the main class. + * For more details refer to the java implementation. */ static jclass LoadMainClass(JNIEnv *env, int mode, char *name) diff --git a/src/java.compiler/share/classes/javax/annotation/processing/Filer.java b/src/java.compiler/share/classes/javax/annotation/processing/Filer.java index 9ebcf2c5908..7487c1fb236 100644 --- a/src/java.compiler/share/classes/javax/annotation/processing/Filer.java +++ b/src/java.compiler/share/classes/javax/annotation/processing/Filer.java @@ -177,12 +177,11 @@ public interface Filer { *

Creating a source file in or for an unnamed package in a named * module is not supported. * - *

If the environment is configured to support {@linkplain - * TypeElement#isUnnamed unnamed classes}, the name argument is - * used to provide the leading component of the name used for the - * output file. For example {@code filer.createSourceFile("Foo")} - * to create an unnamed class hosted in {@code Foo.java}. All - * unnamed classes must be in an unnamed package. + *

If the environment is configured to support implicitly declared + * classes, the name argument is used to provide the leading component of the + * name used for the output file. For example {@code filer.createSourceFile("Foo")} + * to create an implicitly declared class hosted in {@code Foo.java}. All + * implicit classes must be in an unnamed package. * * @apiNote To use a particular {@linkplain * java.nio.charset.Charset charset} to encode the contents of the @@ -263,12 +262,11 @@ public interface Filer { *

Creating a class file in or for an unnamed package in a named * module is not supported. * - *

If the environment is configured to support {@linkplain - * TypeElement#isUnnamed unnamed classes}, the name argument is - * used to provide the leading component of the name used for the - * output file. For example {@code filer.createClassFile("Foo")} to - * create an unnamed class hosted in {@code Foo.class}. All unnamed - * classes must be in an unnamed package. + *

If the environment is configured to support implicitly declared + * classes, the name argument is used to provide the leading component of the + * name used for the output file. For example {@code filer.createSourceFile("Foo")} + * to create an implicitly declared class hosted in {@code Foo.java}. All + * implicit classes must be in an unnamed package. * * @apiNote To avoid subsequent errors, the contents of the class * file should be compatible with the {@linkplain diff --git a/src/java.compiler/share/classes/javax/lang/model/element/TypeElement.java b/src/java.compiler/share/classes/javax/lang/model/element/TypeElement.java index d430e33e591..a8c3b91f8b5 100644 --- a/src/java.compiler/share/classes/javax/lang/model/element/TypeElement.java +++ b/src/java.compiler/share/classes/javax/lang/model/element/TypeElement.java @@ -155,7 +155,7 @@ public interface TypeElement extends Element, Parameterizable, QualifiedNameable /** * Returns the fully qualified name of this class or interface * element. More precisely, it returns the canonical name. - * For local, anonymous, and {@linkplain #isUnnamed() unnamed} classes, which do not have canonical + * For local, and anonymous classes, which do not have canonical * names, an {@linkplain Name##empty_name empty name} is * returned. * @@ -171,7 +171,6 @@ public interface TypeElement extends Element, Parameterizable, QualifiedNameable * * @see Elements#getBinaryName * @jls 6.7 Fully Qualified Names and Canonical Names - * @jls 7.3 Compilation Units */ Name getQualifiedName(); @@ -181,10 +180,6 @@ public interface TypeElement extends Element, Parameterizable, QualifiedNameable * For an anonymous class, an {@linkplain Name##empty_name empty * name} is returned. * - * For an {@linkplain #isUnnamed() unnamed} class, a name matching - * the base name of the hosting file, minus any extension, is - * returned. - * * @return the simple name of this class or interface, * an empty name for an anonymous class * @@ -192,22 +187,6 @@ public interface TypeElement extends Element, Parameterizable, QualifiedNameable @Override Name getSimpleName(); - /** - * {@return {@code true} if this is an unnamed class and {@code - * false} otherwise} - * - * @implSpec - * The default implementation of this method returns {@code false}. - * - * @jls 7.3 Compilation Units - * @since 21 - */ - @PreviewFeature(feature=PreviewFeature.Feature.UNNAMED_CLASSES, - reflective=true) - default boolean isUnnamed() { - return false; - } - /** * Returns the direct superclass of this class or interface element. * If this class or interface element represents an interface or the class diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/ClassFinder.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/ClassFinder.java index 56b692fb7cc..770adbebc2e 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/ClassFinder.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/ClassFinder.java @@ -444,9 +444,6 @@ public class ClassFinder { if (c.members_field == null) { try { c.complete(); - if ((c.flags_field & UNNAMED_CLASS) != 0) { - syms.removeClass(ps.modle, flatname); - } } catch (CompletionFailure ex) { if (absent) { syms.removeClass(ps.modle, flatname); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java index 8856654aed3..c6b8f2cd44e 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java @@ -123,9 +123,9 @@ public class Flags { */ public static final int HASINIT = 1<<18; - /** Class is a unnamed top level class. + /** Class is a implicit top level class. */ - public static final int UNNAMED_CLASS = 1<<19; + public static final int IMPLICIT_CLASS = 1<<19; /** Flag is set for compiler-generated anonymous method symbols * that `own' an initializer block. @@ -499,7 +499,7 @@ public class Flags { ANNOTATION(Flags.ANNOTATION), DEPRECATED(Flags.DEPRECATED), HASINIT(Flags.HASINIT), - UNNAMED_CLASS(Flags.UNNAMED_CLASS), + IMPLICIT_CLASS(Flags.IMPLICIT_CLASS), BLOCK(Flags.BLOCK), FROM_SOURCE(Flags.FROM_SOURCE), ENUM(Flags.ENUM), diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java index ad859bb4507..3c696a3e65d 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java @@ -210,7 +210,7 @@ public class Preview { public boolean isPreview(Feature feature) { return switch (feature) { case STRING_TEMPLATES -> true; - case UNNAMED_CLASSES -> true; + case IMPLICIT_CLASSES -> true; case SUPER_INIT -> true; //Note: this is a backdoor which allows to optionally treat all features as 'preview' (for testing). //When real preview features will be added, this method can be implemented to return 'true' diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Source.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Source.java index 0290f8bc752..ffff623dc4f 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Source.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Source.java @@ -245,7 +245,7 @@ public enum Source { UNCONDITIONAL_PATTERN_IN_INSTANCEOF(JDK21, Fragments.FeatureUnconditionalPatternsInInstanceof, DiagKind.PLURAL), RECORD_PATTERNS(JDK21, Fragments.FeatureDeconstructionPatterns, DiagKind.PLURAL), STRING_TEMPLATES(JDK21, Fragments.FeatureStringTemplates, DiagKind.PLURAL), - UNNAMED_CLASSES(JDK21, Fragments.FeatureUnnamedClasses, DiagKind.PLURAL), + IMPLICIT_CLASSES(JDK21, Fragments.FeatureImplicitClasses, DiagKind.PLURAL), WARN_ON_ILLEGAL_UTF8(MIN, JDK21), UNNAMED_VARIABLES(JDK22, Fragments.FeatureUnnamedVariables, DiagKind.PLURAL), SUPER_INIT(JDK22, Fragments.FeatureSuperInit, DiagKind.NORMAL), diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java index b3dcde6c8cf..d583a86ca3f 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java @@ -435,6 +435,10 @@ public abstract class Symbol extends AnnoConstruct implements PoolConstant, Elem return (flags_field & FINAL) != 0; } + public boolean isImplicit() { + return (flags_field & IMPLICIT_CLASS) != 0; + } + /** Is this symbol declared (directly or indirectly) local * to a method or variable initializer? * Also includes fields of inner classes which are in @@ -1256,7 +1260,6 @@ public abstract class Symbol extends AnnoConstruct implements PoolConstant, Elem /** A class for class symbols */ - @SuppressWarnings("preview") // isUnnamed() public static class ClassSymbol extends TypeSymbol implements TypeElement { /** a scope for all class members; variables, methods and inner classes @@ -1370,7 +1373,7 @@ public abstract class Symbol extends AnnoConstruct implements PoolConstant, Elem @Override @DefinedBy(Api.LANGUAGE_MODEL) public Name getQualifiedName() { - return isUnnamed() ? fullname.subName(0, 0) /* empty name */ : fullname; + return fullname; } @Override @DefinedBy(Api.LANGUAGE_MODEL) @@ -1551,7 +1554,7 @@ public abstract class Symbol extends AnnoConstruct implements PoolConstant, Elem @DefinedBy(Api.LANGUAGE_MODEL) public NestingKind getNestingKind() { apiComplete(); - if (owner.kind == PCK) // Handles unnamed classes as well + if (owner.kind == PCK) // Handles implicit classes as well return NestingKind.TOP_LEVEL; else if (name.isEmpty()) return NestingKind.ANONYMOUS; @@ -1642,11 +1645,6 @@ public abstract class Symbol extends AnnoConstruct implements PoolConstant, Elem public List getPermittedSubclasses() { return permitted.map(s -> s.type); } - - @Override @DefinedBy(Api.LANGUAGE_MODEL) - public boolean isUnnamed() { - return (flags_field & Flags.UNNAMED_CLASS) != 0 ; - } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index b69c2156e1d..567cf0a191f 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -5507,7 +5507,7 @@ public class Attr extends JCTree.Visitor { chk.checkFunctionalInterface((JCClassDecl) env.tree, c); chk.checkLeaksNotAccessible(env, (JCClassDecl) env.tree); - if ((c.flags_field & Flags.UNNAMED_CLASS) != 0) { + if (c.isImplicit()) { chk.checkHasMain(env.tree.pos(), c); } } finally { diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java index ead1ecf3c63..f9b228203d4 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java @@ -452,7 +452,8 @@ public class Check { } } for (Symbol sym = s.owner; sym != null; sym = sym.owner) { - if (sym.kind == TYP && sym.name == name && sym.name != names.error) { + if (sym.kind == TYP && sym.name == name && sym.name != names.error && + !sym.isImplicit()) { duplicateError(pos, sym); return true; } @@ -2276,7 +2277,7 @@ public class Check { } if (!found) { - log.error(pos, Errors.UnnamedClassDoesNotHaveMainMethod); + log.error(pos, Errors.ImplicitClassDoesNotHaveMainMethod); } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Enter.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Enter.java index 90d48243841..7478ef1c907 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Enter.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Enter.java @@ -442,7 +442,7 @@ public class Enter extends JCTree.Visitor { log.error(tree.pos(), Errors.ClassPublicShouldBeInFile(topElement, tree.name)); } - if ((tree.mods.flags & UNNAMED_CLASS) != 0) { + if ((tree.mods.flags & IMPLICIT_CLASS) != 0) { syms.removeClass(env.toplevel.modle, tree.name); } } else { diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java index 51486b997d3..8d12ba5ccaa 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java @@ -3047,14 +3047,6 @@ public class ClassReader { signatureBuffer = new byte[ns]; } readClass(c); - if (previewClassFile) { - if ((c.flags_field & SYNTHETIC) != 0 && - c.owner.kind == PCK && - (c.flags_field & AUXILIARY) == 0 && - (c.flags_field & FINAL) != 0) { - c.flags_field |= UNNAMED_CLASS; - } - } } public void readClassFile(ClassSymbol c) { diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/launcher/Main.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/launcher/Main.java index 729d1cf2b05..cd35ccaa3b7 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/launcher/Main.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/launcher/Main.java @@ -85,7 +85,7 @@ import com.sun.tools.javac.code.Source; import com.sun.tools.javac.resources.LauncherProperties.Errors; import com.sun.tools.javac.util.JCDiagnostic.Error; -import jdk.internal.misc.MainMethodFinder; +import jdk.internal.misc.MethodFinder; import jdk.internal.misc.PreviewFeatures; import jdk.internal.misc.VM; @@ -407,8 +407,8 @@ public class Main { throw new Fault(Errors.NoClass); } TypeElement mainClass = l.mainClass; - String mainClassName = (mainClass.isUnnamed() ? mainClass.getSimpleName() - : mainClass.getQualifiedName()).toString(); + String mainClassName = mainClass.getQualifiedName().toString(); + return mainClassName; } @@ -434,26 +434,13 @@ public class Main { throw new Fault(Errors.CantFindClass(mainClassName)); } - Method mainMethod; - try { - mainMethod = MainMethodFinder.findMainMethod(appClass); - } catch (NoSuchMethodException e) { + Method mainMethod = MethodFinder.findMainMethod(appClass); + + if (mainMethod == null) { throw new Fault(Errors.CantFindMainMethod(mainClassName)); } - int mods = mainMethod.getModifiers(); - boolean isStatic = Modifier.isStatic(mods); - boolean isPublic = Modifier.isPublic(mods); - boolean noArgs = mainMethod.getParameterCount() == 0; - - if (!PreviewFeatures.isEnabled() && (!isStatic || !isPublic)) { - throw new Fault(Errors.MainNotPublicStatic); - } - - if (!mainMethod.getReturnType().equals(void.class)) { - throw new Fault(Errors.MainNotVoid); - } - + boolean isStatic = Modifier.isStatic(mainMethod.getModifiers()); Object instance = null; if (!isStatic) { @@ -478,7 +465,7 @@ public class Main { mainMethod.setAccessible(true); Object receiver = isStatic ? appClass : instance; - if (noArgs) { + if (mainMethod.getParameterCount() == 0) { mainMethod.invoke(receiver); } else { mainMethod.invoke(receiver, (Object)mainArgs); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java index 0713739050a..bf124bc35bc 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java @@ -206,9 +206,9 @@ public class JavacParser implements Parser { this.source = parser.source; this.preview = parser.preview; this.allowStringFolding = parser.allowStringFolding; - this.keepDocComments = false; + this.keepDocComments = parser.keepDocComments; this.parseModuleInfo = false; - this.docComments = null; + this.docComments = parser.docComments; this.errorTree = F.Erroneous(); this.endPosTable = newEndPosTable(false); this.allowYieldStatement = Feature.SWITCH_EXPRESSION.allowedInSource(source); @@ -3916,8 +3916,8 @@ public class JavacParser implements Parser { defs.append(pd); } - boolean firstTypeDecl = true; // have we see a class, enum, or interface declaration yet? - boolean isUnnamedClass = false; + boolean firstTypeDecl = true; // have we seen a class, enum, or interface declaration yet? + boolean isImplicitClass = false; OUTER: while (token.kind != EOF) { if (token.pos <= endPosTable.errorEndPos) { // error recovery @@ -3984,13 +3984,13 @@ public class JavacParser implements Parser { // this code speculatively tests to see if a top level method // or field can parse. If the method or field can parse then // it is parsed. Otherwise, parsing continues as though - // unnamed classes did not exist and error reporting + // implicit classes did not exist and error reporting // is the same as in the past. - if (Feature.UNNAMED_CLASSES.allowedInSource(source) && !isDeclaration()) { + if (Feature.IMPLICIT_CLASSES.allowedInSource(source) && !isDeclaration()) { final JCModifiers finalMods = mods; JavacParser speculative = new VirtualParser(this); List speculativeResult = - speculative.topLevelMethodOrFieldDeclaration(finalMods); + speculative.topLevelMethodOrFieldDeclaration(finalMods, null); if (speculativeResult.head.hasTag(METHODDEF) || speculativeResult.head.hasTag(VARDEF)) { isTopLevelMethodOrField = true; @@ -3998,9 +3998,9 @@ public class JavacParser implements Parser { } if (isTopLevelMethodOrField) { - checkSourceLevel(token.pos, Feature.UNNAMED_CLASSES); - defs.appendList(topLevelMethodOrFieldDeclaration(mods)); - isUnnamedClass = true; + checkSourceLevel(token.pos, Feature.IMPLICIT_CLASSES); + defs.appendList(topLevelMethodOrFieldDeclaration(mods, docComment)); + isImplicitClass = true; } else { JCTree def = typeDeclaration(mods, docComment); if (def instanceof JCExpressionStatement statement) @@ -4012,7 +4012,7 @@ public class JavacParser implements Parser { firstTypeDecl = false; } } - List topLevelDefs = isUnnamedClass ? constructUnnamedClass(defs.toList()) : defs.toList(); + List topLevelDefs = isImplicitClass ? constructImplictClass(defs.toList()) : defs.toList(); JCTree.JCCompilationUnit toplevel = F.at(firstToken.pos).TopLevel(topLevelDefs); if (!consumedToplevelDoc) attach(toplevel, firstToken.docComment()); @@ -4027,14 +4027,14 @@ public class JavacParser implements Parser { return toplevel; } - // Restructure top level to be an unnamed class. - private List constructUnnamedClass(List origDefs) { + // Restructure top level to be an implicit class. + private List constructImplictClass(List origDefs) { ListBuffer topDefs = new ListBuffer<>(); ListBuffer defs = new ListBuffer<>(); for (JCTree def : origDefs) { if (def.hasTag(Tag.PACKAGEDEF)) { - log.error(def.pos(), Errors.UnnamedClassShouldNotHavePackageDeclaration); + log.error(def.pos(), Errors.ImplicitClassShouldNotHavePackageDeclaration); } else if (def.hasTag(Tag.IMPORT)) { topDefs.append(def); } else if (!def.hasTag(Tag.SKIP)) { @@ -4053,12 +4053,12 @@ public class JavacParser implements Parser { } Name name = names.fromString(simplename); - JCModifiers unnamedMods = F.at(Position.NOPOS) - .Modifiers(Flags.FINAL|Flags.SYNTHETIC|Flags.UNNAMED_CLASS, List.nil()); - JCClassDecl unnamed = F.at(primaryPos).ClassDef( - unnamedMods, name, List.nil(), null, List.nil(), List.nil(), + JCModifiers implicitMods = F.at(Position.NOPOS) + .Modifiers(Flags.FINAL|Flags.IMPLICIT_CLASS, List.nil()); + JCClassDecl implicit = F.at(primaryPos).ClassDef( + implicitMods, name, List.nil(), null, List.nil(), List.nil(), defs.toList()); - topDefs.append(unnamed); + topDefs.append(implicit); return topDefs.toList(); } @@ -4735,10 +4735,9 @@ public class JavacParser implements Parser { return List.of(syntaxError(token.pos, err, Errors.Expected(LPAREN))); } - private List topLevelMethodOrFieldDeclaration(JCModifiers mods) throws AssertionError { - int topPos = token.pos; + private List topLevelMethodOrFieldDeclaration(JCModifiers mods, Comment dc) throws AssertionError { int pos = token.pos; - Comment dc = token.docComment(); + dc = dc == null ? token.docComment() : dc; List typarams = typeParametersOpt(); // if there are type parameters but no modifiers, save the start diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/PrintingProcessor.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/PrintingProcessor.java index ee49a5b344d..9cb3b4992f1 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/PrintingProcessor.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/PrintingProcessor.java @@ -126,7 +126,7 @@ public class PrintingProcessor extends AbstractProcessor { kind != INSTANCE_INIT) { Element enclosing = e.getEnclosingElement(); - // Don't print out the constructor of an anonymous or unnamed class + // Don't print out the constructor of an anonymous class if (kind == CONSTRUCTOR && enclosing != null && (NestingKind.ANONYMOUS == @@ -136,13 +136,6 @@ public class PrintingProcessor extends AbstractProcessor { public NestingKind visitType(TypeElement e, Void p) { return e.getNestingKind(); } - }).visit(enclosing) - || // Don't print the constructor of an unnamed class - (new SimpleElementVisitor14(false) { - @Override @DefinedBy(Api.LANGUAGE_MODEL) - public Boolean visitType(TypeElement e, Void p) { - return e.isUnnamed(); - } }).visit(enclosing)) ) { return this; } @@ -222,14 +215,6 @@ public class PrintingProcessor extends AbstractProcessor { printParameters(constructors.get(0)); } writer.print(")"); - } else if (e.isUnnamed()) { - writer.println("// Unnamed class in file whose name starts with " + e.getSimpleName()); - - for(Element element : e.getEnclosedElements()) { - this.visit(element); - } - - return this; } else { if (nestingKind == TOP_LEVEL) { PackageElement pkg = elementUtils.getPackageOf(e); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties index 30756ef6fa6..975c3205a36 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties @@ -524,11 +524,11 @@ compiler.err.duplicate.class=\ compiler.err.bad.file.name=\ bad file name: {0} -compiler.err.unnamed.class.should.not.have.package.declaration=\ - unnamed class should not have package declaration +compiler.err.implicit.class.should.not.have.package.declaration=\ + implicit class should not have package declaration -compiler.err.unnamed.class.does.not.have.main.method=\ - unnamed class does not have main method in the form of void main() or void main(String[] args) +compiler.err.implicit.class.does.not.have.main.method=\ + implicit class does not have main method in the form of void main() or void main(String[] args) # 0: name, 1: name compiler.err.same.binary.name=\ @@ -3218,8 +3218,8 @@ compiler.misc.feature.string.templates=\ compiler.misc.feature.unconditional.patterns.in.instanceof=\ unconditional patterns in instanceof -compiler.misc.feature.unnamed.classes=\ - unnamed classes +compiler.misc.feature.implicit.classes=\ + implicit classes compiler.misc.feature.super.init=\ statements before super() diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeMaker.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeMaker.java index 7cd0df9295b..21ced44c80e 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeMaker.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeMaker.java @@ -1161,7 +1161,7 @@ public class TreeMaker implements JCTree.Factory { !it.hasNext(); } } - return sym.kind == TYP && (sym.flags_field & Flags.UNNAMED_CLASS) != 0; + return sym.kind == TYP && sym.isImplicit(); } /** The name of synthetic parameter number `i'. diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/ElementsTable.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/ElementsTable.java index 7f4f31844f5..b0a6b4dfdf4 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/ElementsTable.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/ElementsTable.java @@ -986,7 +986,7 @@ public class ElementsTable { * @return true if the element is visible */ public boolean isSelected(Element e) { - if (toolEnv.isSynthetic((Symbol) e) && !toolEnv.isUnnamed((Symbol) e)) { + if (toolEnv.isSynthetic((Symbol) e)) { return false; } if (visibleElementVisitor == null) { diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/ToolEnvironment.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/ToolEnvironment.java index a8cb10651dd..4b1e1c66258 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/ToolEnvironment.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/ToolEnvironment.java @@ -174,10 +174,6 @@ public class ToolEnvironment { return (sym.flags() & Flags.SYNTHETIC) != 0; } - boolean isUnnamed(Symbol sym) { - return (sym.flags() & Flags.UNNAMED_CLASS) != 0; - } - void setElementToTreePath(Element e, TreePath tree) { if (e == null || tree == null) return; diff --git a/test/jdk/java/lang/Class/UnnamedClass/TestUnnamedClass.java b/test/jdk/java/lang/Class/UnnamedClass/TestUnnamedClass.java deleted file mode 100644 index 9e4635ab932..00000000000 --- a/test/jdk/java/lang/Class/UnnamedClass/TestUnnamedClass.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (c) 2023, 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. - */ - -/* - * @test - * @bug 8306112 - * @enablePreview - * @build Unnamed TestUnnamedClass - * @run main TestUnnamedClass - * @summary Basic checks of java.lang.Class support for unnamed classes - */ - -import java.lang.reflect.Modifier; - -public class TestUnnamedClass { - public static void main(String... args) throws Exception { - Class unnamedClass = Class.forName("Unnamed"); - - /* - * From JEP 445 JLS changes: - * - * "An unnamed class compilation unit implicitly declares a class that satisfies the following - * properties: - * It is always a top level class. - * It is always an unnamed class (it has no canonical or fully qualified name (6.7)). - * It is never abstract (8.1.1.1). - * It is always final (8.1.1.2). - * It is always a member of an unnamed package (7.4.2) and has package access. - * Its direct superclass type is always Object (8.1.4). - * It never has any direct superinterface types (8.1.5). - */ - - check(unnamedClass.isUnnamedClass(), "Unnamed class was isUnnamedClass() false"); - - check(!(unnamedClass.isInterface() || - unnamedClass.isEnum() || - unnamedClass.isRecord() || - unnamedClass.isHidden()), - "Unnamed class was not a normal class"); - - check(!(unnamedClass.isLocalClass() || - unnamedClass.isMemberClass() || - unnamedClass.isPrimitive() || - unnamedClass.isArray()), - "Unnamed class was not top-level"); - - check("Unnamed".equals(unnamedClass.getName()), "Unexpected Class.getName result"); - check("Unnamed".equals(unnamedClass.getTypeName()), "Unexpected Class.getTypeName result"); - check("".equals(unnamedClass.getSimpleName()), "Unexpected Class.getSimpleName result"); - check(unnamedClass.getCanonicalName() == null, "Unexpected non-null Class.getCanonicalName"); - - int modifiers = unnamedClass.getModifiers(); - check((modifiers & Modifier.ABSTRACT) == 0, "Unnamed class was abstract"); - check((modifiers & Modifier.FINAL) != 0, "Unnamed class was not final"); - check((modifiers & (Modifier.PUBLIC | - Modifier.PRIVATE | - Modifier.PROTECTED)) == 0, "Unnamed class was not package access"); - - check(unnamedClass.isSynthetic(), "Unnamed class was not synthetic"); - - check("".equals(unnamedClass.getPackage().getName()), "Unnamed class not in an unnamed package"); - - check(unnamedClass.getSuperclass() == Object.class, "Superclass was not java.lang.Object"); - - check(unnamedClass.getInterfaces().length == 0, "Unnamed class had super interfaces"); - } - - private static void check(boolean predicate, String message) { - if (!predicate) { - throw new RuntimeException(message); - } - } -} diff --git a/test/jdk/tools/launcher/Arrrghs.java b/test/jdk/tools/launcher/Arrrghs.java index 11a6ab0a520..efc95052842 100644 --- a/test/jdk/tools/launcher/Arrrghs.java +++ b/test/jdk/tools/launcher/Arrrghs.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2023, 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 @@ -541,12 +541,12 @@ public class Arrrghs extends TestHelper { createJar(new File("some.jar"), new File("Foo"), "public static int main(String[] args){return 1;}"); tr = doExec(javaCmd, "-jar", "some.jar"); - tr.contains("Error: Main method must return a value of type void in class Foo"); + tr.contains("Error: Main method not found in class Foo"); if (!tr.testStatus) System.out.println(tr); // use classpath to check tr = doExec(javaCmd, "-cp", "some.jar", "Foo"); - tr.contains("Error: Main method must return a value of type void in class Foo"); + tr.contains("Error: Main method not found in class Foo"); if (!tr.testStatus) System.out.println(tr); @@ -567,12 +567,12 @@ public class Arrrghs extends TestHelper { createJar(new File("some.jar"), new File("Foo"), "public void main(String[] args){}"); tr = doExec(javaCmd, "-jar", "some.jar"); - tr.contains("Error: Main method is not static in class Foo"); + tr.contains("Error: Main method not found in class Foo"); if (!tr.testStatus) System.out.println(tr); // use classpath to check tr = doExec(javaCmd, "-cp", "some.jar", "Foo"); - tr.contains("Error: Main method is not static in class Foo"); + tr.contains("Error: Main method not found in class Foo"); if (!tr.testStatus) System.out.println(tr); diff --git a/test/jdk/tools/launcher/InstanceMainTest.java b/test/jdk/tools/launcher/InstanceMainTest.java index 05973557fc3..08b5cb48dbb 100644 --- a/test/jdk/tools/launcher/InstanceMainTest.java +++ b/test/jdk/tools/launcher/InstanceMainTest.java @@ -42,13 +42,13 @@ public class InstanceMainTest extends TestHelper { } """, - // static dominating instance + // instance dominating static """ class MainClass { void main(String[] args) { - throw new AssertionError(); } static void main() { + throw new AssertionError(); } } """, @@ -81,12 +81,12 @@ public class InstanceMainTest extends TestHelper { } """, - // unnamed class static dominating instance + // main with args dominating main without args """ void main(String[] args) { - throw new AssertionError(); } static void main() { + throw new AssertionError(); } """, @@ -105,15 +105,15 @@ public class InstanceMainTest extends TestHelper { } """, - // instance main dominating super static + // instance main with args dominating super """ class MainClass extends SuperClass { void main() { + throw new AssertionError(); } } class SuperClass { void main(String[] args) { - throw new AssertionError(); } } """, diff --git a/test/jdk/tools/launcher/modules/basic/LauncherErrors.java b/test/jdk/tools/launcher/modules/basic/LauncherErrors.java index b79238218b3..dd5157fa612 100644 --- a/test/jdk/tools/launcher/modules/basic/LauncherErrors.java +++ b/test/jdk/tools/launcher/modules/basic/LauncherErrors.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2023, 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 @@ -92,9 +92,10 @@ public class LauncherErrors { ProcessTools.executeTestJava("-Djava.security.manager", "--module-path", dir, "--module", mid) .outputTo(System.out) .errorTo(System.out) - .shouldContain("Error: Unable to initialize main class " + MAIN_CLASS + " in module " + TEST_MODULE) - .shouldContain("Caused by: java.security.AccessControlException: access denied") + .shouldContain("Error: Unable to load main class " + MAIN_CLASS + " in module " + TEST_MODULE) + .shouldContain("Caused by: java.security.AccessControlException") .shouldNotHaveExitValue(0); + } } diff --git a/test/langtools/jdk/javadoc/doclet/testUnnamed/TestUnnamed.java b/test/langtools/jdk/javadoc/doclet/testUnnamed/TestImplicit.java similarity index 91% rename from test/langtools/jdk/javadoc/doclet/testUnnamed/TestUnnamed.java rename to test/langtools/jdk/javadoc/doclet/testUnnamed/TestImplicit.java index e9658cd4071..fa9a535902f 100644 --- a/test/langtools/jdk/javadoc/doclet/testUnnamed/TestUnnamed.java +++ b/test/langtools/jdk/javadoc/doclet/testUnnamed/TestImplicit.java @@ -24,11 +24,11 @@ /* * @test * @bug 8309595 - * @summary Allow javadoc to process unnamed classes + * @summary Allow javadoc to process implicit classes * @library /tools/lib ../../lib * @modules jdk.javadoc/jdk.javadoc.internal.tool * @build toolbox.ToolBox javadoc.tester.* - * @run main TestUnnamed + * @run main TestImplicit */ import java.io.File; @@ -39,18 +39,18 @@ import java.nio.file.Path; import javadoc.tester.JavadocTester; import toolbox.ToolBox; -public class TestUnnamed extends JavadocTester { +public class TestImplicit extends JavadocTester { private static final String thisVersion = System.getProperty("java.specification.version"); private static final ToolBox tb = new ToolBox(); public static void main(String... args) throws Exception { - new TestUnnamed().runTests(); + new TestImplicit().runTests(); } @Test - public void testUnnamed(Path base) throws IOException { + public void testImplicit(Path base) throws IOException { String className = "Sample"; Files.createDirectories(base); Path out = base.resolve("out"); diff --git a/test/jdk/java/lang/Class/UnnamedClass/Unnamed.java b/test/langtools/tools/javac/ImplicitClass/Implicit.java similarity index 95% rename from test/jdk/java/lang/Class/UnnamedClass/Unnamed.java rename to test/langtools/tools/javac/ImplicitClass/Implicit.java index 9545a90f0de..10e1fad007b 100644 --- a/test/jdk/java/lang/Class/UnnamedClass/Unnamed.java +++ b/test/langtools/tools/javac/ImplicitClass/Implicit.java @@ -23,8 +23,8 @@ /** - * Exemplar unnamed class. + * Exemplar implicit class. */ -public void main() { +void main() { System.out.println("Hello, world."); } diff --git a/test/langtools/tools/javac/unnamed/UnnamedClassRecovery.java b/test/langtools/tools/javac/ImplicitClass/ImplicitClassRecovery.java similarity index 53% rename from test/langtools/tools/javac/unnamed/UnnamedClassRecovery.java rename to test/langtools/tools/javac/ImplicitClass/ImplicitClassRecovery.java index 2c1d533ab6c..0057d779f8c 100644 --- a/test/langtools/tools/javac/unnamed/UnnamedClassRecovery.java +++ b/test/langtools/tools/javac/ImplicitClass/ImplicitClassRecovery.java @@ -1,6 +1,6 @@ /** * @test /nodynamiccopyright/ - * @compile/fail/ref=UnnamedClassRecovery.out -XDrawDiagnostics --enable-preview --source ${jdk.version} UnnamedClassRecovery.java + * @compile/fail/ref=ImplicitClassRecovery.out -XDrawDiagnostics --enable-preview --source ${jdk.version} ImplicitClassRecovery.java */ public void main() { //the following is intentionally missing a semicolon: diff --git a/test/langtools/tools/javac/ImplicitClass/ImplicitClassRecovery.out b/test/langtools/tools/javac/ImplicitClass/ImplicitClassRecovery.out new file mode 100644 index 00000000000..7de6ac6b3ee --- /dev/null +++ b/test/langtools/tools/javac/ImplicitClass/ImplicitClassRecovery.out @@ -0,0 +1,4 @@ +ImplicitClassRecovery.java:7:33: compiler.err.expected: ';' +- compiler.note.preview.filename: ImplicitClassRecovery.java, DEFAULT +- compiler.note.preview.recompile +1 error diff --git a/test/langtools/tools/javac/ImplicitClass/NestedClasses.java b/test/langtools/tools/javac/ImplicitClass/NestedClasses.java new file mode 100644 index 00000000000..19f070c4d48 --- /dev/null +++ b/test/langtools/tools/javac/ImplicitClass/NestedClasses.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2023, 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. + */ + +/* + * @test + * @bug 8315458 + * @enablePreview + * @summary Make sure nesting classes don't create symbol conflicts with implicit name. + * @library /tools/lib + * @modules jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.main + * @build toolbox.ToolBox toolbox.JavacTask + * @run main NestedClasses + */ + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; + +import toolbox.ToolBox; +import toolbox.JavaTask; +import toolbox.JavacTask; +import toolbox.Task; + +public class NestedClasses { + private static ToolBox TOOLBOX = new ToolBox(); + private static final String JAVA_VERSION = System.getProperty("java.specification.version"); + + public static void main(String... arg) throws IOException { + compPass("A.java", """ + void main() {} + class A {} // okay + """); + + compPass("A.java", """ + void main() {} + class B { + class A { } // okay + } + """); + + compFail("A.java", """ + void main() {} + class B { + class B { } //error + } + """); + } + + /* + * Test source for successful compile. + */ + static void compPass(String fileName, String code) throws IOException { + Path path = Path.of(fileName); + Files.writeString(path, code); + String output = new JavacTask(TOOLBOX) + .files(List.of(path)) + .classpath(".") + .options("-encoding", "utf8", "--enable-preview", "-source", JAVA_VERSION) + .run() + .writeAll() + .getOutput(Task.OutputKind.DIRECT); + + if (output.contains("compiler.err")) { + throw new RuntimeException("Error detected"); + } + } + + /* + * Test source for unsuccessful compile and specific error. + */ + static void compFail(String fileName, String code) throws IOException { + Path path = Path.of(fileName); + Files.writeString(path, code); + String output = new JavacTask(TOOLBOX) + .files(List.of(path)) + .classpath(".") + .options("-XDrawDiagnostics", "-encoding", "utf8", "--enable-preview", "-source", JAVA_VERSION) + .run(Task.Expect.FAIL) + .writeAll() + .getOutput(Task.OutputKind.DIRECT); + + if (!output.contains("compiler.err")) { + throw new RuntimeException("No error detected"); + } + } + + } diff --git a/test/langtools/tools/javac/unnamedclass/NestedEnum.java b/test/langtools/tools/javac/ImplicitClass/NestedEnum.java similarity index 100% rename from test/langtools/tools/javac/unnamedclass/NestedEnum.java rename to test/langtools/tools/javac/ImplicitClass/NestedEnum.java diff --git a/test/langtools/tools/javac/unnamedclass/SourceLevelErrorPosition.java b/test/langtools/tools/javac/ImplicitClass/SourceLevelErrorPosition.java similarity index 74% rename from test/langtools/tools/javac/unnamedclass/SourceLevelErrorPosition.java rename to test/langtools/tools/javac/ImplicitClass/SourceLevelErrorPosition.java index 54beb45d90b..786c228f11e 100644 --- a/test/langtools/tools/javac/unnamedclass/SourceLevelErrorPosition.java +++ b/test/langtools/tools/javac/ImplicitClass/SourceLevelErrorPosition.java @@ -1,7 +1,7 @@ /** * @test /nodynamiccopyright/ * @bug 8310314 - * @summary Ensure proper error position for the "unnamed classes not supported" error + * @summary Ensure proper error position for the "implicit classes not supported" error * @compile/fail/ref=SourceLevelErrorPosition.out -XDrawDiagnostics SourceLevelErrorPosition.java */ class Nested {} diff --git a/test/langtools/tools/javac/unnamedclass/SourceLevelErrorPosition.out b/test/langtools/tools/javac/ImplicitClass/SourceLevelErrorPosition.out similarity index 55% rename from test/langtools/tools/javac/unnamedclass/SourceLevelErrorPosition.out rename to test/langtools/tools/javac/ImplicitClass/SourceLevelErrorPosition.out index 518350810a0..1376183d9c6 100644 --- a/test/langtools/tools/javac/unnamedclass/SourceLevelErrorPosition.out +++ b/test/langtools/tools/javac/ImplicitClass/SourceLevelErrorPosition.out @@ -1,2 +1,2 @@ -SourceLevelErrorPosition.java:8:1: compiler.err.preview.feature.disabled.plural: (compiler.misc.feature.unnamed.classes) +SourceLevelErrorPosition.java:8:1: compiler.err.preview.feature.disabled.plural: (compiler.misc.feature.implicit.classes) 1 error diff --git a/test/langtools/tools/javac/ImplicitClass/TestImplicitClass.java b/test/langtools/tools/javac/ImplicitClass/TestImplicitClass.java new file mode 100644 index 00000000000..673c31b6215 --- /dev/null +++ b/test/langtools/tools/javac/ImplicitClass/TestImplicitClass.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2023, 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. + */ + +/* + * @test + * @bug 8315458 + * @enablePreview + * @build Implicit TestImplicitClass + * @run main TestImplicitClass + * @summary Basic checks of java.lang.Class support for implicit classes + */ + +import java.lang.reflect.Modifier; + +public class TestImplicitClass { + public static void main(String... args) throws Exception { + Class implicitClass = Class.forName("Implicit"); + + /* + * From JEP 463 JLS changes: + * + * "An implicit class compilation unit implicitly declares a class that satisfies the following + * properties: + * It is always a top level class. + * It is never abstract (8.1.1.1). + * It is never synthetic (13.1.11) + * It is always final (8.1.1.2). + * It is always a member of unnamed package (7.4.2) and has package access. + * Its direct superclass type is always Object (8.1.4). + * It never has any direct superinterface types (8.1.5). + */ + + check(!(implicitClass.isInterface() || + implicitClass.isEnum() || + implicitClass.isRecord() || + implicitClass.isHidden()), + "Implicit class was not a normal class"); + + check(!(implicitClass.isLocalClass() || + implicitClass.isMemberClass() || + implicitClass.isPrimitive() || + implicitClass.isArray()), + "Implicit class was not top-level"); + + check("Implicit".equals(implicitClass.getName()), "Unexpected Class.getName result"); + check("Implicit".equals(implicitClass.getTypeName()), "Unexpected Class.getTypeName result"); + check("Implicit".equals(implicitClass.getSimpleName()), "Unexpected Class.getSimpleName result"); + check("Implicit".equals(implicitClass.getCanonicalName()), "Unexpected Class.getCanonicalName result"); + + int modifiers = implicitClass.getModifiers(); + check((modifiers & Modifier.ABSTRACT) == 0, "Implicit class was abstract"); + check((modifiers & Modifier.FINAL) != 0, "Implicit class was not final"); + check((modifiers & (Modifier.PUBLIC | + Modifier.PRIVATE | + Modifier.PROTECTED)) == 0, "Implicit class was not package access"); + + check(!implicitClass.isSynthetic(), "Implicit class was synthetic"); + + check("".equals(implicitClass.getPackage().getName()), "Implicit class not in an implicit package"); + + check(implicitClass.getSuperclass() == Object.class, "Superclass was not java.lang.Object"); + + check(implicitClass.getInterfaces().length == 0, "Implicit class had super interfaces"); + } + + private static void check(boolean predicate, String message) { + if (!predicate) { + throw new RuntimeException(message); + } + } +} diff --git a/test/langtools/tools/javac/diags/examples/UnnamedClass.java b/test/langtools/tools/javac/diags/examples/ImplicitClass.java similarity index 96% rename from test/langtools/tools/javac/diags/examples/UnnamedClass.java rename to test/langtools/tools/javac/diags/examples/ImplicitClass.java index c78dcf14e6c..c032bdefeff 100644 --- a/test/langtools/tools/javac/diags/examples/UnnamedClass.java +++ b/test/langtools/tools/javac/diags/examples/ImplicitClass.java @@ -21,7 +21,7 @@ * questions. */ - // key: compiler.misc.feature.unnamed.classes + // key: compiler.misc.feature.implicit.classes // key: compiler.warn.preview.feature.use.plural // options: -source ${jdk.version} --enable-preview -Xlint:preview diff --git a/test/langtools/tools/javac/diags/examples/UnnamedClassBad-Filename.java b/test/langtools/tools/javac/diags/examples/ImplicitClassBad-Filename.java similarity index 100% rename from test/langtools/tools/javac/diags/examples/UnnamedClassBad-Filename.java rename to test/langtools/tools/javac/diags/examples/ImplicitClassBad-Filename.java diff --git a/test/langtools/tools/javac/diags/examples/UnnamedClassHasPackage.java b/test/langtools/tools/javac/diags/examples/ImplicitClassHasPackage.java similarity index 92% rename from test/langtools/tools/javac/diags/examples/UnnamedClassHasPackage.java rename to test/langtools/tools/javac/diags/examples/ImplicitClassHasPackage.java index 96601f9a945..c90e0f3dfbc 100644 --- a/test/langtools/tools/javac/diags/examples/UnnamedClassHasPackage.java +++ b/test/langtools/tools/javac/diags/examples/ImplicitClassHasPackage.java @@ -21,12 +21,12 @@ * questions. */ - // key: compiler.err.unnamed.class.should.not.have.package.declaration + // key: compiler.err.implicit.class.should.not.have.package.declaration // key: compiler.note.preview.filename // key: compiler.note.preview.recompile // options: -source ${jdk.version} --enable-preview -package unnamed.classes; +package implicit.classes; public static void main(String... args) { } diff --git a/test/langtools/tools/javac/diags/examples/UnnamedClassNoMain.java b/test/langtools/tools/javac/diags/examples/ImplicitClassNoMain.java similarity index 95% rename from test/langtools/tools/javac/diags/examples/UnnamedClassNoMain.java rename to test/langtools/tools/javac/diags/examples/ImplicitClassNoMain.java index 0caacb60e66..4c82341f5ac 100644 --- a/test/langtools/tools/javac/diags/examples/UnnamedClassNoMain.java +++ b/test/langtools/tools/javac/diags/examples/ImplicitClassNoMain.java @@ -21,7 +21,7 @@ * questions. */ - // key: compiler.err.unnamed.class.does.not.have.main.method + // key: compiler.err.implicit.class.does.not.have.main.method // key: compiler.note.preview.filename // key: compiler.note.preview.recompile // options: -source ${jdk.version} --enable-preview diff --git a/test/langtools/tools/javac/launcher/SourceLauncherTest.java b/test/langtools/tools/javac/launcher/SourceLauncherTest.java index 06190546d49..9e2ef069369 100644 --- a/test/langtools/tools/javac/launcher/SourceLauncherTest.java +++ b/test/langtools/tools/javac/launcher/SourceLauncherTest.java @@ -564,7 +564,7 @@ public class SourceLauncherTest extends TestRunner { tb.writeJavaFiles(base, "class NotStatic { public void main(String... args) { } }"); testError(base.resolve("NotStatic.java"), "", - "error: 'main' method is not declared 'public static'"); + "error: can't find main(String[]) method in class: NotStatic"); } @Test @@ -572,7 +572,7 @@ public class SourceLauncherTest extends TestRunner { tb.writeJavaFiles(base, "class NotVoid { public static int main(String... args) { return 0; } }"); testError(base.resolve("NotVoid.java"), "", - "error: 'main' method is not declared with a return type of 'void'"); + "error: can't find main(String[]) method in class: NotVoid"); } @Test diff --git a/test/langtools/tools/javac/parser/JavacParserTest.java b/test/langtools/tools/javac/parser/JavacParserTest.java index 14f0c506cd1..f9ccb39c05e 100644 --- a/test/langtools/tools/javac/parser/JavacParserTest.java +++ b/test/langtools/tools/javac/parser/JavacParserTest.java @@ -2472,7 +2472,7 @@ public class JavacParserTest extends TestCase { result, """ \n\ - /*synthetic*/ final class Test { + final class Test { \n\ void main() { (ERROR); diff --git a/test/langtools/tools/javac/processing/model/element/TestUnnamedClass.java b/test/langtools/tools/javac/processing/model/element/TestImplicitClass.java similarity index 61% rename from test/langtools/tools/javac/processing/model/element/TestUnnamedClass.java rename to test/langtools/tools/javac/processing/model/element/TestImplicitClass.java index 13ea7e0fa6d..bad9fa15f8b 100644 --- a/test/langtools/tools/javac/processing/model/element/TestUnnamedClass.java +++ b/test/langtools/tools/javac/processing/model/element/TestImplicitClass.java @@ -24,19 +24,19 @@ /* * @test * @bug 8306112 8309568 - * @summary Test basic processing of unnamed classes. + * @summary Test basic processing of implicitly declared classes. * @library /tools/javac/lib * @modules java.compiler * jdk.compiler - * @build JavacTestingAbstractProcessor TestUnnamedClass - * @compile -processor TestUnnamedClass --enable-preview --release ${jdk.version} Anonymous.java + * @build JavacTestingAbstractProcessor TestImplicitClass + * @compile -processor TestImplicitClass --enable-preview --release ${jdk.version} Anonymous.java * @clean Nameless.java - * @compile/process -processor TestUnnamedClass -proc:only --enable-preview --release ${jdk.version} -Xprefer:newer -AclassOnly Anonymous Nameless + * @compile/process -processor TestImplicitClass -proc:only --enable-preview --release ${jdk.version} -Xprefer:newer -AclassOnly Anonymous Nameless */ // The first @compile line processes Anonymous.java and a -// Nameless.java class generated using the Filer. Both of those -// unnamed classes are then compiled down to class files. The second +// Nameless.java class generated using the Filer. Both of those implicitly +// declared classes are then compiled down to class files. The second // @compile line, as directed by -Xprefer:newer, builds and checks the // language model objects constructed from those class files, ignoring // any source files for those types. @@ -52,12 +52,12 @@ import static javax.lang.model.util.ElementFilter.*; import javax.tools.JavaFileObject; /** - * Test annotation processing representation of unnamed classes + * Test annotation processing representation of implicitly classes * constructed from either a source file or a class file. */ @SuppressWarnings("preview") @SupportedOptions("classOnly") -public class TestUnnamedClass extends JavacTestingAbstractProcessor { +public class TestImplicitClass extends JavacTestingAbstractProcessor { private static int round = 0; private static int checkedClassesCount = 0; @@ -72,14 +72,14 @@ public class TestUnnamedClass extends JavacTestingAbstractProcessor { // Don't generate any files if testing pre-existing class files if (!classOnly) { - generateUnnamed(); + generateImplicitClass(); } } else { if (!roundEnv.processingOver()) { // Test generated file(s) checkRoots(roundEnv); } else { // Should have checked at least one class before processing is over if (checkedClassesCount == 0) { - messager.printError("No unnamed classes checked."); + messager.printError("No implicitly declared classes checked."); } } } @@ -95,11 +95,11 @@ public class TestUnnamedClass extends JavacTestingAbstractProcessor { checkUnnamedClassProperties(type); } if (checks == 0) { - messager.printError("No checking done of any candidate unnamed classes."); + messager.printError("No checking done of any candidate implicitly declared classes."); } } - private void generateUnnamed() { + private void generateImplicitClass() { try { String unnamedSource = """ void main() { @@ -117,12 +117,11 @@ public class TestUnnamedClass extends JavacTestingAbstractProcessor { } /* - * From JEP 445 JLS changes: + * From JEP 463 JLS changes: * - * "An unnamed class compilation unit implicitly declares a class that satisfies the following - * properties: + * "An implicitly declared class compilation unit implicitly declares a class that + * satisfies the following properties: * It is always a top level class. - * It is always an unnamed class (it has no canonical or fully qualified name (6.7)). * It is never abstract (8.1.1.1). * It is always final (8.1.1.2). * It is always a member of an unnamed package (7.4.2) and has package access. @@ -130,8 +129,8 @@ public class TestUnnamedClass extends JavacTestingAbstractProcessor { * It never has any direct superinterface types (8.1.5). * * The body of the class contains every ClassMemberDeclaration - * from the unnamed class compilation unit. It is not possible for - * an unnamed class compilation unit to declare an instance + * from the implicitly declared class compilation unit. It is not possible for + * an implicitly declared class compilation unit to declare an instance * initializer, static initializer, or constructor. * * It has an implicitly declared default constructor (8.8.9). @@ -141,63 +140,55 @@ public class TestUnnamedClass extends JavacTestingAbstractProcessor { * * It is a compile-time error if this class does not declare a candidate main method (12.1.4). */ - void checkUnnamedClassProperties(TypeElement unnamedClass) { + void checkUnnamedClassProperties(TypeElement implicitClass) { checkedClassesCount++; - Name expectedName = unnamedClass.getSimpleName(); + Name expectedName = implicitClass.getSimpleName(); System.out.println("Checking " + expectedName); - if (unnamedClass.getNestingKind() != NestingKind.TOP_LEVEL) { - messager.printError("Unnamed class is not top-level.", unnamedClass); + if (implicitClass.getNestingKind() != NestingKind.TOP_LEVEL) { + messager.printError("Implicitly declared class is not top-level.", implicitClass); } - if (!unnamedClass.isUnnamed()) { - messager.printError("Unnamed class is _not_ indicated as such.", unnamedClass); + if (!implicitClass.getQualifiedName().equals(expectedName)) { + messager.printError("Implicitly declared class qualified name does not match simple name.", implicitClass); } - if (unnamedClass.getSimpleName().isEmpty()) { - messager.printError("Unnamed class has an empty simple name.", unnamedClass); - } - - if (!unnamedClass.getQualifiedName().isEmpty()) { - messager.printError("Unnamed class does _not_ have an empty qualified name.", unnamedClass); - } - - Name binaryName = elements.getBinaryName(unnamedClass); + Name binaryName = elements.getBinaryName(implicitClass); if (!expectedName.equals(binaryName)) { - messager.printError("Unnamed has unexpected binary name" + binaryName + ".", unnamedClass); + messager.printError("Implicitly declared class has unexpected binary name" + binaryName + ".", implicitClass); } - if (unnamedClass.getModifiers().contains(Modifier.ABSTRACT)) { - messager.printError("Unnamed class is abstract.", unnamedClass); + if (implicitClass.getModifiers().contains(Modifier.ABSTRACT)) { + messager.printError("Implicitly declared class is abstract.", implicitClass); } - if (!unnamedClass.getModifiers().contains(Modifier.FINAL)) { - messager.printError("Unnamed class is _not_ final.", unnamedClass); + if (!implicitClass.getModifiers().contains(Modifier.FINAL)) { + messager.printError("Implicitly declared class is _not_ final.", implicitClass); } - if (!elements.getPackageOf(unnamedClass).isUnnamed()) { - messager.printError("Unnamed class is _not_ in an unnamed package.", unnamedClass); + if (!elements.getPackageOf(implicitClass).isUnnamed()) { + messager.printError("Implicitly declared class is _not_ in an unnamed package.", implicitClass); } - if (unnamedClass.getModifiers().contains(Modifier.PUBLIC) || - unnamedClass.getModifiers().contains(Modifier.PRIVATE) || - unnamedClass.getModifiers().contains(Modifier.PROTECTED)) { - messager.printError("Unnamed class does _not_ have package access.", unnamedClass); + if (implicitClass.getModifiers().contains(Modifier.PUBLIC) || + implicitClass.getModifiers().contains(Modifier.PRIVATE) || + implicitClass.getModifiers().contains(Modifier.PROTECTED)) { + messager.printError("Implicitly declared class does _not_ have package access.", implicitClass); } - if ( !types.isSameType(unnamedClass.getSuperclass(), + if ( !types.isSameType(implicitClass.getSuperclass(), elements.getTypeElement("java.lang.Object").asType())) { - messager.printError("Unnamed class does _not_ have java.lang.Object as a superclass.", unnamedClass); + messager.printError("Implicitly declared class does _not_ have java.lang.Object as a superclass.", implicitClass); } - if (!unnamedClass.getInterfaces().isEmpty()) { - messager.printError("Unnamed class has superinterfaces.", unnamedClass); + if (!implicitClass.getInterfaces().isEmpty()) { + messager.printError("Implicitly declared class has superinterfaces.", implicitClass); } - List ctors = constructorsIn(unnamedClass.getEnclosedElements()); + List ctors = constructorsIn(implicitClass.getEnclosedElements()); if (ctors.size() != 1 ) { - messager.printError("Did not find exactly one constructor", unnamedClass); + messager.printError("Did not find exactly one constructor", implicitClass); } if (!classOnly) { @@ -208,7 +199,7 @@ public class TestUnnamedClass extends JavacTestingAbstractProcessor { } } - List methods = methodsIn(unnamedClass.getEnclosedElements()); + List methods = methodsIn(implicitClass.getEnclosedElements()); // Just look for a method named "main"; don't check the other details. boolean mainFound = false; Name mainName = elements.getName("main"); @@ -220,7 +211,7 @@ public class TestUnnamedClass extends JavacTestingAbstractProcessor { } if (!mainFound) { - messager.printError("No main mehtod found", unnamedClass); + messager.printError("No main method found", implicitClass); } } } diff --git a/test/langtools/tools/javac/unnamed/UnnamedClassRecovery.out b/test/langtools/tools/javac/unnamed/UnnamedClassRecovery.out deleted file mode 100644 index 0389b156f01..00000000000 --- a/test/langtools/tools/javac/unnamed/UnnamedClassRecovery.out +++ /dev/null @@ -1,4 +0,0 @@ -UnnamedClassRecovery.java:7:33: compiler.err.expected: ';' -- compiler.note.preview.filename: UnnamedClassRecovery.java, DEFAULT -- compiler.note.preview.recompile -1 error