Skip to content

Commit 40d9c79

Browse files
sormuraspull[bot]
authored andcommitted
8328339: Static import prevents source launcher from finding class with main method
Reviewed-by: jlahoda
1 parent f1343d0 commit 40d9c79

File tree

4 files changed

+67
-53
lines changed

4 files changed

+67
-53
lines changed

src/jdk.compiler/share/classes/com/sun/tools/javac/launcher/MemoryContext.java

Lines changed: 2 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -34,9 +34,6 @@
3434
import com.sun.tools.javac.util.Context;
3535
import com.sun.tools.javac.util.Context.Factory;
3636

37-
import javax.lang.model.element.ElementKind;
38-
import javax.lang.model.element.NestingKind;
39-
import javax.lang.model.element.TypeElement;
4037
import javax.tools.JavaFileManager;
4138
import javax.tools.JavaFileObject;
4239
import javax.tools.StandardLocation;
@@ -112,10 +109,9 @@ Set<String> getNamesOfCompiledClasses() {
112109
* Any messages generated during compilation will be written to the stream
113110
* provided when this object was created.
114111
*
115-
* @return the list of top-level types defined in the source file
116112
* @throws Fault if any compilation errors occur, or if no class was found
117113
*/
118-
List<String> compileProgram() throws Fault {
114+
void compileProgram() throws Fault {
119115
var units = new ArrayList<JavaFileObject>();
120116
units.add(descriptor.fileObject());
121117
if (descriptor.isModular()) {
@@ -126,35 +122,10 @@ List<String> compileProgram() throws Fault {
126122
var context = new Context();
127123
MemoryPreview.registerInstance(context);
128124
var task = compiler.getTask(out, memoryFileManager, null, opts, null, units, context);
129-
var fileUri = descriptor.fileObject().toUri();
130-
var names = new ArrayList<String>();
131-
task.addTaskListener(new TaskListener() {
132-
@Override
133-
public void started(TaskEvent event) {
134-
if (event.getKind() != TaskEvent.Kind.ANALYZE) return;
135-
TypeElement element = event.getTypeElement();
136-
if (element.getNestingKind() != NestingKind.TOP_LEVEL) return;
137-
JavaFileObject source = event.getSourceFile();
138-
if (source == null) return;
139-
if (!source.toUri().equals(fileUri)) return;
140-
ElementKind kind = element.getKind();
141-
if (kind != ElementKind.CLASS
142-
&& kind != ElementKind.ENUM
143-
&& kind != ElementKind.INTERFACE
144-
&& kind != ElementKind.RECORD)
145-
return;
146-
var name = element.getQualifiedName().toString();
147-
names.add(name);
148-
}
149-
});
150125
var ok = task.call();
151126
if (!ok) {
152127
throw new Fault(Errors.CompilationFailed);
153128
}
154-
if (names.isEmpty()) {
155-
throw new Fault(Errors.NoClass);
156-
}
157-
return List.copyOf(names);
158129
}
159130

160131
/**

src/jdk.compiler/share/classes/com/sun/tools/javac/launcher/ProgramDescriptor.java

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -25,6 +25,7 @@
2525

2626
package com.sun.tools.javac.launcher;
2727

28+
import com.sun.source.tree.ClassTree;
2829
import com.sun.tools.javac.api.JavacTool;
2930
import com.sun.tools.javac.resources.LauncherProperties.Errors;
3031

@@ -33,6 +34,7 @@
3334
import java.io.UncheckedIOException;
3435
import java.nio.file.Files;
3536
import java.nio.file.Path;
37+
import java.util.ArrayList;
3638
import java.util.List;
3739
import java.util.Optional;
3840
import java.util.Set;
@@ -46,30 +48,46 @@
4648
* risk. This code and its internal interfaces are subject to change
4749
* or deletion without notice.</strong></p>
4850
*/
49-
public record ProgramDescriptor(ProgramFileObject fileObject, Optional<String> packageName, Path sourceRootPath) {
51+
public record ProgramDescriptor(
52+
ProgramFileObject fileObject,
53+
Optional<String> packageName,
54+
List<String> qualifiedTypeNames,
55+
Path sourceRootPath) {
5056
static ProgramDescriptor of(ProgramFileObject fileObject) throws Fault {
5157
var file = fileObject.getFile();
58+
var packageName = ""; // empty string will be converted into an empty optional
59+
var packageNameAndDot = ""; // empty string or packageName + '.'
60+
var qualifiedTypeNames = new ArrayList<String>();
5261
try {
5362
var compiler = JavacTool.create();
5463
var standardFileManager = compiler.getStandardFileManager(null, null, null);
5564
var units = List.of(fileObject);
5665
var task = compiler.getTask(null, standardFileManager, diagnostic -> {}, null, null, units);
57-
for (var tree : task.parse()) {
58-
var packageTree = tree.getPackage();
59-
if (packageTree != null) {
60-
var packageName = packageTree.getPackageName().toString();
61-
var root = computeSourceRootPath(file, packageName);
62-
return new ProgramDescriptor(fileObject, Optional.of(packageName), root);
66+
var tree = task.parse().iterator().next(); // single compilation unit
67+
var packageTree = tree.getPackage();
68+
if (packageTree != null) {
69+
packageName = packageTree.getPackageName().toString();
70+
packageNameAndDot = packageName + '.';
71+
}
72+
for (var type : tree.getTypeDecls()) {
73+
if (type instanceof ClassTree classType) {
74+
qualifiedTypeNames.add(packageNameAndDot + classType.getSimpleName());
6375
}
6476
}
6577
} catch (IOException ignore) {
6678
// fall through to let actual compilation determine the error message
6779
}
68-
var root = computeSourceRootPath(file, "");
69-
return new ProgramDescriptor(fileObject, Optional.empty(), root);
80+
if (qualifiedTypeNames.isEmpty()) {
81+
throw new Fault(Errors.NoClass);
82+
}
83+
return new ProgramDescriptor(
84+
fileObject,
85+
packageName.isEmpty() ? Optional.empty() : Optional.of(packageName),
86+
List.copyOf(qualifiedTypeNames),
87+
computeSourceRootPath(file, packageName));
7088
}
7189

72-
public static Path computeSourceRootPath(Path program, String packageName) {
90+
public static Path computeSourceRootPath(Path program, String packageName) throws Fault {
7391
var absolute = program.normalize().toAbsolutePath();
7492
var absoluteRoot = absolute.getRoot();
7593
assert absoluteRoot != null;

src/jdk.compiler/share/classes/com/sun/tools/javac/launcher/SourceLauncher.java

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -147,10 +147,10 @@ public Result run(String[] runtimeArgs, String[] args) throws Fault, InvocationT
147147
ProgramDescriptor program = ProgramDescriptor.of(ProgramFileObject.of(file));
148148
RelevantJavacOptions options = RelevantJavacOptions.of(program, runtimeArgs);
149149
MemoryContext context = new MemoryContext(out, program, options);
150-
List<String> names = context.compileProgram();
150+
context.compileProgram();
151151

152152
String[] mainArgs = Arrays.copyOfRange(args, 1, args.length);
153-
var appClass = execute(names, mainArgs, context);
153+
var appClass = execute(context, mainArgs);
154154

155155
return new Result(appClass, context.getNamesOfCompiledClasses());
156156
}
@@ -184,20 +184,20 @@ private Path getFile(String[] args) throws Fault {
184184
* Invokes the {@code main} method of a program class, using a class loader that
185185
* will load recently compiled classes from memory.
186186
*
187-
* @param topLevelClassNames the names of classes in the program compilation unit
188187
* @param mainArgs the arguments for the {@code main} method
189188
* @param context the context for the class to be executed
190189
* @throws Fault if there is a problem finding or invoking the {@code main} method
191190
* @throws InvocationTargetException if the {@code main} method throws an exception
192191
*/
193-
private Class<?> execute(List<String> topLevelClassNames, String[] mainArgs, MemoryContext context)
192+
private Class<?> execute(MemoryContext context, String[] mainArgs)
194193
throws Fault, InvocationTargetException {
195194
System.setProperty("jdk.launcher.sourcefile", context.getSourceFileAsString());
196195
ClassLoader parentLoader = ClassLoader.getSystemClassLoader();
196+
ProgramDescriptor program = context.getProgramDescriptor();
197197

198198
// 1. Find a main method in the first class and if there is one - invoke it
199199
Class<?> firstClass;
200-
String firstClassName = topLevelClassNames.getFirst();
200+
String firstClassName = program.qualifiedTypeNames().getFirst();
201201
try {
202202
ClassLoader loader = context.newClassLoaderFor(parentLoader, firstClassName);
203203
firstClass = Class.forName(firstClassName, false, loader);
@@ -208,10 +208,14 @@ private Class<?> execute(List<String> topLevelClassNames, String[] mainArgs, Mem
208208
Method mainMethod = MethodFinder.findMainMethod(firstClass);
209209
if (mainMethod == null) {
210210
// 2. If the first class doesn't have a main method, look for a class with a matching name
211-
var compilationUnitName = context.getProgramDescriptor().fileObject().getFile().getFileName().toString();
211+
var compilationUnitName = program.fileObject().getFile().getFileName().toString();
212212
assert compilationUnitName.endsWith(".java");
213-
var expectedName = compilationUnitName.substring(0, compilationUnitName.length() - 5);
214-
var actualName = topLevelClassNames.stream()
213+
var expectedSimpleName = compilationUnitName.substring(0, compilationUnitName.length() - 5);
214+
var expectedPackageName = program.packageName().orElse("");
215+
var expectedName = expectedPackageName.isEmpty()
216+
? expectedSimpleName
217+
: expectedPackageName + '.' + expectedSimpleName;
218+
var actualName = program.qualifiedTypeNames().stream()
215219
.filter(name -> name.equals(expectedName))
216220
.findFirst()
217221
.orElseThrow(() -> new Fault(Errors.CantFindClass(expectedName)));

test/langtools/tools/javac/launcher/SourceLauncherTest.java

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323

2424
/*
2525
* @test
26-
* @bug 8192920 8204588 8246774 8248843 8268869 8235876
26+
* @bug 8192920 8204588 8246774 8248843 8268869 8235876 8328339
2727
* @summary Test source launcher
2828
* @library /tools/lib
2929
* @enablePreview
@@ -110,6 +110,27 @@ public void testHelloWorldInPackage(Path base) throws IOException {
110110
testSuccess(base.resolve("hello").resolve("World.java"), "Hello World! [1, 2, 3]\n");
111111
}
112112

113+
@Test
114+
public void testHelloWorldInPackageWithStaticImport(Path base) throws IOException {
115+
tb.writeJavaFiles(base,
116+
"""
117+
package hello;
118+
import static hello.Helper.*;
119+
import java.util.Arrays;
120+
class World {
121+
public static void main(String... args) {
122+
m(args);
123+
}
124+
}
125+
class Helper {
126+
static void m(String... args) {
127+
System.out.println("Hello World! " + Arrays.toString(args));
128+
}
129+
}
130+
""");
131+
testSuccess(base.resolve("hello").resolve("World.java"), "Hello World! [1, 2, 3]\n");
132+
}
133+
113134
@Test
114135
public void testHelloWorldWithAux(Path base) throws IOException {
115136
tb.writeJavaFiles(base,
@@ -300,7 +321,7 @@ public void testNoClass(Path base) throws IOException {
300321
public void testMismatchOfPathAndPackage(Path base) throws IOException {
301322
Files.createDirectories(base);
302323
Path file = base.resolve("MismatchOfPathAndPackage.java");
303-
Files.write(file, List.of("package p;"));
324+
Files.write(file, List.of("package p; class MismatchOfPathAndPackage {}"));
304325
testError(file, "", "error: end of path to source file does not match its package name p: " + file);
305326
}
306327

0 commit comments

Comments
 (0)