mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 07:14:30 +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) {
|
||||
// invalid main or not FX application, abort with an error
|
||||
abort(null, "java.launcher.cls.error4", mainClass.getName(),
|
||||
JAVAFX_APPLICATION_CLASS_NAME);
|
||||
mainMethod = MethodFinder.findMainMethod(mainClass);
|
||||
|
||||
if (mainMethod == null) {
|
||||
// invalid main or not FX application, abort with an error
|
||||
abort(null, "java.launcher.cls.error2", mainClass.getName(),
|
||||
JAVAFX_APPLICATION_CLASS_NAME);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
if (mainClass.getModule().isNamed()) {
|
||||
abort(e, "java.launcher.module.error5",
|
||||
abort(e, "java.launcher.module.error3",
|
||||
mainClass.getName(), mainClass.getModule().getName(),
|
||||
e.getClass().getName(), e.getLocalizedMessage());
|
||||
} else {
|
||||
abort(e, "java.launcher.cls.error7", mainClass.getName(),
|
||||
abort(e, "java.launcher.cls.error5", mainClass.getName(),
|
||||
e.getClass().getName(), e.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
|
||||
setMainType(mainMethod);
|
||||
|
||||
/*
|
||||
* findMainMethod (above) will choose the correct method, based
|
||||
* on its name and parameter type, however, we still have to
|
||||
* ensure that the method is static (non-preview) and returns a void.
|
||||
*/
|
||||
int mods = mainMethod.getModifiers();
|
||||
boolean isStatic = Modifier.isStatic(mods);
|
||||
boolean isPublic = Modifier.isPublic(mods);
|
||||
|
@ -945,32 +919,26 @@ public final class LauncherHelper {
|
|||
|
||||
if (!PreviewFeatures.isEnabled()) {
|
||||
if (!isStatic || !isPublic || noArgs) {
|
||||
abort(null, "java.launcher.cls.error2", "static",
|
||||
mainMethod.getDeclaringClass().getName());
|
||||
abort(null, "java.launcher.cls.error2", mainClass.getName(),
|
||||
JAVAFX_APPLICATION_CLASS_NAME);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isStatic) {
|
||||
String className = mainMethod.getDeclaringClass().getName();
|
||||
if (mainClass.isMemberClass() && !Modifier.isStatic(mainClass.getModifiers())) {
|
||||
abort(null, "java.launcher.cls.error9",
|
||||
mainMethod.getDeclaringClass().getName());
|
||||
abort(null, "java.launcher.cls.error7", className);
|
||||
}
|
||||
try {
|
||||
Constructor<?> constructor = mainClass.getDeclaredConstructor();
|
||||
if (Modifier.isPrivate(constructor.getModifiers())) {
|
||||
abort(null, "java.launcher.cls.error8",
|
||||
mainMethod.getDeclaringClass().getName());
|
||||
abort(null, "java.launcher.cls.error6", className);
|
||||
}
|
||||
} catch (Throwable ex) {
|
||||
abort(null, "java.launcher.cls.error8",
|
||||
mainMethod.getDeclaringClass().getName());
|
||||
abort(null, "java.launcher.cls.error6", className);
|
||||
}
|
||||
}
|
||||
|
||||
if (mainMethod.getReturnType() != java.lang.Void.TYPE) {
|
||||
abort(null, "java.launcher.cls.error3",
|
||||
mainMethod.getDeclaringClass().getName());
|
||||
}
|
||||
}
|
||||
|
||||
private static final String encprop = "sun.jnu.encoding";
|
||||
|
@ -1122,13 +1090,13 @@ public final class LauncherHelper {
|
|||
// find the module with the FX launcher
|
||||
Optional<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=\
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue