mirror of
https://github.com/openjdk/jdk.git
synced 2026-03-31 02:00:02 +00:00
8377004: Java Launcher incorrectly allows inheriting a package-private main from another package
Reviewed-by: jpai, alanb
This commit is contained in:
parent
40e6069ff0
commit
2449dc2e80
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2023, 2026, 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
|
||||
@ -27,6 +27,7 @@ package jdk.internal.misc;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Objects;
|
||||
|
||||
import jdk.internal.access.JavaLangAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
@ -88,21 +89,27 @@ public class MethodFinder {
|
||||
mainMethod = JLA.findMethod(cls, false, "main", String[].class);
|
||||
}
|
||||
|
||||
if (mainMethod == null || !isValidMainMethod(mainMethod)) {
|
||||
if (mainMethod == null || !isValidMainMethod(cls, mainMethod)) {
|
||||
mainMethod = JLA.findMethod(cls, false, "main");
|
||||
}
|
||||
|
||||
if (mainMethod == null || !isValidMainMethod(mainMethod)) {
|
||||
if (mainMethod == null || !isValidMainMethod(cls, mainMethod)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return mainMethod;
|
||||
}
|
||||
|
||||
private static boolean isValidMainMethod(Method mainMethodCandidate) {
|
||||
private static boolean isValidMainMethod(Class<?> initialClass, Method mainMethodCandidate) {
|
||||
return mainMethodCandidate.getReturnType() == void.class &&
|
||||
!Modifier.isPrivate(mainMethodCandidate.getModifiers());
|
||||
|
||||
!Modifier.isPrivate(mainMethodCandidate.getModifiers()) &&
|
||||
(Modifier.isPublic(mainMethodCandidate.getModifiers()) ||
|
||||
Modifier.isProtected(mainMethodCandidate.getModifiers()) ||
|
||||
isInSameRuntimePackage(initialClass, mainMethodCandidate.getDeclaringClass()));
|
||||
}
|
||||
|
||||
private static boolean isInSameRuntimePackage(Class<?> c1, Class<?> c2) {
|
||||
return Objects.equals(c1.getPackageName(), c2.getPackageName()) &&
|
||||
c1.getClassLoader() == c2.getClassLoader();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2023, 2026, 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
|
||||
@ -28,11 +28,12 @@ import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 8329420
|
||||
* @bug 8329420 8377004
|
||||
* @summary test execution priority and behavior of main methods
|
||||
* @run main/timeout=480 InstanceMainTest
|
||||
*/
|
||||
public class InstanceMainTest extends TestHelper {
|
||||
private static String JAVA_VERSION = System.getProperty("java.specification.version");
|
||||
|
||||
private static final String[] SOURCES = new String[] {
|
||||
// static dominating with args
|
||||
@ -227,11 +228,7 @@ public class InstanceMainTest extends TestHelper {
|
||||
private static void testExecutionOrder() throws Exception {
|
||||
for (TestCase testCase : EXECUTION_ORDER) {
|
||||
performTest(testCase.sourceCode, testCase.enablePreview(), tr -> {
|
||||
if (!Objects.equals(testCase.expectedOutput, tr.testOutput)) {
|
||||
throw new AssertionError("Unexpected output, " +
|
||||
"expected: " + testCase.expectedOutput +
|
||||
", actual: " + tr.testOutput);
|
||||
}
|
||||
assertEquals(testCase.expectedOutput, tr.testOutput);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -350,20 +347,227 @@ public class InstanceMainTest extends TestHelper {
|
||||
private static void performTest(String source, boolean enablePreview, Consumer<TestResult> validator) throws Exception {
|
||||
Path mainClass = Path.of("MainClass.java");
|
||||
Files.writeString(mainClass, source);
|
||||
var version = System.getProperty("java.specification.version");
|
||||
var previewRuntime = enablePreview ? "--enable-preview" : "-DtestNoPreview";
|
||||
var previewCompile = enablePreview ? "--enable-preview" : "-XDtestNoPreview";
|
||||
var trSource = doExec(javaCmd, previewRuntime, "--source", version, "MainClass.java");
|
||||
var trSource = doExec(javaCmd, previewRuntime, "--source", JAVA_VERSION, "MainClass.java");
|
||||
validator.accept(trSource);
|
||||
compile(previewCompile, "--source", version, "MainClass.java");
|
||||
compile(previewCompile, "--source", JAVA_VERSION, "MainClass.java");
|
||||
String cp = mainClass.toAbsolutePath().getParent().toString();
|
||||
var trCompile = doExec(javaCmd, previewRuntime, "--class-path", cp, "MainClass");
|
||||
validator.accept(trCompile);
|
||||
}
|
||||
|
||||
private static void testInheritance() throws Exception {
|
||||
Path testInheritance = Path.of("testInheritance");
|
||||
Path src = testInheritance.resolve("src");
|
||||
Path classes = testInheritance.resolve("classes");
|
||||
Path mainClass = src.resolve("Main.java");
|
||||
Path libClass = src.resolve("p").resolve("Lib.java");
|
||||
|
||||
Files.createDirectories(libClass.getParent());
|
||||
|
||||
Files.writeString(mainClass,
|
||||
"""
|
||||
import p.Lib;
|
||||
|
||||
public class Main extends Lib {
|
||||
public void main() {
|
||||
System.err.println("Main!");
|
||||
}
|
||||
}
|
||||
""");
|
||||
|
||||
{
|
||||
Files.writeString(libClass,
|
||||
"""
|
||||
package p;
|
||||
public class Lib {
|
||||
void main(String... args) {
|
||||
System.err.println("Lib!");
|
||||
}
|
||||
}
|
||||
""");
|
||||
compile("--release", JAVA_VERSION, "-d", classes.toString(), mainClass.toString(), libClass.toString());
|
||||
var tr = doExec(javaCmd, "--class-path", classes.toString(), "Main");
|
||||
assertEquals(List.of("Main!"), tr.testOutput);
|
||||
}
|
||||
|
||||
{
|
||||
Files.writeString(libClass,
|
||||
"""
|
||||
package p;
|
||||
public class Lib {
|
||||
protected void main(String... args) {
|
||||
System.err.println("Lib!");
|
||||
}
|
||||
}
|
||||
""");
|
||||
compile("--release", JAVA_VERSION, "-d", classes.toString(), mainClass.toString(), libClass.toString());
|
||||
var tr = doExec(javaCmd, "--class-path", classes.toString(), "Main");
|
||||
assertEquals(List.of("Lib!"), tr.testOutput);
|
||||
}
|
||||
|
||||
{
|
||||
Files.writeString(libClass,
|
||||
"""
|
||||
package p;
|
||||
public class Lib {
|
||||
public void main(String... args) {
|
||||
System.err.println("Lib!");
|
||||
}
|
||||
}
|
||||
""");
|
||||
compile("--release", JAVA_VERSION, "-d", classes.toString(), mainClass.toString(), libClass.toString());
|
||||
var tr = doExec(javaCmd, "--class-path", classes.toString(), "Main");
|
||||
assertEquals(List.of("Lib!"), tr.testOutput);
|
||||
}
|
||||
|
||||
{
|
||||
Files.writeString(mainClass,
|
||||
"""
|
||||
package p;
|
||||
|
||||
public class Main extends Lib {
|
||||
public void main() {
|
||||
System.err.println("Main!");
|
||||
}
|
||||
}
|
||||
""");
|
||||
|
||||
Files.writeString(libClass,
|
||||
"""
|
||||
package p;
|
||||
public class Lib {
|
||||
void main(String... args) {
|
||||
System.err.println("Lib!");
|
||||
}
|
||||
}
|
||||
""");
|
||||
compile("--release", JAVA_VERSION, "-d", classes.toString(), mainClass.toString(), libClass.toString());
|
||||
var tr = doExec(javaCmd, "--class-path", classes.toString(), "p.Main");
|
||||
assertEquals(List.of("Lib!"), tr.testOutput);
|
||||
}
|
||||
|
||||
{
|
||||
Files.writeString(mainClass,
|
||||
"""
|
||||
package p;
|
||||
|
||||
public class Main implements Lib {
|
||||
public void main() {
|
||||
System.err.println("Main!");
|
||||
}
|
||||
}
|
||||
""");
|
||||
|
||||
Files.writeString(libClass,
|
||||
"""
|
||||
package p;
|
||||
public interface Lib {
|
||||
public default void main(String... args) {
|
||||
System.err.println("Lib!");
|
||||
}
|
||||
}
|
||||
""");
|
||||
compile("--release", JAVA_VERSION, "-d", classes.toString(), mainClass.toString(), libClass.toString());
|
||||
var tr = doExec(javaCmd, "--class-path", classes.toString(), "p.Main");
|
||||
assertEquals(List.of("Lib!"), tr.testOutput);
|
||||
}
|
||||
|
||||
{
|
||||
Files.writeString(mainClass,
|
||||
"""
|
||||
package p;
|
||||
|
||||
public class Main implements Lib {
|
||||
public void main() {
|
||||
System.err.println("Main!");
|
||||
}
|
||||
}
|
||||
""");
|
||||
|
||||
Files.writeString(libClass,
|
||||
"""
|
||||
package p;
|
||||
public interface Lib {
|
||||
public static void main(String... args) {
|
||||
System.err.println("Lib!");
|
||||
}
|
||||
}
|
||||
""");
|
||||
compile("--release", JAVA_VERSION, "-d", classes.toString(), mainClass.toString(), libClass.toString());
|
||||
var tr = doExec(javaCmd, "--class-path", classes.toString(), "p.Main");
|
||||
assertEquals(List.of("Main!"), tr.testOutput);
|
||||
}
|
||||
|
||||
{
|
||||
Files.writeString(mainClass,
|
||||
"""
|
||||
package p;
|
||||
|
||||
public class Main extends AbstractClass implements Lib {
|
||||
}
|
||||
abstract class AbstractClass {
|
||||
public void main(String... args) {
|
||||
System.err.println("Correct.");
|
||||
}
|
||||
}
|
||||
""");
|
||||
|
||||
Files.writeString(libClass,
|
||||
"""
|
||||
package p;
|
||||
public interface Lib {
|
||||
default void main(String... args) {
|
||||
System.err.println("Incorrect!");
|
||||
}
|
||||
}
|
||||
""");
|
||||
compile("--release", JAVA_VERSION, "-d", classes.toString(), mainClass.toString(), libClass.toString());
|
||||
var tr = doExec(javaCmd, "--class-path", classes.toString(), "p.Main");
|
||||
assertEquals(List.of("Correct."), tr.testOutput);
|
||||
}
|
||||
|
||||
{
|
||||
Files.writeString(mainClass,
|
||||
"""
|
||||
package p;
|
||||
|
||||
public class Main extends AbstractClass implements Lib {
|
||||
}
|
||||
abstract class AbstractClass {
|
||||
public void main() {
|
||||
System.err.println("Incorrect!");
|
||||
}
|
||||
}
|
||||
""");
|
||||
|
||||
Files.writeString(libClass,
|
||||
"""
|
||||
package p;
|
||||
public interface Lib {
|
||||
default void main(String... args) {
|
||||
System.err.println("Correct.");
|
||||
}
|
||||
}
|
||||
""");
|
||||
compile("--release", JAVA_VERSION, "-d", classes.toString(), mainClass.toString(), libClass.toString());
|
||||
var tr = doExec(javaCmd, "--class-path", classes.toString(), "p.Main");
|
||||
assertEquals(List.of("Correct."), tr.testOutput);
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertEquals(List<String> expected, List<String> actual) {
|
||||
if (!Objects.equals(expected, actual)) {
|
||||
throw new AssertionError("Unexpected output, " +
|
||||
"expected: " + expected +
|
||||
", actual: " + actual);
|
||||
}
|
||||
}
|
||||
public static void main(String... args) throws Exception {
|
||||
testMethodOrder();
|
||||
testExecutionOrder();
|
||||
testExecutionErrors();
|
||||
testInheritance();
|
||||
}
|
||||
}
|
||||
|
||||
196
test/jdk/tools/launcher/MethodFinderTest.java
Normal file
196
test/jdk/tools/launcher/MethodFinderTest.java
Normal file
@ -0,0 +1,196 @@
|
||||
/*
|
||||
* Copyright (c) 2026, 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 8377004
|
||||
* @summary Whitebox test for MethodFinder
|
||||
* @modules java.base/jdk.internal.misc
|
||||
* jdk.compiler
|
||||
* jdk.zipfs
|
||||
* @run junit MethodFinderTest
|
||||
*/
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import jdk.internal.misc.MethodFinder;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class MethodFinderTest {
|
||||
|
||||
private static final String JAVA_VERSION = System.getProperty("java.specification.version");
|
||||
|
||||
@Test
|
||||
public void testDistinctClassLoaders() throws Exception {
|
||||
Path base = Path.of("testDistinctClassLoaders");
|
||||
Path libSrc = base.resolve("libSrc");
|
||||
Path libClasses = base.resolve("libClasses");
|
||||
Path libJava = libSrc.resolve("p").resolve("Lib.java");
|
||||
|
||||
Files.createDirectories(libJava.getParent());
|
||||
|
||||
Files.writeString(libJava,
|
||||
"""
|
||||
package p;
|
||||
public class Lib {
|
||||
void main(String... args) {
|
||||
System.err.println("Lib!");
|
||||
}
|
||||
}
|
||||
""");
|
||||
|
||||
TestHelper.compile("--release", JAVA_VERSION, "-d", libClasses.toString(), libJava.toString());
|
||||
|
||||
Path mainSrc = base.resolve("mainSrc");
|
||||
Path mainClasses = base.resolve("mainClasses");
|
||||
Path mainJava = mainSrc.resolve("p").resolve("Main.java");
|
||||
|
||||
Files.createDirectories(mainJava.getParent());
|
||||
|
||||
Files.writeString(mainJava,
|
||||
"""
|
||||
package p;
|
||||
|
||||
public class Main extends Lib {
|
||||
public void main() {
|
||||
System.err.println("Main!");
|
||||
}
|
||||
}
|
||||
""");
|
||||
|
||||
TestHelper.compile("--release", JAVA_VERSION, "--class-path", libClasses.toString(), "-d", mainClasses.toString(), mainJava.toString());
|
||||
|
||||
{
|
||||
ClassLoader cl = new URLClassLoader(new URL[] {
|
||||
libClasses.toUri().toURL(),
|
||||
mainClasses.toUri().toURL()
|
||||
});
|
||||
Class<?> mainClass = cl.loadClass("p.Main");
|
||||
Method mainMethod = MethodFinder.findMainMethod(mainClass);
|
||||
|
||||
//p.Main and p.Lib are in the same runtime package:
|
||||
assertEquals("p.Lib", mainMethod.getDeclaringClass().getName());
|
||||
}
|
||||
|
||||
{
|
||||
ClassLoader libCl = new URLClassLoader(new URL[] {
|
||||
libClasses.toUri().toURL(),
|
||||
});
|
||||
ClassLoader mainCl = new URLClassLoader(new URL[] {
|
||||
mainClasses.toUri().toURL()
|
||||
}, libCl);
|
||||
Class<?> mainClass = mainCl.loadClass("p.Main");
|
||||
Method mainMethod = MethodFinder.findMainMethod(mainClass);
|
||||
|
||||
//p.Main and p.Lib are in the different runtime packages:
|
||||
assertEquals("p.Main", mainMethod.getDeclaringClass().getName());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrongEquals() throws Exception {
|
||||
Path base = Path.of("testDistinctClassLoaders");
|
||||
Path libSrc = base.resolve("libSrc");
|
||||
Path libClasses = base.resolve("libClasses");
|
||||
Path libJava = libSrc.resolve("p").resolve("Lib.java");
|
||||
|
||||
Files.createDirectories(libJava.getParent());
|
||||
|
||||
Files.writeString(libJava,
|
||||
"""
|
||||
package p;
|
||||
public class Lib {
|
||||
void main(String... args) {
|
||||
System.err.println("Lib!");
|
||||
}
|
||||
}
|
||||
""");
|
||||
|
||||
TestHelper.compile("--release", JAVA_VERSION, "-d", libClasses.toString(), libJava.toString());
|
||||
|
||||
Path mainSrc = base.resolve("mainSrc");
|
||||
Path mainClasses = base.resolve("mainClasses");
|
||||
Path mainJava = mainSrc.resolve("p").resolve("Main.java");
|
||||
|
||||
Files.createDirectories(mainJava.getParent());
|
||||
|
||||
Files.writeString(mainJava,
|
||||
"""
|
||||
package p;
|
||||
|
||||
public class Main extends Lib {
|
||||
public void main() {
|
||||
System.err.println("Main!");
|
||||
}
|
||||
}
|
||||
""");
|
||||
|
||||
TestHelper.compile("--release", JAVA_VERSION, "--class-path", libClasses.toString(), "-d", mainClasses.toString(), mainJava.toString());
|
||||
|
||||
{
|
||||
ClassLoader cl = new URLClassLoader(new URL[] {
|
||||
libClasses.toUri().toURL(),
|
||||
mainClasses.toUri().toURL()
|
||||
});
|
||||
Class<?> mainClass = cl.loadClass("p.Main");
|
||||
Method mainMethod = MethodFinder.findMainMethod(mainClass);
|
||||
|
||||
//p.Main and p.Lib are in the same runtime package:
|
||||
assertEquals("p.Lib", mainMethod.getDeclaringClass().getName());
|
||||
}
|
||||
|
||||
{
|
||||
class WrongEquals extends URLClassLoader {
|
||||
|
||||
public WrongEquals(URL[] urls) {
|
||||
super(urls);
|
||||
}
|
||||
|
||||
public WrongEquals(URL[] urls, ClassLoader parent) {
|
||||
super(urls, parent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof WrongEquals;
|
||||
}
|
||||
}
|
||||
ClassLoader libCl = new WrongEquals(new URL[] {
|
||||
libClasses.toUri().toURL(),
|
||||
});
|
||||
ClassLoader mainCl = new WrongEquals(new URL[] {
|
||||
mainClasses.toUri().toURL()
|
||||
}, libCl);
|
||||
Class<?> mainClass = mainCl.loadClass("p.Main");
|
||||
Method mainMethod = MethodFinder.findMainMethod(mainClass);
|
||||
|
||||
//p.Main and p.Lib are in the different runtime packages:
|
||||
assertEquals("p.Main", mainMethod.getDeclaringClass().getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user