mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 14:54:52 +02:00
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:
parent
03759e892d
commit
04ad98ed32
48 changed files with 612 additions and 664 deletions
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
108
src/java.base/share/classes/jdk/internal/misc/MethodFinder.java
Normal file
108
src/java.base/share/classes/jdk/internal/misc/MethodFinder.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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=\
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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 ;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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'.
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
""",
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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");
|
|
@ -23,8 +23,8 @@
|
|||
|
||||
|
||||
/**
|
||||
* Exemplar unnamed class.
|
||||
* Exemplar implicit class.
|
||||
*/
|
||||
public void main() {
|
||||
void main() {
|
||||
System.out.println("Hello, world.");
|
||||
}
|
|
@ -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:
|
|
@ -0,0 +1,4 @@
|
|||
ImplicitClassRecovery.java:7:33: compiler.err.expected: ';'
|
||||
- compiler.note.preview.filename: ImplicitClassRecovery.java, DEFAULT
|
||||
- compiler.note.preview.recompile
|
||||
1 error
|
109
test/langtools/tools/javac/ImplicitClass/NestedClasses.java
Normal file
109
test/langtools/tools/javac/ImplicitClass/NestedClasses.java
Normal 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");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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 {}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
@ -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) {
|
||||
}
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -2472,7 +2472,7 @@ public class JavacParserTest extends TestCase {
|
|||
result,
|
||||
"""
|
||||
\n\
|
||||
/*synthetic*/ final class Test {
|
||||
final class Test {
|
||||
\n\
|
||||
void main() {
|
||||
(ERROR);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
UnnamedClassRecovery.java:7:33: compiler.err.expected: ';'
|
||||
- compiler.note.preview.filename: UnnamedClassRecovery.java, DEFAULT
|
||||
- compiler.note.preview.recompile
|
||||
1 error
|
Loading…
Add table
Add a link
Reference in a new issue