blob: 60895b8115a7b1710f816695189d6c75b1c37cf6 [file]
/*
* Copyright (c) 2023, 2025, 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>
*</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) {
Method mainMethod = JLA.findMethod(cls, true, "main", String[].class);
if (mainMethod == null) {
//if not public method, try to lookup a non-public one
mainMethod = JLA.findMethod(cls, false, "main", String[].class);
}
if (mainMethod == null || !isValidMainMethod(mainMethod)) {
mainMethod = JLA.findMethod(cls, false, "main");
}
if (mainMethod == null || !isValidMainMethod(mainMethod)) {
return null;
}
return mainMethod;
}
private static boolean isValidMainMethod(Method mainMethodCandidate) {
return mainMethodCandidate.getReturnType() == void.class &&
!Modifier.isPrivate(mainMethodCandidate.getModifiers());
}
}