8315458: Implement JEP 463: Implicitly Declared Classes and Instance Main Method (Second Preview)

Reviewed-by: jlahoda, mcimadamore, vromero, rriggs, alanb, mchung
This commit is contained in:
Jim Laskey 2023-11-30 12:49:49 +00:00
parent 03759e892d
commit 04ad98ed32
48 changed files with 612 additions and 664 deletions

View file

@ -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}.
*
* <h2><a id=unnamedClasses>Unnamed Classes</a></h2>
*
* 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 <em>not</em> 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.
* <h2><a id=implicitClasses>Implicit Classes</a></h2>
*
* 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 <T> 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<T> 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<T> implements java.io.Serializable,
* @since 1.5
*/
public String getSimpleName() {
if (isUnnamedClass()) {
return "";
}
ReflectionData<T> rd = reflectionData();
String simpleName = rd.simpleName;
if (simpleName == null) {
@ -1874,7 +1862,6 @@ public final class Class<T> implements java.io.Serializable,
* <ul>
* <li>a {@linkplain #isLocalClass() local class}
* <li>a {@linkplain #isAnonymousClass() anonymous class}
* <li>an {@linkplain #isUnnamedClass() unnamed class}
* <li>a {@linkplain #isHidden() hidden class}
* <li>an array whose component type does not have a canonical name</li>
* </ul>
@ -1894,9 +1881,6 @@ public final class Class<T> implements java.io.Serializable,
* @since 1.5
*/
public String getCanonicalName() {
if (isUnnamedClass()) {
return null;
}
ReflectionData<T> rd = reflectionData();
String canonicalName = rd.canonicalName;
if (canonicalName == null) {
@ -1931,33 +1915,12 @@ public final class Class<T> 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<T> 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<T> 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<T> 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<T> 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;

View file

@ -2345,6 +2345,9 @@ public final class System {
public List<Method> 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();
}

View file

@ -65,6 +65,11 @@ public interface JavaLangAccess {
*/
List<Method> 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.
*/

View file

@ -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")

View file

@ -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<Method> 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<Method> 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);
}
}

View file

@ -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:
* <ul>
* <li>be declared in this class's hierarchy</li>
* <li>have the name "main"</li>
* <li>have a single argument of type {@code String[]}, {@code String...} or no argument</li>
* <li>have the return type of void</li>
* <li>be public, protected or package private</li>
* <li>not be abstract</li>
*</ul>
*
* 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.
* <p>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."
* <p>{@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;
}
}

View file

@ -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) {
mainMethod = MethodFinder.findMainMethod(mainClass);
if (mainMethod == null) {
// invalid main or not FX application, abort with an error
abort(null, "java.launcher.cls.error4", mainClass.getName(),
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<Module> 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;

View file

@ -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=\

View file

@ -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, "<init>", "()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, "<init>", "()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, "<init>", "()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, "<init>", "()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)

View file

@ -177,12 +177,11 @@ public interface Filer {
* <p>Creating a source file in or for an <em>unnamed</em> package in a <em>named</em>
* module is <em>not</em> supported.
*
* <p>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.
* <p>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 {
* <p>Creating a class file in or for an <em>unnamed</em> package in a <em>named</em>
* module is <em>not</em> supported.
*
* <p>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.
* <p>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

View file

@ -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 <i>canonical</i> 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

View file

@ -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);

View file

@ -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),

View file

@ -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'

View file

@ -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),

View file

@ -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<Type> getPermittedSubclasses() {
return permitted.map(s -> s.type);
}
@Override @DefinedBy(Api.LANGUAGE_MODEL)
public boolean isUnnamed() {
return (flags_field & Flags.UNNAMED_CLASS) != 0 ;
}
}

View file

@ -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 {

View file

@ -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);
}
}

View file

@ -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 {

View file

@ -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) {

View file

@ -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);

View file

@ -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<JCTree> 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<JCTree> topLevelDefs = isUnnamedClass ? constructUnnamedClass(defs.toList()) : defs.toList();
List<JCTree> 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<JCTree> constructUnnamedClass(List<JCTree> origDefs) {
// Restructure top level to be an implicit class.
private List<JCTree> constructImplictClass(List<JCTree> origDefs) {
ListBuffer<JCTree> topDefs = new ListBuffer<>();
ListBuffer<JCTree> 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<JCTree> topLevelMethodOrFieldDeclaration(JCModifiers mods) throws AssertionError {
int topPos = token.pos;
private List<JCTree> topLevelMethodOrFieldDeclaration(JCModifiers mods, Comment dc) throws AssertionError {
int pos = token.pos;
Comment dc = token.docComment();
dc = dc == null ? token.docComment() : dc;
List<JCTypeParameter> typarams = typeParametersOpt();
// if there are type parameters but no modifiers, save the start

View file

@ -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<Boolean, Void>(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);

View file

@ -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()

View file

@ -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'.

View file

@ -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) {

View file

@ -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;

View file

@ -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);
}
}
}

View file

@ -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);

View file

@ -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();
}
}
""",

View file

@ -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);
}
}

View file

@ -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");

View file

@ -23,8 +23,8 @@
/**
* Exemplar unnamed class.
* Exemplar implicit class.
*/
public void main() {
void main() {
System.out.println("Hello, world.");
}

View file

@ -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:

View file

@ -0,0 +1,4 @@
ImplicitClassRecovery.java:7:33: compiler.err.expected: ';'
- compiler.note.preview.filename: ImplicitClassRecovery.java, DEFAULT
- compiler.note.preview.recompile
1 error

View file

@ -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");
}
}
}

View file

@ -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 {}

View file

@ -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

View file

@ -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);
}
}
}

View file

@ -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

View file

@ -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) {
}

View file

@ -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

View file

@ -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

View file

@ -2472,7 +2472,7 @@ public class JavacParserTest extends TestCase {
result,
"""
\n\
/*synthetic*/ final class Test {
final class Test {
\n\
void main() {
(ERROR);

View file

@ -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<ExecutableElement> ctors = constructorsIn(unnamedClass.getEnclosedElements());
List<ExecutableElement> 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<ExecutableElement> methods = methodsIn(unnamedClass.getEnclosedElements());
List<ExecutableElement> 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);
}
}
}

View file

@ -1,4 +0,0 @@
UnnamedClassRecovery.java:7:33: compiler.err.expected: ';'
- compiler.note.preview.filename: UnnamedClassRecovery.java, DEFAULT
- compiler.note.preview.recompile
1 error