mirror of
https://github.com/openjdk/jdk.git
synced 2026-03-14 09:53:18 +00:00
8328339: Static import prevents source launcher from finding class with main method
Reviewed-by: jlahoda
This commit is contained in:
parent
256d48b196
commit
9bc741d04f
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2023, 2024, 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
|
||||
@ -34,9 +34,6 @@ import com.sun.tools.javac.resources.LauncherProperties.Errors;
|
||||
import com.sun.tools.javac.util.Context;
|
||||
import com.sun.tools.javac.util.Context.Factory;
|
||||
|
||||
import javax.lang.model.element.ElementKind;
|
||||
import javax.lang.model.element.NestingKind;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.tools.JavaFileManager;
|
||||
import javax.tools.JavaFileObject;
|
||||
import javax.tools.StandardLocation;
|
||||
@ -112,10 +109,9 @@ final class MemoryContext {
|
||||
* Any messages generated during compilation will be written to the stream
|
||||
* provided when this object was created.
|
||||
*
|
||||
* @return the list of top-level types defined in the source file
|
||||
* @throws Fault if any compilation errors occur, or if no class was found
|
||||
*/
|
||||
List<String> compileProgram() throws Fault {
|
||||
void compileProgram() throws Fault {
|
||||
var units = new ArrayList<JavaFileObject>();
|
||||
units.add(descriptor.fileObject());
|
||||
if (descriptor.isModular()) {
|
||||
@ -126,35 +122,10 @@ final class MemoryContext {
|
||||
var context = new Context();
|
||||
MemoryPreview.registerInstance(context);
|
||||
var task = compiler.getTask(out, memoryFileManager, null, opts, null, units, context);
|
||||
var fileUri = descriptor.fileObject().toUri();
|
||||
var names = new ArrayList<String>();
|
||||
task.addTaskListener(new TaskListener() {
|
||||
@Override
|
||||
public void started(TaskEvent event) {
|
||||
if (event.getKind() != TaskEvent.Kind.ANALYZE) return;
|
||||
TypeElement element = event.getTypeElement();
|
||||
if (element.getNestingKind() != NestingKind.TOP_LEVEL) return;
|
||||
JavaFileObject source = event.getSourceFile();
|
||||
if (source == null) return;
|
||||
if (!source.toUri().equals(fileUri)) return;
|
||||
ElementKind kind = element.getKind();
|
||||
if (kind != ElementKind.CLASS
|
||||
&& kind != ElementKind.ENUM
|
||||
&& kind != ElementKind.INTERFACE
|
||||
&& kind != ElementKind.RECORD)
|
||||
return;
|
||||
var name = element.getQualifiedName().toString();
|
||||
names.add(name);
|
||||
}
|
||||
});
|
||||
var ok = task.call();
|
||||
if (!ok) {
|
||||
throw new Fault(Errors.CompilationFailed);
|
||||
}
|
||||
if (names.isEmpty()) {
|
||||
throw new Fault(Errors.NoClass);
|
||||
}
|
||||
return List.copyOf(names);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2023, 2024, 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
|
||||
@ -25,6 +25,7 @@
|
||||
|
||||
package com.sun.tools.javac.launcher;
|
||||
|
||||
import com.sun.source.tree.ClassTree;
|
||||
import com.sun.tools.javac.api.JavacTool;
|
||||
import com.sun.tools.javac.resources.LauncherProperties.Errors;
|
||||
|
||||
@ -33,6 +34,7 @@ import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
@ -46,30 +48,46 @@ import java.util.TreeSet;
|
||||
* risk. This code and its internal interfaces are subject to change
|
||||
* or deletion without notice.</strong></p>
|
||||
*/
|
||||
public record ProgramDescriptor(ProgramFileObject fileObject, Optional<String> packageName, Path sourceRootPath) {
|
||||
public record ProgramDescriptor(
|
||||
ProgramFileObject fileObject,
|
||||
Optional<String> packageName,
|
||||
List<String> qualifiedTypeNames,
|
||||
Path sourceRootPath) {
|
||||
static ProgramDescriptor of(ProgramFileObject fileObject) throws Fault {
|
||||
var file = fileObject.getFile();
|
||||
var packageName = ""; // empty string will be converted into an empty optional
|
||||
var packageNameAndDot = ""; // empty string or packageName + '.'
|
||||
var qualifiedTypeNames = new ArrayList<String>();
|
||||
try {
|
||||
var compiler = JavacTool.create();
|
||||
var standardFileManager = compiler.getStandardFileManager(null, null, null);
|
||||
var units = List.of(fileObject);
|
||||
var task = compiler.getTask(null, standardFileManager, diagnostic -> {}, null, null, units);
|
||||
for (var tree : task.parse()) {
|
||||
var packageTree = tree.getPackage();
|
||||
if (packageTree != null) {
|
||||
var packageName = packageTree.getPackageName().toString();
|
||||
var root = computeSourceRootPath(file, packageName);
|
||||
return new ProgramDescriptor(fileObject, Optional.of(packageName), root);
|
||||
var tree = task.parse().iterator().next(); // single compilation unit
|
||||
var packageTree = tree.getPackage();
|
||||
if (packageTree != null) {
|
||||
packageName = packageTree.getPackageName().toString();
|
||||
packageNameAndDot = packageName + '.';
|
||||
}
|
||||
for (var type : tree.getTypeDecls()) {
|
||||
if (type instanceof ClassTree classType) {
|
||||
qualifiedTypeNames.add(packageNameAndDot + classType.getSimpleName());
|
||||
}
|
||||
}
|
||||
} catch (IOException ignore) {
|
||||
// fall through to let actual compilation determine the error message
|
||||
}
|
||||
var root = computeSourceRootPath(file, "");
|
||||
return new ProgramDescriptor(fileObject, Optional.empty(), root);
|
||||
if (qualifiedTypeNames.isEmpty()) {
|
||||
throw new Fault(Errors.NoClass);
|
||||
}
|
||||
return new ProgramDescriptor(
|
||||
fileObject,
|
||||
packageName.isEmpty() ? Optional.empty() : Optional.of(packageName),
|
||||
List.copyOf(qualifiedTypeNames),
|
||||
computeSourceRootPath(file, packageName));
|
||||
}
|
||||
|
||||
public static Path computeSourceRootPath(Path program, String packageName) {
|
||||
public static Path computeSourceRootPath(Path program, String packageName) throws Fault {
|
||||
var absolute = program.normalize().toAbsolutePath();
|
||||
var absoluteRoot = absolute.getRoot();
|
||||
assert absoluteRoot != null;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2023, 2024, 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
|
||||
@ -147,10 +147,10 @@ public final class SourceLauncher {
|
||||
ProgramDescriptor program = ProgramDescriptor.of(ProgramFileObject.of(file));
|
||||
RelevantJavacOptions options = RelevantJavacOptions.of(program, runtimeArgs);
|
||||
MemoryContext context = new MemoryContext(out, program, options);
|
||||
List<String> names = context.compileProgram();
|
||||
context.compileProgram();
|
||||
|
||||
String[] mainArgs = Arrays.copyOfRange(args, 1, args.length);
|
||||
var appClass = execute(names, mainArgs, context);
|
||||
var appClass = execute(context, mainArgs);
|
||||
|
||||
return new Result(appClass, context.getNamesOfCompiledClasses());
|
||||
}
|
||||
@ -184,20 +184,20 @@ public final class SourceLauncher {
|
||||
* Invokes the {@code main} method of a program class, using a class loader that
|
||||
* will load recently compiled classes from memory.
|
||||
*
|
||||
* @param topLevelClassNames the names of classes in the program compilation unit
|
||||
* @param mainArgs the arguments for the {@code main} method
|
||||
* @param context the context for the class to be executed
|
||||
* @throws Fault if there is a problem finding or invoking the {@code main} method
|
||||
* @throws InvocationTargetException if the {@code main} method throws an exception
|
||||
*/
|
||||
private Class<?> execute(List<String> topLevelClassNames, String[] mainArgs, MemoryContext context)
|
||||
private Class<?> execute(MemoryContext context, String[] mainArgs)
|
||||
throws Fault, InvocationTargetException {
|
||||
System.setProperty("jdk.launcher.sourcefile", context.getSourceFileAsString());
|
||||
ClassLoader parentLoader = ClassLoader.getSystemClassLoader();
|
||||
ProgramDescriptor program = context.getProgramDescriptor();
|
||||
|
||||
// 1. Find a main method in the first class and if there is one - invoke it
|
||||
Class<?> firstClass;
|
||||
String firstClassName = topLevelClassNames.getFirst();
|
||||
String firstClassName = program.qualifiedTypeNames().getFirst();
|
||||
try {
|
||||
ClassLoader loader = context.newClassLoaderFor(parentLoader, firstClassName);
|
||||
firstClass = Class.forName(firstClassName, false, loader);
|
||||
@ -208,10 +208,14 @@ public final class SourceLauncher {
|
||||
Method mainMethod = MethodFinder.findMainMethod(firstClass);
|
||||
if (mainMethod == null) {
|
||||
// 2. If the first class doesn't have a main method, look for a class with a matching name
|
||||
var compilationUnitName = context.getProgramDescriptor().fileObject().getFile().getFileName().toString();
|
||||
var compilationUnitName = program.fileObject().getFile().getFileName().toString();
|
||||
assert compilationUnitName.endsWith(".java");
|
||||
var expectedName = compilationUnitName.substring(0, compilationUnitName.length() - 5);
|
||||
var actualName = topLevelClassNames.stream()
|
||||
var expectedSimpleName = compilationUnitName.substring(0, compilationUnitName.length() - 5);
|
||||
var expectedPackageName = program.packageName().orElse("");
|
||||
var expectedName = expectedPackageName.isEmpty()
|
||||
? expectedSimpleName
|
||||
: expectedPackageName + '.' + expectedSimpleName;
|
||||
var actualName = program.qualifiedTypeNames().stream()
|
||||
.filter(name -> name.equals(expectedName))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new Fault(Errors.CantFindClass(expectedName)));
|
||||
|
||||
@ -23,7 +23,7 @@
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8192920 8204588 8246774 8248843 8268869 8235876
|
||||
* @bug 8192920 8204588 8246774 8248843 8268869 8235876 8328339
|
||||
* @summary Test source launcher
|
||||
* @library /tools/lib
|
||||
* @enablePreview
|
||||
@ -110,6 +110,27 @@ public class SourceLauncherTest extends TestRunner {
|
||||
testSuccess(base.resolve("hello").resolve("World.java"), "Hello World! [1, 2, 3]\n");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHelloWorldInPackageWithStaticImport(Path base) throws IOException {
|
||||
tb.writeJavaFiles(base,
|
||||
"""
|
||||
package hello;
|
||||
import static hello.Helper.*;
|
||||
import java.util.Arrays;
|
||||
class World {
|
||||
public static void main(String... args) {
|
||||
m(args);
|
||||
}
|
||||
}
|
||||
class Helper {
|
||||
static void m(String... args) {
|
||||
System.out.println("Hello World! " + Arrays.toString(args));
|
||||
}
|
||||
}
|
||||
""");
|
||||
testSuccess(base.resolve("hello").resolve("World.java"), "Hello World! [1, 2, 3]\n");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHelloWorldWithAux(Path base) throws IOException {
|
||||
tb.writeJavaFiles(base,
|
||||
@ -300,7 +321,7 @@ public class SourceLauncherTest extends TestRunner {
|
||||
public void testMismatchOfPathAndPackage(Path base) throws IOException {
|
||||
Files.createDirectories(base);
|
||||
Path file = base.resolve("MismatchOfPathAndPackage.java");
|
||||
Files.write(file, List.of("package p;"));
|
||||
Files.write(file, List.of("package p; class MismatchOfPathAndPackage {}"));
|
||||
testError(file, "", "error: end of path to source file does not match its package name p: " + file);
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user