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 { class JNativeScanTask {
private final PrintWriter out; private final PrintWriter out;
private final PrintWriter err;
private final List<Path> classPaths; private final List<Path> classPaths;
private final List<Path> modulePaths; private final List<Path> modulePaths;
private final List<String> cmdRootModules; private final List<String> cmdRootModules;
private final Runtime.Version version; private final Runtime.Version version;
private final Action action; 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) { List<String> cmdRootModules, Runtime.Version version, Action action) {
this.out = out; this.out = out;
this.err = err;
this.classPaths = classPaths; this.classPaths = classPaths;
this.modulePaths = modulePaths; this.modulePaths = modulePaths;
this.version = version; this.version = version;
@ -71,10 +73,13 @@ class JNativeScanTask {
toScan.add(new ClassFileSource.Module(m.reference())); 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; SortedMap<ClassFileSource, SortedMap<ClassDesc, List<RestrictedUse>>> allRestrictedMethods;
try(ClassResolver classesToScan = ClassResolver.forClassFileSources(toScan, version); try(ClassResolver classesToScan = ClassResolver.forClassFileSources(toScan, version);
ClassResolver systemClassResolver = ClassResolver.forSystemModules(version)) { ClassResolver systemClassResolver = ClassResolver.forSystemModules(version)) {
NativeMethodFinder finder = NativeMethodFinder.create(classesToScan, systemClassResolver); NativeMethodFinder finder = NativeMethodFinder.create(diagnostics, classesToScan, systemClassResolver);
allRestrictedMethods = finder.findAll(); allRestrictedMethods = finder.findAll();
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
@ -82,7 +87,7 @@ class JNativeScanTask {
switch (action) { switch (action) {
case PRINT -> printNativeAccess(allRestrictedMethods); case PRINT -> printNativeAccess(allRestrictedMethods);
case DUMP_ALL -> dumpAll(allRestrictedMethods); case DUMP_ALL -> dumpAll(allRestrictedMethods, errors);
} }
} }
@ -156,7 +161,7 @@ class JNativeScanTask {
out.println(nativeAccess); 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()) { if (allRestrictedMethods.isEmpty()) {
out.println(" <no restricted methods>"); out.println(" <no restricted methods>");
} else { } 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 { private static boolean isJarFile(Path path) throws JNativeScanFatalError {
@ -192,4 +201,8 @@ class JNativeScanTask {
String packagePrefix = desc.packageName().isEmpty() ? "" : desc.packageName() + "."; String packagePrefix = desc.packageName().isEmpty() ? "" : desc.packageName() + ".";
return packagePrefix + desc.displayName(); 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; 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 { private static String[] expandArgFiles(String[] args) throws JNativeScanFatalError {

View File

@ -24,6 +24,7 @@
*/ */
package com.sun.tools.jnativescan; 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.NativeMethodDecl;
import com.sun.tools.jnativescan.RestrictedUse.RestrictedMethodRefs; 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 static final String RESTRICTED_NAME = "Ljdk/internal/javac/Restricted+Annotation;";
private final Map<MethodRef, Boolean> cache = new HashMap<>(); private final Map<MethodRef, Boolean> cache = new HashMap<>();
private final Diagnostics diagnostics;
private final ClassResolver classesToScan; private final ClassResolver classesToScan;
private final ClassResolver systemClassResolver; 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.classesToScan = classesToScan;
this.systemClassResolver = systemClassResolver; this.systemClassResolver = systemClassResolver;
} }
public static NativeMethodFinder create(ClassResolver classesToScan, ClassResolver systemClassResolver) throws JNativeScanFatalError, IOException { public static NativeMethodFinder create(Diagnostics diagnostics, ClassResolver classesToScan,
return new NativeMethodFinder(classesToScan, systemClassResolver); ClassResolver systemClassResolver) throws JNativeScanFatalError, IOException {
return new NativeMethodFinder(diagnostics, classesToScan, systemClassResolver);
} }
public SortedMap<ClassFileSource, SortedMap<ClassDesc, List<RestrictedUse>>> findAll() throws JNativeScanFatalError { public SortedMap<ClassFileSource, SortedMap<ClassDesc, List<RestrictedUse>>> findAll() throws JNativeScanFatalError {
@ -68,23 +72,22 @@ class NativeMethodFinder {
} else { } else {
SortedSet<MethodRef> perMethod = new TreeSet<>(Comparator.comparing(MethodRef::toString)); SortedSet<MethodRef> perMethod = new TreeSet<>(Comparator.comparing(MethodRef::toString));
methodModel.code().ifPresent(code -> { methodModel.code().ifPresent(code -> {
try {
code.forEach(e -> { code.forEach(e -> {
switch (e) { switch (e) {
case InvokeInstruction invoke -> { case InvokeInstruction invoke -> {
MethodRef ref = MethodRef.ofInvokeInstruction(invoke); MethodRef ref = MethodRef.ofInvokeInstruction(invoke);
try {
if (isRestrictedMethod(ref)) { if (isRestrictedMethod(ref)) {
perMethod.add(ref); perMethod.add(ref);
} }
} catch (JNativeScanFatalError ex) {
diagnostics.error(MethodRef.ofModel(methodModel), ex);
}
} }
default -> { default -> {
} }
} }
}); });
} catch (JNativeScanFatalError e) {
throw new JNativeScanFatalError("Error while processing method: " +
MethodRef.ofModel(methodModel), e);
}
}); });
if (!perMethod.isEmpty()) { if (!perMethod.isEmpty()) {
perClass.add(new RestrictedMethodRefs(MethodRef.ofModel(methodModel), perMethod)); 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.io.IOException;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class TestMissingSystemClass extends JNativeScanTestBase { public class TestMissingSystemClass extends JNativeScanTestBase {
@ -49,12 +52,15 @@ public class TestMissingSystemClass extends JNativeScanTestBase {
@Test @Test
public void testSingleJarClassPath() { public void testSingleJarClassPath() {
assertFailure(jnativescan("--class-path", MISSING_SYSTEM.toString(), "--release", "21")) List<String> stderr = assertSuccess(jnativescan("--class-path", MISSING_SYSTEM.toString(), "--release", "21"))
.stdoutShouldBeEmpty() .stdoutShouldContain("<no restricted methods>")
.stderrShouldContain("Error(s) while processing classes")
.stderrShouldContain("Error while processing method") .stderrShouldContain("Error while processing method")
.stderrShouldContain("missingsystem.App::main(String[])void") .stderrShouldContain("missingsystem.App::main(String[])void")
.stderrShouldContain("CAUSED BY:")
.stderrShouldContain("System class can not be found") .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 // if we compile with --release 20, but run jnativescan
// with --release 21, we should get an error // with --release 21, we should get an error
java.lang.Compiler.enable(); java.lang.Compiler.enable();
java.lang.Compiler.enable(); // should be de-duplicated in the error logs
} }
} }