8353840: JNativeScan should not abort for missing classes

Reviewed-by: jvernee, liach
This commit is contained in:
Danish Nawab 2025-04-09 15:25:24 +00:00 committed by Chen Liang
parent 1f21da75eb
commit 5f2a604b63
5 changed files with 52 additions and 29 deletions

View File

@ -42,15 +42,17 @@ import java.util.zip.ZipFile;
class JNativeScanTask {
private final PrintWriter out;
private final PrintWriter err;
private final List<Path> classPaths;
private final List<Path> modulePaths;
private final List<String> cmdRootModules;
private final Runtime.Version version;
private final Action action;
public JNativeScanTask(PrintWriter out, List<Path> classPaths, List<Path> modulePaths,
public JNativeScanTask(PrintWriter out, PrintWriter err, List<Path> classPaths, List<Path> modulePaths,
List<String> cmdRootModules, Runtime.Version version, Action action) {
this.out = out;
this.err = err;
this.classPaths = classPaths;
this.modulePaths = modulePaths;
this.version = version;
@ -71,10 +73,13 @@ class JNativeScanTask {
toScan.add(new ClassFileSource.Module(m.reference()));
}
Set<String> errors = new LinkedHashSet<>();
Diagnostics diagnostics = (context, error) ->
errors.add("Error while processing method: " + context + ": " + error.getMessage());
SortedMap<ClassFileSource, SortedMap<ClassDesc, List<RestrictedUse>>> allRestrictedMethods;
try(ClassResolver classesToScan = ClassResolver.forClassFileSources(toScan, version);
ClassResolver systemClassResolver = ClassResolver.forSystemModules(version)) {
NativeMethodFinder finder = NativeMethodFinder.create(classesToScan, systemClassResolver);
NativeMethodFinder finder = NativeMethodFinder.create(diagnostics, classesToScan, systemClassResolver);
allRestrictedMethods = finder.findAll();
} catch (IOException e) {
throw new RuntimeException(e);
@ -82,7 +87,7 @@ class JNativeScanTask {
switch (action) {
case PRINT -> printNativeAccess(allRestrictedMethods);
case DUMP_ALL -> dumpAll(allRestrictedMethods);
case DUMP_ALL -> dumpAll(allRestrictedMethods, errors);
}
}
@ -156,7 +161,7 @@ class JNativeScanTask {
out.println(nativeAccess);
}
private void dumpAll(SortedMap<ClassFileSource, SortedMap<ClassDesc, List<RestrictedUse>>> allRestrictedMethods) {
private void dumpAll(SortedMap<ClassFileSource, SortedMap<ClassDesc, List<RestrictedUse>>> allRestrictedMethods, Set<String> errors) {
if (allRestrictedMethods.isEmpty()) {
out.println(" <no restricted methods>");
} else {
@ -177,6 +182,10 @@ class JNativeScanTask {
});
});
}
if (!errors.isEmpty()) {
err.println("Error(s) while processing classes:");
errors.forEach(error -> err.println(" " + error));
}
}
private static boolean isJarFile(Path path) throws JNativeScanFatalError {
@ -192,4 +201,8 @@ class JNativeScanTask {
String packagePrefix = desc.packageName().isEmpty() ? "" : desc.packageName() + ".";
return packagePrefix + desc.displayName();
}
interface Diagnostics {
void error(MethodRef context, JNativeScanFatalError error);
}
}

View File

@ -165,7 +165,7 @@ public class Main {
action = JNativeScanTask.Action.PRINT;
}
new JNativeScanTask(out, classPathJars, modulePaths, rootModules, version, action).run();
new JNativeScanTask(out, err, classPathJars, modulePaths, rootModules, version, action).run();
}
private static String[] expandArgFiles(String[] args) throws JNativeScanFatalError {

View File

@ -24,6 +24,7 @@
*/
package com.sun.tools.jnativescan;
import com.sun.tools.jnativescan.JNativeScanTask.Diagnostics;
import com.sun.tools.jnativescan.RestrictedUse.NativeMethodDecl;
import com.sun.tools.jnativescan.RestrictedUse.RestrictedMethodRefs;
@ -44,16 +45,19 @@ class NativeMethodFinder {
private static final String RESTRICTED_NAME = "Ljdk/internal/javac/Restricted+Annotation;";
private final Map<MethodRef, Boolean> cache = new HashMap<>();
private final Diagnostics diagnostics;
private final ClassResolver classesToScan;
private final ClassResolver systemClassResolver;
private NativeMethodFinder(ClassResolver classesToScan, ClassResolver systemClassResolver) {
private NativeMethodFinder(Diagnostics diagnostics, ClassResolver classesToScan, ClassResolver systemClassResolver) {
this.diagnostics = diagnostics;
this.classesToScan = classesToScan;
this.systemClassResolver = systemClassResolver;
}
public static NativeMethodFinder create(ClassResolver classesToScan, ClassResolver systemClassResolver) throws JNativeScanFatalError, IOException {
return new NativeMethodFinder(classesToScan, systemClassResolver);
public static NativeMethodFinder create(Diagnostics diagnostics, ClassResolver classesToScan,
ClassResolver systemClassResolver) throws JNativeScanFatalError, IOException {
return new NativeMethodFinder(diagnostics, classesToScan, systemClassResolver);
}
public SortedMap<ClassFileSource, SortedMap<ClassDesc, List<RestrictedUse>>> findAll() throws JNativeScanFatalError {
@ -68,23 +72,22 @@ class NativeMethodFinder {
} else {
SortedSet<MethodRef> perMethod = new TreeSet<>(Comparator.comparing(MethodRef::toString));
methodModel.code().ifPresent(code -> {
try {
code.forEach(e -> {
switch (e) {
case InvokeInstruction invoke -> {
MethodRef ref = MethodRef.ofInvokeInstruction(invoke);
try {
if (isRestrictedMethod(ref)) {
perMethod.add(ref);
}
} catch (JNativeScanFatalError ex) {
diagnostics.error(MethodRef.ofModel(methodModel), ex);
}
}
default -> {
}
}
});
} catch (JNativeScanFatalError e) {
throw new JNativeScanFatalError("Error while processing method: " +
MethodRef.ofModel(methodModel), e);
}
});
if (!perMethod.isEmpty()) {
perClass.add(new RestrictedMethodRefs(MethodRef.ofModel(methodModel), perMethod));

View File

@ -35,6 +35,9 @@ import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.nio.file.Path;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class TestMissingSystemClass extends JNativeScanTestBase {
@ -49,12 +52,15 @@ public class TestMissingSystemClass extends JNativeScanTestBase {
@Test
public void testSingleJarClassPath() {
assertFailure(jnativescan("--class-path", MISSING_SYSTEM.toString(), "--release", "21"))
.stdoutShouldBeEmpty()
List<String> stderr = assertSuccess(jnativescan("--class-path", MISSING_SYSTEM.toString(), "--release", "21"))
.stdoutShouldContain("<no restricted methods>")
.stderrShouldContain("Error(s) while processing classes")
.stderrShouldContain("Error while processing method")
.stderrShouldContain("missingsystem.App::main(String[])void")
.stderrShouldContain("CAUSED BY:")
.stderrShouldContain("System class can not be found")
.stderrShouldContain("java.lang.Compiler");
.stderrShouldContain("java.lang.Compiler")
.stderrAsLines();
assertEquals(2, stderr.size(), "Unexpected number of lines in stderr");
}
}

View File

@ -28,5 +28,6 @@ public class App {
// if we compile with --release 20, but run jnativescan
// with --release 21, we should get an error
java.lang.Compiler.enable();
java.lang.Compiler.enable(); // should be de-duplicated in the error logs
}
}