From cec222e46065fc15db3f2eb241d3607d605ab580 Mon Sep 17 00:00:00 2001 From: Jorn Vernee Date: Mon, 8 Jul 2024 12:39:33 +0000 Subject: [PATCH] 8317611: Add a tool like jdeprscan to find usage of restricted methods Reviewed-by: alanb, ihse, mcimadamore, jlahoda, jwaters --- make/modules/jdk.jdeps/Launcher.gmk | 9 + .../javac/platform/JDKPlatformProvider.java | 11 +- .../share/classes/module-info.java | 8 +- .../classes/com/sun/tools/jdeprscan/Main.java | 4 +- .../tools/jnativescan/ClassFileSource.java | 124 +++++++++ .../sun/tools/jnativescan/ClassResolver.java | 163 +++++++++++ .../jnativescan/JNativeScanFatalError.java | 45 ++++ .../tools/jnativescan/JNativeScanTask.java | 195 ++++++++++++++ .../com/sun/tools/jnativescan/Main.java | 234 ++++++++++++++++ .../com/sun/tools/jnativescan/MethodRef.java | 48 ++++ .../tools/jnativescan/NativeMethodFinder.java | 141 ++++++++++ .../sun/tools/jnativescan/RestrictedUse.java | 32 +++ src/jdk.jdeps/share/classes/module-info.java | 23 +- src/jdk.jdeps/share/man/jnativescan.1 | 220 +++++++++++++++ test/jdk/tools/launcher/HelpFlagsTest.java | 1 + test/langtools/TEST.groups | 4 + .../jnativescan/JNativeScanTestBase.java | 86 ++++++ .../tools/jnativescan/TestArrayTypeRefs.java | 56 ++++ .../tools/jnativescan/TestJNativeScan.java | 252 ++++++++++++++++++ .../jnativescan/TestMissingSystemClass.java | 60 +++++ .../tools/jnativescan/TestSubclassRefs.java | 56 ++++ .../jnativescan/cases/classpath/app/App.java | 31 +++ .../cases/classpath/arrayref/App.java | 32 +++ .../jnativescan/cases/classpath/lib/Lib.java | 33 +++ .../cases/classpath/missingsystem/App.java | 32 +++ .../cases/classpath/singlejar/main/Main.java | 34 +++ .../cases/classpath/subclassref/App.java | 32 +++ .../unnamed_package/UnnamedPackage.java | 32 +++ .../cases/modules/org.lib/module-info.java | 26 ++ .../cases/modules/org.lib/org/lib/Lib.java | 34 +++ .../modules/org.lib/org/lib/Service.java | 26 ++ .../cases/modules/org.myapp/module-info.java | 28 ++ .../org.myapp/org/myapp/main/Main.java | 32 +++ .../modules/org.service/module-info.java | 27 ++ .../org.service/org/service/ServiceImpl.java | 35 +++ .../modules/org.singlejar/module-info.java | 25 ++ .../org/singlejar/main/Main.java | 34 +++ 37 files changed, 2250 insertions(+), 15 deletions(-) create mode 100644 src/jdk.jdeps/share/classes/com/sun/tools/jnativescan/ClassFileSource.java create mode 100644 src/jdk.jdeps/share/classes/com/sun/tools/jnativescan/ClassResolver.java create mode 100644 src/jdk.jdeps/share/classes/com/sun/tools/jnativescan/JNativeScanFatalError.java create mode 100644 src/jdk.jdeps/share/classes/com/sun/tools/jnativescan/JNativeScanTask.java create mode 100644 src/jdk.jdeps/share/classes/com/sun/tools/jnativescan/Main.java create mode 100644 src/jdk.jdeps/share/classes/com/sun/tools/jnativescan/MethodRef.java create mode 100644 src/jdk.jdeps/share/classes/com/sun/tools/jnativescan/NativeMethodFinder.java create mode 100644 src/jdk.jdeps/share/classes/com/sun/tools/jnativescan/RestrictedUse.java create mode 100644 src/jdk.jdeps/share/man/jnativescan.1 create mode 100644 test/langtools/tools/jnativescan/JNativeScanTestBase.java create mode 100644 test/langtools/tools/jnativescan/TestArrayTypeRefs.java create mode 100644 test/langtools/tools/jnativescan/TestJNativeScan.java create mode 100644 test/langtools/tools/jnativescan/TestMissingSystemClass.java create mode 100644 test/langtools/tools/jnativescan/TestSubclassRefs.java create mode 100644 test/langtools/tools/jnativescan/cases/classpath/app/App.java create mode 100644 test/langtools/tools/jnativescan/cases/classpath/arrayref/App.java create mode 100644 test/langtools/tools/jnativescan/cases/classpath/lib/Lib.java create mode 100644 test/langtools/tools/jnativescan/cases/classpath/missingsystem/App.java create mode 100644 test/langtools/tools/jnativescan/cases/classpath/singlejar/main/Main.java create mode 100644 test/langtools/tools/jnativescan/cases/classpath/subclassref/App.java create mode 100644 test/langtools/tools/jnativescan/cases/classpath/unnamed_package/UnnamedPackage.java create mode 100644 test/langtools/tools/jnativescan/cases/modules/org.lib/module-info.java create mode 100644 test/langtools/tools/jnativescan/cases/modules/org.lib/org/lib/Lib.java create mode 100644 test/langtools/tools/jnativescan/cases/modules/org.lib/org/lib/Service.java create mode 100644 test/langtools/tools/jnativescan/cases/modules/org.myapp/module-info.java create mode 100644 test/langtools/tools/jnativescan/cases/modules/org.myapp/org/myapp/main/Main.java create mode 100644 test/langtools/tools/jnativescan/cases/modules/org.service/module-info.java create mode 100644 test/langtools/tools/jnativescan/cases/modules/org.service/org/service/ServiceImpl.java create mode 100644 test/langtools/tools/jnativescan/cases/modules/org.singlejar/module-info.java create mode 100644 test/langtools/tools/jnativescan/cases/modules/org.singlejar/org/singlejar/main/Main.java diff --git a/make/modules/jdk.jdeps/Launcher.gmk b/make/modules/jdk.jdeps/Launcher.gmk index ceab7931245..1aa54e16f45 100644 --- a/make/modules/jdk.jdeps/Launcher.gmk +++ b/make/modules/jdk.jdeps/Launcher.gmk @@ -51,3 +51,12 @@ $(eval $(call SetupBuildLauncher, jdeprscan, \ MAIN_CLASS := com.sun.tools.jdeprscan.Main, \ CFLAGS := -DEXPAND_CLASSPATH_WILDCARDS, \ )) + +################################################################################ +## Build jnativescan +################################################################################ + +$(eval $(call SetupBuildLauncher, jnativescan, \ + MAIN_CLASS := com.sun.tools.jnativescan.Main, \ + CFLAGS := -DEXPAND_CLASSPATH_WILDCARDS, \ +)) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/platform/JDKPlatformProvider.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/platform/JDKPlatformProvider.java index 487fe969f97..4c24f9892a6 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/platform/JDKPlatformProvider.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/platform/JDKPlatformProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 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 @@ -83,7 +83,14 @@ public class JDKPlatformProvider implements PlatformProvider { } @Override - public PlatformDescription getPlatform(String platformName, String options) { + public PlatformDescription getPlatform(String platformName, String options) throws PlatformNotSupported { + if (!SUPPORTED_JAVA_PLATFORM_VERSIONS.contains(platformName)) { + throw new PlatformNotSupported(); + } + return getPlatformTrusted(platformName); + } + + public PlatformDescription getPlatformTrusted(String platformName) { return new PlatformDescriptionImpl(platformName); } diff --git a/src/jdk.internal.opt/share/classes/module-info.java b/src/jdk.internal.opt/share/classes/module-info.java index 67ed1410560..ba6987f1ea9 100644 --- a/src/jdk.internal.opt/share/classes/module-info.java +++ b/src/jdk.internal.opt/share/classes/module-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -31,11 +31,13 @@ module jdk.internal.opt { exports jdk.internal.joptsimple to jdk.jlink, - jdk.jshell; + jdk.jshell, + jdk.jdeps; exports jdk.internal.opt to jdk.compiler, jdk.jartool, jdk.javadoc, jdk.jlink, - jdk.jpackage; + jdk.jpackage, + jdk.jdeps; } diff --git a/src/jdk.jdeps/share/classes/com/sun/tools/jdeprscan/Main.java b/src/jdk.jdeps/share/classes/com/sun/tools/jdeprscan/Main.java index c67510de441..f77d29a8bfa 100644 --- a/src/jdk.jdeps/share/classes/com/sun/tools/jdeprscan/Main.java +++ b/src/jdk.jdeps/share/classes/com/sun/tools/jdeprscan/Main.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 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 @@ -414,7 +414,7 @@ public class Main implements DiagnosticListener { .noneMatch(n -> n.equals(release))) { return false; } - JavaFileManager fm = pp.getPlatform(release, "").getFileManager(); + JavaFileManager fm = pp.getPlatformTrusted(release).getFileManager(); List classNames = new ArrayList<>(); for (JavaFileObject fo : fm.list(StandardLocation.PLATFORM_CLASS_PATH, "", diff --git a/src/jdk.jdeps/share/classes/com/sun/tools/jnativescan/ClassFileSource.java b/src/jdk.jdeps/share/classes/com/sun/tools/jnativescan/ClassFileSource.java new file mode 100644 index 00000000000..754904c9c7f --- /dev/null +++ b/src/jdk.jdeps/share/classes/com/sun/tools/jnativescan/ClassFileSource.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ +package com.sun.tools.jnativescan; + +import java.io.IOException; +import java.io.InputStream; +import java.io.UncheckedIOException; +import java.lang.module.ModuleReader; +import java.lang.module.ModuleReference; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.jar.JarFile; +import java.util.stream.Stream; +import java.util.zip.ZipFile; + +sealed interface ClassFileSource { + String moduleName(); + Path path(); + + Stream classFiles(Runtime.Version version) throws IOException; + + record Module(ModuleReference reference) implements ClassFileSource { + @Override + public String moduleName() { + return reference.descriptor().name(); + } + + @Override + public Path path() { + URI location = reference.location().orElseThrow(); + return Path.of(location); + } + + @Override + public Stream classFiles(Runtime.Version version) throws IOException { + ModuleReader reader = reference().open(); + return reader.list() + .filter(resourceName -> resourceName.endsWith(".class")) + .map(resourceName -> { + try (InputStream stream = reader.open(resourceName).orElseThrow()) { + return stream.readAllBytes(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }).onClose(() -> { + try { + reader.close(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }); + } + } + + record ClassPathJar(Path path) implements ClassFileSource { + @Override + public String moduleName() { + return "ALL-UNNAMED"; + } + + @Override + public Stream classFiles(Runtime.Version version) throws IOException { + JarFile jf = new JarFile(path().toFile(), false, ZipFile.OPEN_READ, version); + return jf.versionedStream() + .filter(je -> je.getName().endsWith(".class")) + .map(je -> { + try (InputStream stream = jf.getInputStream(je)){ + return stream.readAllBytes(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }).onClose(() -> { + try { + jf.close(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }); + } + } + + record ClassPathDirectory(Path path) implements ClassFileSource { + @Override + public String moduleName() { + return "ALL-UNNAMED"; + } + + @Override + public Stream classFiles(Runtime.Version version) throws IOException { + return Files.walk(path) + .filter(file -> Files.isRegularFile(file) && file.toString().endsWith(".class")) + .map(file -> { + try (InputStream stream = Files.newInputStream(file)){ + return stream.readAllBytes(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }); + } + } +} diff --git a/src/jdk.jdeps/share/classes/com/sun/tools/jnativescan/ClassResolver.java b/src/jdk.jdeps/share/classes/com/sun/tools/jnativescan/ClassResolver.java new file mode 100644 index 00000000000..7a267a58aa5 --- /dev/null +++ b/src/jdk.jdeps/share/classes/com/sun/tools/jnativescan/ClassResolver.java @@ -0,0 +1,163 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ +package com.sun.tools.jnativescan; + +import com.sun.tools.javac.platform.PlatformDescription; +import com.sun.tools.javac.platform.PlatformProvider; + +import javax.tools.JavaFileManager; +import javax.tools.JavaFileObject; +import javax.tools.StandardLocation; +import java.io.IOException; +import java.lang.classfile.ClassFile; +import java.lang.classfile.ClassModel; +import java.lang.constant.ClassDesc; +import java.lang.module.ModuleDescriptor; +import java.util.*; +import java.util.function.BiConsumer; +import java.util.stream.Stream; + +abstract class ClassResolver implements AutoCloseable { + + static ClassResolver forClassFileSources(List sources, Runtime.Version version) throws IOException { + Map classMap = new HashMap<>(); + for (ClassFileSource source : sources) { + try (Stream classFiles = source.classFiles(version)) { + classFiles.forEach(bytes -> { + ClassModel model = ClassFile.of().parse(bytes); + ClassDesc desc = model.thisClass().asSymbol(); + classMap.put(desc, new Info(source, model)); + }); + } + } + return new SimpleClassResolver(classMap); + } + + static ClassResolver forSystemModules(Runtime.Version version) { + String platformName = String.valueOf(version.feature()); + PlatformProvider platformProvider = ServiceLoader.load(PlatformProvider.class).findFirst().orElseThrow(); + PlatformDescription platform; + try { + platform = platformProvider.getPlatform(platformName, null); + } catch (PlatformProvider.PlatformNotSupported e) { + throw new JNativeScanFatalError("Release: " + platformName + " not supported", e); + } + JavaFileManager fm = platform.getFileManager(); + return new SystemModuleClassResolver(fm); + } + + record Info(ClassFileSource source, ClassModel model) {} + + public abstract void forEach(BiConsumer action); + public abstract Optional lookup(ClassDesc desc); + + @Override + public abstract void close() throws IOException; + + private static class SimpleClassResolver extends ClassResolver { + + private final Map classMap; + + public SimpleClassResolver(Map classMap) { + this.classMap = classMap; + } + + public void forEach(BiConsumer action) { + classMap.forEach(action); + } + + public Optional lookup(ClassDesc desc) { + return Optional.ofNullable(classMap.get(desc)); + } + + @Override + public void close() {} + } + + private static class SystemModuleClassResolver extends ClassResolver { + + private final JavaFileManager platformFileManager; + private final Map packageToSystemModule; + private final Map cache = new HashMap<>(); + + public SystemModuleClassResolver(JavaFileManager platformFileManager) { + this.platformFileManager = platformFileManager; + this.packageToSystemModule = packageToSystemModule(platformFileManager); + } + + private static Map packageToSystemModule(JavaFileManager platformFileManager) { + try { + Set locations = platformFileManager.listLocationsForModules( + StandardLocation.SYSTEM_MODULES).iterator().next(); + + Map result = new HashMap<>(); + for (JavaFileManager.Location loc : locations) { + JavaFileObject jfo = platformFileManager.getJavaFileForInput(loc, "module-info", JavaFileObject.Kind.CLASS); + ModuleDescriptor descriptor = ModuleDescriptor.read(jfo.openInputStream()); + for (ModuleDescriptor.Exports export : descriptor.exports()) { + if (!export.isQualified()) { + result.put(export.source(), descriptor.name()); + } + } + } + return result; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public void forEach(BiConsumer action) { + throw new UnsupportedOperationException("NYI"); + } + + @Override + public Optional lookup(ClassDesc desc) { + return Optional.ofNullable(cache.computeIfAbsent(desc, _ -> { + String qualName = JNativeScanTask.qualName(desc); + String moduleName = packageToSystemModule.get(desc.packageName()); + if (moduleName != null) { + try { + JavaFileManager.Location loc = platformFileManager.getLocationForModule(StandardLocation.SYSTEM_MODULES, moduleName); + JavaFileObject jfo = platformFileManager.getJavaFileForInput(loc, qualName, JavaFileObject.Kind.CLASS); + if (jfo == null) { + throw new JNativeScanFatalError("System class can not be found: " + qualName); + } + ClassModel model = ClassFile.of().parse(jfo.openInputStream().readAllBytes()); + return new Info(null, model); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + return null; + })); + } + + @Override + public void close() throws IOException { + platformFileManager.close(); + } + } +} diff --git a/src/jdk.jdeps/share/classes/com/sun/tools/jnativescan/JNativeScanFatalError.java b/src/jdk.jdeps/share/classes/com/sun/tools/jnativescan/JNativeScanFatalError.java new file mode 100644 index 00000000000..15cf86e03c3 --- /dev/null +++ b/src/jdk.jdeps/share/classes/com/sun/tools/jnativescan/JNativeScanFatalError.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ +package com.sun.tools.jnativescan; + +import java.io.Serial; + +// Exception used in case of fatal error that is reasonably expected and handled. +public class JNativeScanFatalError extends RuntimeException { + @Serial + private static final long serialVersionUID = 1L; + + public JNativeScanFatalError(String message) { + super(message); + } + + public JNativeScanFatalError(String message, Throwable cause) { + super(message, cause); + } + + public JNativeScanFatalError(Throwable cause) { + super(cause); + } +} diff --git a/src/jdk.jdeps/share/classes/com/sun/tools/jnativescan/JNativeScanTask.java b/src/jdk.jdeps/share/classes/com/sun/tools/jnativescan/JNativeScanTask.java new file mode 100644 index 00000000000..2ff172e9c1b --- /dev/null +++ b/src/jdk.jdeps/share/classes/com/sun/tools/jnativescan/JNativeScanTask.java @@ -0,0 +1,195 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ +package com.sun.tools.jnativescan; + +import java.io.IOException; +import java.io.PrintWriter; +import java.lang.constant.ClassDesc; +import java.lang.module.Configuration; +import java.lang.module.ModuleFinder; +import java.lang.module.ResolvedModule; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; +import java.util.jar.JarFile; +import java.util.jar.Manifest; +import java.util.stream.Collectors; +import java.util.zip.ZipFile; + +class JNativeScanTask { + + private final PrintWriter out; + private final List classPaths; + private final List modulePaths; + private final List cmdRootModules; + private final Runtime.Version version; + private final Action action; + + public JNativeScanTask(PrintWriter out, List classPaths, List modulePaths, + List cmdRootModules, Runtime.Version version, Action action) { + this.out = out; + this.classPaths = classPaths; + this.modulePaths = modulePaths; + this.version = version; + this.action = action; + this.cmdRootModules = cmdRootModules; + } + + public void run() throws JNativeScanFatalError { + List toScan = new ArrayList<>(findAllClassPathJars()); + + ModuleFinder moduleFinder = ModuleFinder.of(modulePaths.toArray(Path[]::new)); + List rootModules = cmdRootModules; + if (rootModules.contains("ALL-MODULE-PATH")) { + rootModules = allModuleNames(moduleFinder); + } + Configuration config = systemConfiguration().resolveAndBind(ModuleFinder.of(), moduleFinder, rootModules); + for (ResolvedModule m : config.modules()) { + toScan.add(new ClassFileSource.Module(m.reference())); + } + + SortedMap>> allRestrictedMethods; + try(ClassResolver classesToScan = ClassResolver.forClassFileSources(toScan, version); + ClassResolver systemClassResolver = ClassResolver.forSystemModules(version)) { + NativeMethodFinder finder = NativeMethodFinder.create(classesToScan, systemClassResolver); + allRestrictedMethods = finder.findAll(); + } catch (IOException e) { + throw new RuntimeException(e); + } + + switch (action) { + case PRINT -> printNativeAccess(allRestrictedMethods); + case DUMP_ALL -> dumpAll(allRestrictedMethods); + } + } + + private List findAllClassPathJars() throws JNativeScanFatalError { + List result = new ArrayList<>(); + for (Path path : classPaths) { + if (isJarFile(path)) { + Deque jarsToScan = new ArrayDeque<>(); + jarsToScan.offer(path); + + // recursively look for all class path jars, starting at the root jars + // in this.classPaths, and recursively following all Class-Path manifest + // attributes + while (!jarsToScan.isEmpty()) { + Path jar = jarsToScan.poll(); + String[] classPathAttribute = classPathAttribute(jar); + Path parentDir = jar.getParent(); + for (String classPathEntry : classPathAttribute) { + Path otherJar = parentDir != null + ? parentDir.resolve(classPathEntry) + : Path.of(classPathEntry); + if (Files.exists(otherJar)) { + // Class-Path attribute specifies that jars that + // are not found are simply ignored. Do the same here + jarsToScan.offer(otherJar); + } + } + result.add(new ClassFileSource.ClassPathJar(jar)); + } + } else if (Files.isDirectory(path)) { + result.add(new ClassFileSource.ClassPathDirectory(path)); + } else { + throw new JNativeScanFatalError( + "Path does not appear to be a jar file, or directory containing classes: " + path); + } + } + return result; + } + + private String[] classPathAttribute(Path jar) { + try (JarFile jf = new JarFile(jar.toFile(), false, ZipFile.OPEN_READ, version)) { + Manifest manifest = jf.getManifest(); + if (manifest != null) { + String attrib = manifest.getMainAttributes().getValue("Class-Path"); + if (attrib != null) { + return attrib.split("\\s+"); + } + } + return new String[0]; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private Configuration systemConfiguration() { + ModuleFinder systemFinder = ModuleFinder.ofSystem(); + Configuration system = Configuration.resolve(systemFinder, List.of(Configuration.empty()), ModuleFinder.of(), + allModuleNames(systemFinder)); // resolve all of them + return system; + } + + private List allModuleNames(ModuleFinder finder) { + return finder.findAll().stream().map(mr -> mr.descriptor().name()).toList(); + } + + private void printNativeAccess(SortedMap>> allRestrictedMethods) { + String nativeAccess = allRestrictedMethods.keySet().stream() + .map(ClassFileSource::moduleName) + .distinct() + .collect(Collectors.joining(",")); + out.println(nativeAccess); + } + + private void dumpAll(SortedMap>> allRestrictedMethods) { + if (allRestrictedMethods.isEmpty()) { + out.println(" "); + } else { + allRestrictedMethods.forEach((module, perClass) -> { + out.println(module.path() + " (" + module.moduleName() + "):"); + perClass.forEach((classDesc, restrictedUses) -> { + out.println(" " + qualName(classDesc) + ":"); + restrictedUses.forEach(use -> { + switch (use) { + case RestrictedUse.NativeMethodDecl(MethodRef nmd) -> + out.println(" " + nmd + " is a native method declaration"); + case RestrictedUse.RestrictedMethodRefs(MethodRef referent, Set referees) -> { + out.println(" " + referent + " references restricted methods:"); + referees.forEach(referee -> out.println(" " + referee)); + } + } + }); + }); + }); + } + } + + private static boolean isJarFile(Path path) throws JNativeScanFatalError { + return Files.exists(path) && Files.isRegularFile(path) && path.toString().endsWith(".jar"); + } + + public enum Action { + DUMP_ALL, + PRINT + } + + public static String qualName(ClassDesc desc) { + String packagePrefix = desc.packageName().isEmpty() ? "" : desc.packageName() + "."; + return packagePrefix + desc.displayName(); + } +} diff --git a/src/jdk.jdeps/share/classes/com/sun/tools/jnativescan/Main.java b/src/jdk.jdeps/share/classes/com/sun/tools/jnativescan/Main.java new file mode 100644 index 00000000000..425a106d599 --- /dev/null +++ b/src/jdk.jdeps/share/classes/com/sun/tools/jnativescan/Main.java @@ -0,0 +1,234 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ +package com.sun.tools.jnativescan; + +import jdk.internal.joptsimple.*; +import jdk.internal.opt.CommandLine; + +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.nio.file.Path; +import java.util.List; +import java.util.Optional; +import java.util.function.Function; +import java.util.spi.ToolProvider; + +public class Main { + + private static boolean DEBUG = Boolean.getBoolean("com.sun.tools.jnativescan.DEBUG"); + + private static final int SUCCESS_CODE = 0; + private static final int FATAL_ERROR_CODE = 1; + + private final PrintWriter out; + private final PrintWriter err; + + private Main(PrintWriter out, PrintWriter err) { + this.out = out; + this.err = err; + } + + private void printError(String message) { + err.println("ERROR: " + message); + } + + private void printUsage() { + out.print(""" + Use 'jnativescan --help' for help + """); + } + + private void printVersion() { + out.println(System.getProperty("java.version")); + } + + public int run(String[] args) { + if (args.length < 1) { + printUsage(); + return FATAL_ERROR_CODE; + } + + try { + String[] expandedArgs = expandArgFiles(args); + parseOptionsAndRun(expandedArgs); + } catch (JNativeScanFatalError fatalError) { + printError(fatalError.getMessage()); + for (Throwable cause = fatalError.getCause(); + cause instanceof JNativeScanFatalError jNativeScanFatalError; + cause = jNativeScanFatalError.getCause()) { + err.println("CAUSED BY: " + jNativeScanFatalError.getMessage()); + } + if (DEBUG) { + fatalError.printStackTrace(err); + } + return FATAL_ERROR_CODE; + } catch (Throwable e) { + printError("Unexpected exception encountered"); + e.printStackTrace(err); + return FATAL_ERROR_CODE; + } + + return SUCCESS_CODE; + } + + private void parseOptionsAndRun(String[] expandedArgs) throws JNativeScanFatalError { + OptionParser parser = new OptionParser(false); + OptionSpec helpOpt = parser.acceptsAll(List.of("?", "h", "help"), "help").forHelp(); + OptionSpec versionOpt = parser.accepts("version", "Print version information and exit"); + OptionSpec classPathOpt = parser.accepts( + "class-path", + "The class path as used at runtime") + .withRequiredArg() + .withValuesSeparatedBy(File.pathSeparatorChar) + .withValuesConvertedBy(PARSE_PATH); + OptionSpec modulePathOpt = parser.accepts( + "module-path", + "The module path as used at runtime") + .withRequiredArg() + .withValuesSeparatedBy(File.pathSeparatorChar) + .withValuesConvertedBy(PARSE_PATH); + OptionSpec releaseOpt = parser.accepts( + "release", + "The runtime version that will run the application") + .withRequiredArg() + .withValuesConvertedBy(PARSE_VERSION); + OptionSpec addModulesOpt = parser.accepts( + "add-modules", + "List of root modules to scan") + .requiredIf(modulePathOpt) + .withRequiredArg() + .withValuesSeparatedBy(','); + OptionSpec printNativeAccessOpt = parser.accepts( + "print-native-access", + "print a comma separated list of modules that may perform native access operations." + + " ALL-UNNAMED is used to indicate unnamed modules."); + + OptionSet optionSet; + try { + optionSet = parser.parse(expandedArgs); + } catch (OptionException oe) { + throw new JNativeScanFatalError("Parsing options failed: " + oe.getMessage(), oe); + } + + if (optionSet.nonOptionArguments().size() != 0) { + throw new JNativeScanFatalError("jnativescan does not accept positional arguments"); + } + + if (optionSet.has(helpOpt)) { + out.println(""" + The jnativescan tool can be used to find methods that may access native functionality when + run. This includes restricted method calls and 'native' method declarations. + """); + try { + parser.printHelpOn(out); + return; + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + if (optionSet.has(versionOpt)) { + printVersion(); + return; + } + + List classPathJars = optionSet.valuesOf(classPathOpt); + List modulePaths = optionSet.valuesOf(modulePathOpt); + List rootModules = optionSet.valuesOf(addModulesOpt); + Runtime.Version version = Optional.ofNullable(optionSet.valueOf(releaseOpt)).orElse(Runtime.version()); + + JNativeScanTask.Action action = JNativeScanTask.Action.DUMP_ALL; + if (optionSet.has(printNativeAccessOpt)) { + action = JNativeScanTask.Action.PRINT; + } + + new JNativeScanTask(out, classPathJars, modulePaths, rootModules, version, action).run(); + } + + private static String[] expandArgFiles(String[] args) throws JNativeScanFatalError { + try { + return CommandLine.parse(args); + } catch (IOException e) { // file not found + throw new JNativeScanFatalError(e.getMessage(), e); + } + } + + public static void main(String[] args) { + System.exit(new Main.Provider().run(System.out, System.err, args)); + } + + public static class Provider implements ToolProvider { + + @Override + public String name() { + return "jnativescan"; + } + + @Override + public int run(PrintWriter out, PrintWriter err, String... args) { + return new Main(out, err).run(args); + } + } + + // where + private static final ValueConverter PARSE_PATH = new ValueConverter<>() { + @Override + public Path convert(String value) { + return Path.of(value); + } + + @Override + public Class valueType() { + return Path.class; + } + + @Override + public String valuePattern() { + return "Path"; + } + }; + + private static final ValueConverter PARSE_VERSION = new ValueConverter<>() { + @Override + public Runtime.Version convert(String value) { + try { + return Runtime.Version.parse(value); + } catch (IllegalArgumentException e) { + throw new JNativeScanFatalError("Invalid release: " + value + ": " + e.getMessage()); + } + } + + @Override + public Class valueType() { + return Runtime.Version.class; + } + + @Override + public String valuePattern() { + return "Version"; + } + }; +} diff --git a/src/jdk.jdeps/share/classes/com/sun/tools/jnativescan/MethodRef.java b/src/jdk.jdeps/share/classes/com/sun/tools/jnativescan/MethodRef.java new file mode 100644 index 00000000000..b19e4e7ec8f --- /dev/null +++ b/src/jdk.jdeps/share/classes/com/sun/tools/jnativescan/MethodRef.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ +package com.sun.tools.jnativescan; + +import java.lang.classfile.MethodModel; +import java.lang.classfile.constantpool.MemberRefEntry; +import java.lang.classfile.instruction.InvokeInstruction; +import java.lang.constant.ClassDesc; +import java.lang.constant.MethodTypeDesc; + +record MethodRef(ClassDesc owner, String name, MethodTypeDesc type) { + public static MethodRef ofModel(MethodModel model) { + return new MethodRef(model.parent().orElseThrow().thisClass().asSymbol(), + model.methodName().stringValue(), model.methodTypeSymbol()); + } + + public static MethodRef ofInvokeInstruction(InvokeInstruction instruction) { + return new MethodRef(instruction.owner().asSymbol(), + instruction.name().stringValue(), instruction.typeSymbol()); + } + + @Override + public String toString() { + return JNativeScanTask.qualName(owner) + "::" + name + type.displayDescriptor(); + } +} diff --git a/src/jdk.jdeps/share/classes/com/sun/tools/jnativescan/NativeMethodFinder.java b/src/jdk.jdeps/share/classes/com/sun/tools/jnativescan/NativeMethodFinder.java new file mode 100644 index 00000000000..681b954d4cd --- /dev/null +++ b/src/jdk.jdeps/share/classes/com/sun/tools/jnativescan/NativeMethodFinder.java @@ -0,0 +1,141 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ +package com.sun.tools.jnativescan; + +import com.sun.tools.jnativescan.RestrictedUse.NativeMethodDecl; +import com.sun.tools.jnativescan.RestrictedUse.RestrictedMethodRefs; + +import java.io.IOException; +import java.lang.classfile.Attributes; +import java.lang.classfile.ClassModel; +import java.lang.classfile.MethodModel; +import java.lang.classfile.instruction.InvokeInstruction; +import java.lang.constant.ClassDesc; +import java.lang.constant.MethodTypeDesc; +import java.lang.reflect.AccessFlag; +import java.util.*; + +class NativeMethodFinder { + + // ct.sym uses this fake name for the restricted annotation instead + // see make/langtools/src/classes/build/tools/symbolgenerator/CreateSymbols.java + private static final String RESTRICTED_NAME = "Ljdk/internal/javac/Restricted+Annotation;"; + + private final Map cache = new HashMap<>(); + private final ClassResolver classesToScan; + private final ClassResolver systemClassResolver; + + private NativeMethodFinder(ClassResolver classesToScan, ClassResolver systemClassResolver) { + this.classesToScan = classesToScan; + this.systemClassResolver = systemClassResolver; + } + + public static NativeMethodFinder create(ClassResolver classesToScan, ClassResolver systemClassResolver) throws JNativeScanFatalError, IOException { + return new NativeMethodFinder(classesToScan, systemClassResolver); + } + + public SortedMap>> findAll() throws JNativeScanFatalError { + SortedMap>> restrictedMethods + = new TreeMap<>(Comparator.comparing(ClassFileSource::path)); + classesToScan.forEach((_, info) -> { + ClassModel classModel = info.model(); + List perClass = new ArrayList<>(); + classModel.methods().forEach(methodModel -> { + if (methodModel.flags().has(AccessFlag.NATIVE)) { + perClass.add(new NativeMethodDecl(MethodRef.ofModel(methodModel))); + } else { + SortedSet 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); + if (isRestrictedMethod(ref)) { + perMethod.add(ref); + } + } + 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)); + } + } + }); + if (!perClass.isEmpty()) { + restrictedMethods.computeIfAbsent(info.source(), + _ -> new TreeMap<>(Comparator.comparing(JNativeScanTask::qualName))) + .put(classModel.thisClass().asSymbol(), perClass); + } + }); + return restrictedMethods; + } + + private boolean isRestrictedMethod(MethodRef ref) throws JNativeScanFatalError { + return cache.computeIfAbsent(ref, methodRef -> { + if (methodRef.owner().isArray()) { + // no restricted methods in arrays atm, and we can't look them up since they have no class file + return false; + } + Optional info = systemClassResolver.lookup(methodRef.owner()); + if (!info.isPresent()) { + return false; + } + ClassModel classModel = info.get().model(); + Optional methodModel = findMethod(classModel, methodRef.name(), methodRef.type()); + if (!methodModel.isPresent()) { + // If we are here, the method was referenced through a subclass of the class containing the actual + // method declaration. We could implement a method resolver (that needs to be version aware + // as well) to find the method model of the declaration, but it's not really worth it. + // None of the restricted methods (atm) are exposed through more than 1 public type, so it's not + // possible for user code to reference them through a subclass. + return false; + } + + return hasRestrictedAnnotation(methodModel.get()); + }); + } + + private static boolean hasRestrictedAnnotation(MethodModel method) { + return method.findAttribute(Attributes.runtimeVisibleAnnotations()) + .map(rva -> rva.annotations().stream().anyMatch(ann -> + ann.className().stringValue().equals(RESTRICTED_NAME))) + .orElse(false); + } + + private static Optional findMethod(ClassModel classModel, String name, MethodTypeDesc type) { + return classModel.methods().stream() + .filter(m -> m.methodName().stringValue().equals(name) + && m.methodType().stringValue().equals(type.descriptorString())) + .findFirst(); + } +} diff --git a/src/jdk.jdeps/share/classes/com/sun/tools/jnativescan/RestrictedUse.java b/src/jdk.jdeps/share/classes/com/sun/tools/jnativescan/RestrictedUse.java new file mode 100644 index 00000000000..58c5798d33f --- /dev/null +++ b/src/jdk.jdeps/share/classes/com/sun/tools/jnativescan/RestrictedUse.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ +package com.sun.tools.jnativescan; + +import java.util.SortedSet; + +sealed interface RestrictedUse { + record RestrictedMethodRefs(MethodRef referent, SortedSet referees) implements RestrictedUse {} + record NativeMethodDecl(MethodRef decl) implements RestrictedUse {} +} diff --git a/src/jdk.jdeps/share/classes/module-info.java b/src/jdk.jdeps/share/classes/module-info.java index e8ad319d70c..3fdd3ca32d2 100644 --- a/src/jdk.jdeps/share/classes/module-info.java +++ b/src/jdk.jdeps/share/classes/module-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -28,12 +28,13 @@ import jdk.internal.javac.ParticipatesInPreview; /** * Defines tools for analysing dependencies in Java libraries and programs, * including the {@index jdeps jdeps tool}, - * {@index javap javap tool}, and - * {@index jdeprscan jdeprscan tool} tools. + * {@index javap javap tool}, + * {@index jdeprscan jdeprscan tool}, and + * {@index jnativescan jnativescan tool} tools. * *

* This module provides the equivalent of command-line access to the - * javap and jdeps tools via the + * javap, jdeps, and jnativescan tools via the * {@link java.util.spi.ToolProvider ToolProvider} service provider * interface (SPI)

* @@ -49,12 +50,14 @@ import jdk.internal.javac.ParticipatesInPreview; * @toolGuide javap * @toolGuide jdeprscan * @toolGuide jdeps + * @toolGuide jnativescan * * @provides java.util.spi.ToolProvider - * Use {@link java.util.spi.ToolProvider#findFirst ToolProvider.findFirst("javap")} - * or {@link java.util.spi.ToolProvider#findFirst ToolProvider.findFirst("jdeps")} + * Use {@link java.util.spi.ToolProvider#findFirst ToolProvider.findFirst("javap")}, + * {@link java.util.spi.ToolProvider#findFirst ToolProvider.findFirst("jdeps")}, + * or {@link java.util.spi.ToolProvider#findFirst ToolProvider.findFirst("jnativescan")} * to obtain an instance of a {@code ToolProvider} that provides the equivalent - * of command-line access to the {@code javap} or {@code jdeps} tool. + * of command-line access to the {@code javap}, {@code jdeps}, {@code jnativescan} tool. * * @moduleGraph * @since 9 @@ -63,10 +66,14 @@ import jdk.internal.javac.ParticipatesInPreview; module jdk.jdeps { requires java.compiler; requires jdk.compiler; + requires jdk.internal.opt; + + uses com.sun.tools.javac.platform.PlatformProvider; exports com.sun.tools.classfile to jdk.jlink; provides java.util.spi.ToolProvider with com.sun.tools.javap.Main.JavapToolProvider, - com.sun.tools.jdeps.Main.JDepsToolProvider; + com.sun.tools.jdeps.Main.JDepsToolProvider, + com.sun.tools.jnativescan.Main.Provider; } diff --git a/src/jdk.jdeps/share/man/jnativescan.1 b/src/jdk.jdeps/share/man/jnativescan.1 new file mode 100644 index 00000000000..ff7f18277f2 --- /dev/null +++ b/src/jdk.jdeps/share/man/jnativescan.1 @@ -0,0 +1,220 @@ +.\" Copyright (c) 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 +.\" 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. +.\" +.\" Automatically generated by Pandoc 2.19.2 +.\" +.\" Define V font for inline verbatim, using C font in formats +.\" that render this, and otherwise B font. +.ie "\f[CB]x\f[R]"x" \{\ +. ftr V B +. ftr VI BI +. ftr VB B +. ftr VBI BI +.\} +.el \{\ +. ftr V CR +. ftr VI CI +. ftr VB CB +. ftr VBI CBI +.\} +.TH "JNATIVESCAN" "1" "2025" "JDK 24-ea" "JDK Commands" +.hy +.SH NAME +.PP +jnativescan - static analysis tool that scans one or more jar files for +uses of native functionalities, such as restricted method calls or +\f[V]native\f[R] method declarations. +.SH SYNOPSIS +.PP +\f[V]jnativescan\f[R] [\f[I]options\f[R]] +.TP +\f[I]options\f[R] +See \f[B]Options for the jnativescan Command\f[R] +.SH DESCRIPTION +.PP +The \f[V]jnative\f[R] tool is a static analysis tool provided by the JDK +that scans a JAR file for uses of native functionalities, such as +restricted method calls or \f[V]native\f[R] method declarations. +.PP +\f[V]jnativescan\f[R] accepts a runtime class path and module path +configuration, as well as a set of root modules, and a target release. +It scans the jars on the class and module paths, and reports uses of +native functionalities either in a tree like structure, which also +identifies that calling classes and methods, or as a list of module +names when the \f[V]--print-native-access\f[R] flag is specified. +.SH OPTIONS FOR THE JNATIVESCAN COMMAND +.PP +The following options are available: +.TP +\f[V]--class-path\f[R] \f[I]path\f[R] +Used to specify a list of paths pointing to jar files to be scanned. +.PP +All jar files specified through this list will be scanned. +If a jar file contains a \f[V]Class-Path\f[R] attribute in its manifest, +jar files listed there will be scanned as well. +Jar files listed in the \f[V]Class-Path\f[R] manifest attribute that can +not be found are ignored. +All the jar files found are treated as if they belonged to the unnamed +module. +.TP +\f[V]--module-path\f[R] \f[I]path\f[R] +Used to specify a list of paths pointing to jar files or directories +containing jar files, that the tool can use to find modules that need to +be scanned. +The list of jar files that will be scanned depends on the +\f[V]--add-modules\f[R] option. +.RS +.PP +For both the \f[V]--class-path\f[R] and \f[V]--module-path\f[R] options, +\f[I]path\f[R] should be a search path that consists of one or more jar +files, separated by the system-specific path separator. +For example: +.IP \[bu] 2 +\f[B]Linux and macOS:\f[R] +.RS 2 +.RS +.PP +\f[V]--class-path /some/foo.jar:/another/different/bar.jar\f[R] +.RE +.RE +.PP +\f[B]Note:\f[R] +.PP +On Windows, use a semicolon (\f[V];\f[R]) as the separator instead of a +colon (\f[V]:\f[R]). +.IP \[bu] 2 +\f[B]Windows:\f[R] +.RS 2 +.RS +.PP +\f[V]--class-path C:\[rs]some\[rs]foo.jar;C:\[rs]another\[rs]different\[rs]bar.jar\f[R] +.RE +.RE +.RE +.TP +\f[V]--add-modules\f[R] \f[I]module[,module...]\f[R] +Used to specify a comma-separated list of module names that indicate the +root modules to scan. +All the root modules will be scanned, as well as any modules that they +depend on. +This includes dependencies on service implementations specified through +the \f[V]uses\f[R] directive in a module\[aq]s \f[V]module-info\f[R] +file. +All modules found on the module path that provide an implementation of +such a service will be scanned as well. +.TP +\f[V]--release\f[R] \f[I]version\f[R] +Used to specify the Java SE release that specifies the set of restricted +methods to scan for. +For multi-release jar files, this option also indicates the version of +class file that should be loaded from the jar. +This option should be set to the version of the runtime under which the +application is eventually intended to be run. +If this flag is omitted, the version of \f[V]jnativescan\f[R] is used as +release version, which is the same as the version of the JDK that the +tool belongs to. +.TP +\f[V]--print-native-access\f[R] +Print a comma-separated list of module names that use native +functionalities, instead of the default tree structure. +.TP +\f[V]--help\f[R] or \f[V]-h\f[R] +Prints out a full help message. +.TP +\f[V]--version\f[R] +Prints out the abbreviated version string of the tool. +.SH EXAMPLE OF \f[V]jnativescan\f[R] USE +.PP +\f[V]jnativescan\f[R] accepts a runtime configuration in the form of a +class path, module path, set of root modules, and a target release +version. +For the class path, the tool will scan all jar files, including those +found recursively through the \f[V]Class-Path\f[R] manifest attribute. +For the module path, the tool scans all root modules specified through +\f[V]--add-modules\f[R], and any (transitive) dependence of the root +modules, including any modules that contain service implementations that +are used by a scanned module. +.PP +By default, the tool prints out which jars, classes, and methods use +native functionalities, in a tree-like structure. +The following is an example output: +.IP +.nf +\f[CB] +$ jnativescan --class-path app.jar +app.jar (ALL-UNNAMED): + foo.Main: + foo.Main::main(String[])void references restricted methods: + java.lang.foreign.MemorySegment::reinterpret(long)MemorySegment + foo.Main::nativeMethod()void is a native method declaration +\f[R] +.fi +.PP +\f[V]app.jar (ALL-UNNAMED)\f[R] is the path to the jar file, with the +module name in parentheses behind it. +Since in this case the jar file appears on the class path, +\f[V]ALL-UNNAMED\f[R] is printed to indicate the unnamed module. +The second line of the output, \f[V]foo.Main\f[R], indicates that +methods using native functionalities were found in the +\f[V]foo.Main\f[R] class. +The next line: +.IP +.nf +\f[CB] + foo.Main::main(String[])void references restricted methods: +\f[R] +.fi +.PP +Indicates that the \f[V]main(String[])\f[R] method in the +\f[V]foo.Main\f[R] class references a restricted method, which is listed +on the following line as: +.IP +.nf +\f[CB] + java.lang.foreign.MemorySegment::reinterpret(long)MemorySegment +\f[R] +.fi +.PP +Lastly, the text: +.IP +.nf +\f[CB] + foo.Main::nativeMethod()void is a native method declaration +\f[R] +.fi +.PP +Indicates that the \f[V]foo.Main\f[R] class contains a declaration of a +\f[V]native\f[R] method named \f[V]nativeMethod\f[R]. +.PP +If we add \f[V]--print-native-access\f[R] to the example command line, +we instead get a list of the names of modules that contain accesses to +native functionalities: +.IP +.nf +\f[CB] +$ jnativescan --class-path app.jar --print-native-access +ALL-UNNAMED +\f[R] +.fi +.PP +In this case the output consists of just \f[V]ALL-UNNAMED\f[R], which +indicates a jar file on the class path, that is, in the unnamed module, +contains an access to native functionalities. diff --git a/test/jdk/tools/launcher/HelpFlagsTest.java b/test/jdk/tools/launcher/HelpFlagsTest.java index dda649b9f41..15c6c101dd0 100644 --- a/test/jdk/tools/launcher/HelpFlagsTest.java +++ b/test/jdk/tools/launcher/HelpFlagsTest.java @@ -141,6 +141,7 @@ public class HelpFlagsTest extends TestHelper { new ToolHelpSpec("jlink", 1, 1, 1, 0, 0, 0, 2), // -?, -h, --help new ToolHelpSpec("jmap", 1, 1, 1, 0, 1, 0, 1), // -?, -h, --help, -help accepted but not documented. new ToolHelpSpec("jmod", 1, 1, 1, 0, 1, 0, 2), // -?, -h, --help, -help accepted but not documented. + new ToolHelpSpec("jnativescan", 1, 1, 1, 0, 1, 0, 1), // -?, -h, --help, -help accepted but not documented. new ToolHelpSpec("jps", 1, 1, 1, 0, 1, 1, 1), // -?, -h, --help -help, Documents -help new ToolHelpSpec("jrunscript", 1, 1, 1, 0, 1, 1, 7), // -?, -h, --help -help, Documents -help new ToolHelpSpec("jshell", 1, 1, 1, 0, 1, 0, 1), // -?, -h, --help, -help accepted but not documented. diff --git a/test/langtools/TEST.groups b/test/langtools/TEST.groups index 74503501da9..e290a1431a3 100644 --- a/test/langtools/TEST.groups +++ b/test/langtools/TEST.groups @@ -63,6 +63,10 @@ langtools_jdeps = \ tools/all \ tools/jdeps +langtools_jnativescan = \ + tools/all \ + tools/jnativescan + langtools_slow = \ jdk/internal/shellsupport/doc/FullJavadocHelperTest.java diff --git a/test/langtools/tools/jnativescan/JNativeScanTestBase.java b/test/langtools/tools/jnativescan/JNativeScanTestBase.java new file mode 100644 index 00000000000..97c0b21a738 --- /dev/null +++ b/test/langtools/tools/jnativescan/JNativeScanTestBase.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 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 + * 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. + */ + +import java.io.IOException; +import java.io.PrintWriter; +import java.nio.file.Path; +import java.util.Arrays; + +import java.io.StringWriter; +import java.util.spi.ToolProvider; + +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.util.JarUtils; + +public class JNativeScanTestBase { + + public static final String MODULE_PATH = "mods"; + + private static final ToolProvider JNATIVESCAN_TOOL = ToolProvider.findFirst("jnativescan") + .orElseThrow(() -> new RuntimeException("jnativescan tool not found")); + + public static OutputAnalyzer jnativescan(String... args) { + return run(JNATIVESCAN_TOOL, args); + } + + private static OutputAnalyzer run(ToolProvider tp, String[] commands) { + int rc; + StringWriter sw = new StringWriter(); + StringWriter esw = new StringWriter(); + + try (PrintWriter pw = new PrintWriter(sw); + PrintWriter epw = new PrintWriter(esw)) { + System.out.println("Running " + tp.name() + ", Command: " + Arrays.toString(commands)); + rc = tp.run(pw, epw, commands); + } + OutputAnalyzer output = new OutputAnalyzer(sw.toString(), esw.toString(), rc); + output.outputTo(System.out); + output.errorTo(System.err); + return output; + } + + public static Path makeModularJar(String moduleName) throws IOException { + Path jarPath = Path.of(MODULE_PATH, moduleName + ".jar"); + Path moduleRoot = moduleRoot(moduleName); + JarUtils.createJarFile(jarPath, moduleRoot); + return jarPath; + } + + public static Path moduleRoot(String name) { + return Path.of(System.getProperty("test.module.path")).resolve(name); + } + + public static OutputAnalyzer assertSuccess(OutputAnalyzer output) { + if (output.getExitValue() != 0) { + throw new IllegalStateException("tool run failed"); + } + return output; + } + + public static OutputAnalyzer assertFailure(OutputAnalyzer output) { + if (output.getExitValue() == 0) { + throw new IllegalStateException("tool run succeeded"); + } + return output; + } +} diff --git a/test/langtools/tools/jnativescan/TestArrayTypeRefs.java b/test/langtools/tools/jnativescan/TestArrayTypeRefs.java new file mode 100644 index 00000000000..a439fdfdef9 --- /dev/null +++ b/test/langtools/tools/jnativescan/TestArrayTypeRefs.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 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 + * 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 + * @library /test/lib .. ./cases/modules + * @build JNativeScanTestBase + * cases.classpath.arrayref.App + * @run junit TestArrayTypeRefs + */ + +import jdk.test.lib.util.JarUtils; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.nio.file.Path; + +public class TestArrayTypeRefs extends JNativeScanTestBase { + + static Path ARRAY_REF; + + @BeforeAll + public static void before() throws IOException { + ARRAY_REF = Path.of("arrayref.jar"); + Path testClasses = Path.of(System.getProperty("test.classes", "")); + JarUtils.createJarFile(ARRAY_REF, testClasses, Path.of("arrayref", "App.class")); + } + + @Test + public void testSingleJarClassPath() { + assertSuccess(jnativescan("--class-path", ARRAY_REF.toString())) + .stderrShouldBeEmpty() + .stdoutShouldContain(""); + } +} diff --git a/test/langtools/tools/jnativescan/TestJNativeScan.java b/test/langtools/tools/jnativescan/TestJNativeScan.java new file mode 100644 index 00000000000..94db4924412 --- /dev/null +++ b/test/langtools/tools/jnativescan/TestJNativeScan.java @@ -0,0 +1,252 @@ +/* + * Copyright (c) 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 + * 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 + * @library /test/lib .. ./cases/modules + * @build JNativeScanTestBase + * org.singlejar/* org.lib/* org.myapp/* org.service/* + * cases.classpath.singlejar.main.Main + * cases.classpath.lib.Lib + * cases.classpath.app.App + * cases.classpath.unnamed_package.UnnamedPackage + * @run junit TestJNativeScan + */ + +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.util.JarUtils; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; +import java.util.HashSet; +import java.util.Set; +import java.util.jar.Attributes; +import java.util.jar.Manifest; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +class TestJNativeScan extends JNativeScanTestBase { + + static Path TEST_CLASSES; + + static Path CLASS_PATH_APP; + static Path SINGLE_JAR_CLASS_PATH; + static Path SINGLE_JAR_MODULAR; + static Path ORG_MYAPP; + static Path ORG_LIB; + static Path UNNAMED_PACKAGE_JAR; + static Path LIB_JAR; + + @BeforeAll + public static void before() throws IOException { + SINGLE_JAR_CLASS_PATH = Path.of("singleJar.jar"); + TEST_CLASSES = Path.of(System.getProperty("test.classes", "")); + JarUtils.createJarFile(SINGLE_JAR_CLASS_PATH, TEST_CLASSES, Path.of("main", "Main.class")); + + LIB_JAR = Path.of("lib.jar"); + JarUtils.createJarFile(LIB_JAR, TEST_CLASSES, Path.of("lib", "Lib.class")); + Manifest manifest = new Manifest(); + Attributes mainAttrs = manifest.getMainAttributes(); + mainAttrs.put(Attributes.Name.MANIFEST_VERSION, "1.0"); // need version or other attributes will be ignored + mainAttrs.putValue("Class-Path", "lib.jar non-existent.jar"); + CLASS_PATH_APP = Path.of("app.jar"); + JarUtils.createJarFile(CLASS_PATH_APP, manifest, TEST_CLASSES, Path.of("app", "App.class")); + + SINGLE_JAR_MODULAR = makeModularJar("org.singlejar"); + ORG_MYAPP = makeModularJar("org.myapp"); + ORG_LIB = makeModularJar("org.lib"); + makeModularJar("org.service"); + + UNNAMED_PACKAGE_JAR = Path.of("unnamed_package.jar"); + JarUtils.createJarFile(UNNAMED_PACKAGE_JAR, TEST_CLASSES, Path.of("UnnamedPackage.class")); + } + + @Test + public void testSingleJarClassPath() { + assertSuccess(jnativescan("--class-path", SINGLE_JAR_CLASS_PATH.toString())) + .stderrShouldBeEmpty() + .stdoutShouldContain("ALL-UNNAMED") + .stdoutShouldContain("main.Main") + .stdoutShouldContain("main.Main::m()void is a native method declaration") + .stdoutShouldContain("main.Main::main(String[])void references restricted methods") + .stdoutShouldContain("java.lang.foreign.MemorySegment::reinterpret(long)MemorySegment"); + } + + @Test + public void testSingleJarModulePath() { + assertSuccess(jnativescan("--module-path", MODULE_PATH, "--add-modules", "org.singlejar")) + .stderrShouldBeEmpty() + .stdoutShouldContain("org.singlejar") + .stdoutShouldContain("org.singlejar.main.Main") + .stdoutShouldContain("org.singlejar.main.Main::m()void is a native method declaration") + .stdoutShouldContain("org.singlejar.main.Main::main(String[])void references restricted methods") + .stdoutShouldContain("java.lang.foreign.MemorySegment::reinterpret(long)MemorySegment"); + } + + @Test + public void testWithDepModule() { + assertSuccess(jnativescan("--module-path", MODULE_PATH, "--add-modules", "org.myapp")) + .stderrShouldBeEmpty() + .stdoutShouldContain("org.lib") + .stdoutShouldContain("org.lib.Lib") + .stdoutShouldContain("org.lib.Lib::m()void is a native method declaration") + .stdoutShouldContain("org.lib.Lib::doIt()void references restricted methods") + .stdoutShouldContain("java.lang.foreign.MemorySegment::reinterpret(long)MemorySegment") + .stdoutShouldContain("org.service") + .stdoutShouldContain("org.service.ServiceImpl") + .stdoutShouldContain("org.service.ServiceImpl::m()void is a native method declaration") + .stdoutShouldContain("org.service.ServiceImpl::doIt()void references restricted methods") + .stdoutShouldContain("java.lang.foreign.MemorySegment::reinterpret(long)MemorySegment"); + } + + @Test + public void testAllModulePath() { + assertSuccess(jnativescan("--module-path", MODULE_PATH, "--add-modules", "ALL-MODULE-PATH")) + .stderrShouldBeEmpty() + .stdoutShouldContain("org.singlejar") + .stdoutShouldContain("org.lib") + .stdoutShouldContain("org.service"); + } + + @Test + public void testClassPathAttribute() { + assertSuccess(jnativescan("--class-path", CLASS_PATH_APP.toString())) + .stderrShouldBeEmpty() + .stdoutShouldContain("ALL-UNNAMED") + .stdoutShouldContain("lib.Lib") + .stdoutShouldContain("lib.Lib::m()void is a native method declaration") + .stdoutShouldContain("lib.Lib::doIt()void references restricted methods") + .stdoutShouldContain("java.lang.foreign.MemorySegment::reinterpret(long)MemorySegment"); + } + + @Test + public void testInvalidRelease() { + assertFailure(jnativescan("--module-path", MODULE_PATH, "--add-modules", "ALL-MODULE-PATH", "--release", "asdf")) + .stderrShouldContain("Invalid release"); + } + + @Test + public void testReleaseNotSupported() { + assertFailure(jnativescan("--module-path", MODULE_PATH, "--add-modules", "ALL-MODULE-PATH", "--release", "9999999")) + .stderrShouldContain("Release: 9999999 not supported"); + } + + @Test + public void testFileDoesNotExist() { + assertFailure(jnativescan("--class-path", "non-existent.jar")) + .stderrShouldContain("Path does not appear to be a jar file, or directory containing classes"); + } + + @Test + public void testModuleNotAJarFile() { + String modulePath = moduleRoot("org.myapp").toString() + File.pathSeparator + ORG_LIB.toString(); + assertSuccess(jnativescan("--module-path", modulePath, + "--add-modules", "ALL-MODULE-PATH")) + .stdoutShouldContain("lib.Lib") + .stdoutShouldContain("lib.Lib::m()void is a native method declaration") + .stdoutShouldContain("lib.Lib::doIt()void references restricted methods") + .stdoutShouldContain("java.lang.foreign.MemorySegment::reinterpret(long)MemorySegment"); + } + + @Test + public void testPrintNativeAccess() { + assertSuccess(jnativescan("--module-path", MODULE_PATH, + "-add-modules", "org.singlejar,org.myapp", + "--print-native-access")) + .stdoutShouldMatch("org.lib,org.service,org.singlejar"); + } + + @Test + public void testNoDuplicateNames() { + String classPath = SINGLE_JAR_CLASS_PATH + File.pathSeparator + CLASS_PATH_APP; + OutputAnalyzer output = assertSuccess(jnativescan("--class-path", classPath, "--print-native-access")); + String[] moduleNames = output.getStdout().split(","); + Set names = new HashSet<>(); + for (String name : moduleNames) { + assertTrue(names.add(name.strip())); + } + } + + @Test + public void testUnnamedPackage() { + assertSuccess(jnativescan("--class-path", UNNAMED_PACKAGE_JAR.toString())) + .stderrShouldBeEmpty() + .stdoutShouldContain("ALL-UNNAMED") + .stdoutShouldNotContain(".UnnamedPackage") + .stdoutShouldContain("UnnamedPackage") + .stdoutShouldContain("UnnamedPackage::m()void is a native method declaration") + .stdoutShouldContain("UnnamedPackage::main(String[])void references restricted methods") + .stdoutShouldContain("java.lang.foreign.MemorySegment::reinterpret(long)MemorySegment"); + } + + @Test + public void testPositionalArguments() { + assertFailure(jnativescan("foo")) + .stdoutShouldBeEmpty() + .stderrShouldContain("jnativescan does not accept positional arguments"); + } + + @Test + public void testMissingRootModules() { + assertFailure(jnativescan("--module-path", MODULE_PATH)) + .stdoutShouldBeEmpty() + .stderrShouldContain("Missing required option(s) [add-modules]"); + } + + @Test + public void testClassPathDirectory() { + assertSuccess(jnativescan("--class-path", TEST_CLASSES.toString())) + .stderrShouldBeEmpty() + .stdoutShouldContain("ALL-UNNAMED") + .stdoutShouldContain("UnnamedPackage") + .stdoutShouldContain("UnnamedPackage::m()void is a native method declaration") + .stdoutShouldContain("UnnamedPackage::main(String[])void references restricted methods") + .stdoutShouldContain("main.Main") + .stdoutShouldContain("main.Main::m()void is a native method declaration") + .stdoutShouldContain("main.Main::main(String[])void references restricted methods") + .stdoutShouldContain("lib.Lib") + .stdoutShouldContain("lib.Lib::m()void is a native method declaration") + .stdoutShouldContain("lib.Lib::doIt()void references restricted methods") + .stdoutShouldContain("java.lang.foreign.MemorySegment::reinterpret(long)MemorySegment"); + } + + @Test + public void testMultipleClassPathJars() { + // make sure all of these are reported, even when they are all in the ALL-UNNAMED module + String classPath = UNNAMED_PACKAGE_JAR + + File.pathSeparator + SINGLE_JAR_CLASS_PATH + + File.pathSeparator + LIB_JAR; + assertSuccess(jnativescan("--class-path", classPath)) + .stderrShouldBeEmpty() + .stdoutShouldContain("ALL-UNNAMED") + .stdoutShouldContain("UnnamedPackage") + .stdoutShouldContain(UNNAMED_PACKAGE_JAR.toString()) + .stdoutShouldContain("lib.Lib") + .stdoutShouldContain(LIB_JAR.toString()) + .stdoutShouldContain("main.Main") + .stdoutShouldContain(SINGLE_JAR_CLASS_PATH.toString()); + } +} diff --git a/test/langtools/tools/jnativescan/TestMissingSystemClass.java b/test/langtools/tools/jnativescan/TestMissingSystemClass.java new file mode 100644 index 00000000000..5806590d0e0 --- /dev/null +++ b/test/langtools/tools/jnativescan/TestMissingSystemClass.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 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 + * 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 + * @library /test/lib .. ./cases/modules + * @build JNativeScanTestBase + * @compile --release 20 cases/classpath/missingsystem/App.java + * @run junit TestMissingSystemClass + */ + +import jdk.test.lib.util.JarUtils; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.nio.file.Path; + +public class TestMissingSystemClass extends JNativeScanTestBase { + + static Path MISSING_SYSTEM; + + @BeforeAll + public static void before() throws IOException { + MISSING_SYSTEM = Path.of("missingsystem.jar"); + Path testClasses = Path.of(System.getProperty("test.classes", "")); + JarUtils.createJarFile(MISSING_SYSTEM, testClasses, Path.of("missingsystem", "App.class")); + } + + @Test + public void testSingleJarClassPath() { + assertFailure(jnativescan("--class-path", MISSING_SYSTEM.toString(), "--release", "21")) + .stdoutShouldBeEmpty() + .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"); + } +} diff --git a/test/langtools/tools/jnativescan/TestSubclassRefs.java b/test/langtools/tools/jnativescan/TestSubclassRefs.java new file mode 100644 index 00000000000..c8eed0439ee --- /dev/null +++ b/test/langtools/tools/jnativescan/TestSubclassRefs.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 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 + * 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 + * @library /test/lib .. ./cases/modules + * @build JNativeScanTestBase + * cases.classpath.subclassref.App + * @run junit TestSubclassRefs + */ + +import jdk.test.lib.util.JarUtils; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.nio.file.Path; + +public class TestSubclassRefs extends JNativeScanTestBase { + + static Path SUBCLASS_REF; + + @BeforeAll + public static void before() throws IOException { + SUBCLASS_REF = Path.of("subclassref.jar"); + Path testClasses = Path.of(System.getProperty("test.classes", "")); + JarUtils.createJarFile(SUBCLASS_REF, testClasses, Path.of("subclassref", "App.class")); + } + + @Test + public void testSingleJarClassPath() { + assertSuccess(jnativescan("--class-path", SUBCLASS_REF.toString())) + .stderrShouldBeEmpty() + .stdoutShouldContain(""); + } +} diff --git a/test/langtools/tools/jnativescan/cases/classpath/app/App.java b/test/langtools/tools/jnativescan/cases/classpath/app/App.java new file mode 100644 index 00000000000..f96ab11e576 --- /dev/null +++ b/test/langtools/tools/jnativescan/cases/classpath/app/App.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 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 + * 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. + */ +package app; + +import lib.Lib; + +public class App { + public static void main(String[] args) { + Lib.doIt(); + } +} diff --git a/test/langtools/tools/jnativescan/cases/classpath/arrayref/App.java b/test/langtools/tools/jnativescan/cases/classpath/arrayref/App.java new file mode 100644 index 00000000000..aa480f392fb --- /dev/null +++ b/test/langtools/tools/jnativescan/cases/classpath/arrayref/App.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 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 + * 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. + */ +package arrayref; + +public class App { + public static void main(String[] args) { + // reference an array method to see that + // RestrictedMethodFinder correctly handles + // references to array methods + args.clone(); + } +} diff --git a/test/langtools/tools/jnativescan/cases/classpath/lib/Lib.java b/test/langtools/tools/jnativescan/cases/classpath/lib/Lib.java new file mode 100644 index 00000000000..ec92696364c --- /dev/null +++ b/test/langtools/tools/jnativescan/cases/classpath/lib/Lib.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 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 + * 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. + */ +package lib; + +import java.lang.foreign.MemorySegment; + +public class Lib { + public static void doIt() { + MemorySegment.ofAddress(1234).reinterpret(10); + } + + private static native void m(); +} diff --git a/test/langtools/tools/jnativescan/cases/classpath/missingsystem/App.java b/test/langtools/tools/jnativescan/cases/classpath/missingsystem/App.java new file mode 100644 index 00000000000..0e20fe81eec --- /dev/null +++ b/test/langtools/tools/jnativescan/cases/classpath/missingsystem/App.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 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 + * 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. + */ +package missingsystem; + +public class App { + public static void main(String[] args) { + // this class was present in Java 20, but removed in 21 + // if we compile with --release 20, but run jnativescan + // with --release 21, we should get an error + java.lang.Compiler.enable(); + } +} diff --git a/test/langtools/tools/jnativescan/cases/classpath/singlejar/main/Main.java b/test/langtools/tools/jnativescan/cases/classpath/singlejar/main/Main.java new file mode 100644 index 00000000000..280e8646f9f --- /dev/null +++ b/test/langtools/tools/jnativescan/cases/classpath/singlejar/main/Main.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 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 + * 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. + */ + +package main; + +import java.lang.foreign.*; + +public class Main { + public static void main(String[] args) { + MemorySegment.ofAddress(1234).reinterpret(10); + } + + private static native void m(); +} diff --git a/test/langtools/tools/jnativescan/cases/classpath/subclassref/App.java b/test/langtools/tools/jnativescan/cases/classpath/subclassref/App.java new file mode 100644 index 00000000000..abe9e41265e --- /dev/null +++ b/test/langtools/tools/jnativescan/cases/classpath/subclassref/App.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 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 + * 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. + */ +package subclassref; + +import java.util.List; + +public class App { + public static void main(String[] args) { + List l = List.of(args); + l.stream(); // List does not declare stream() + } +} diff --git a/test/langtools/tools/jnativescan/cases/classpath/unnamed_package/UnnamedPackage.java b/test/langtools/tools/jnativescan/cases/classpath/unnamed_package/UnnamedPackage.java new file mode 100644 index 00000000000..4e8dfe69b5a --- /dev/null +++ b/test/langtools/tools/jnativescan/cases/classpath/unnamed_package/UnnamedPackage.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 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 + * 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. + */ + +import java.lang.foreign.*; + +public class UnnamedPackage { + public static void main(String[] args) { + MemorySegment.ofAddress(1234).reinterpret(10); + } + + private static native void m(); +} diff --git a/test/langtools/tools/jnativescan/cases/modules/org.lib/module-info.java b/test/langtools/tools/jnativescan/cases/modules/org.lib/module-info.java new file mode 100644 index 00000000000..8572ed80e43 --- /dev/null +++ b/test/langtools/tools/jnativescan/cases/modules/org.lib/module-info.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 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 + * 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. + */ + +module org.lib { + exports org.lib; +} diff --git a/test/langtools/tools/jnativescan/cases/modules/org.lib/org/lib/Lib.java b/test/langtools/tools/jnativescan/cases/modules/org.lib/org/lib/Lib.java new file mode 100644 index 00000000000..3f9ea0e1ada --- /dev/null +++ b/test/langtools/tools/jnativescan/cases/modules/org.lib/org/lib/Lib.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 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 + * 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. + */ + +package org.lib; + +import java.lang.foreign.*; + +public class Lib { + public static void doIt() { + MemorySegment.ofAddress(1234).reinterpret(10); + } + + private static native void m(); +} diff --git a/test/langtools/tools/jnativescan/cases/modules/org.lib/org/lib/Service.java b/test/langtools/tools/jnativescan/cases/modules/org.lib/org/lib/Service.java new file mode 100644 index 00000000000..2e406d926a6 --- /dev/null +++ b/test/langtools/tools/jnativescan/cases/modules/org.lib/org/lib/Service.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 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 + * 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. + */ +package org.lib; + +public interface Service { +} diff --git a/test/langtools/tools/jnativescan/cases/modules/org.myapp/module-info.java b/test/langtools/tools/jnativescan/cases/modules/org.myapp/module-info.java new file mode 100644 index 00000000000..8155fb5d8f2 --- /dev/null +++ b/test/langtools/tools/jnativescan/cases/modules/org.myapp/module-info.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 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 + * 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. + */ + +module org.myapp { + requires org.lib; + + uses org.lib.Service; +} diff --git a/test/langtools/tools/jnativescan/cases/modules/org.myapp/org/myapp/main/Main.java b/test/langtools/tools/jnativescan/cases/modules/org.myapp/org/myapp/main/Main.java new file mode 100644 index 00000000000..c2329b2ceeb --- /dev/null +++ b/test/langtools/tools/jnativescan/cases/modules/org.myapp/org/myapp/main/Main.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 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 + * 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. + */ + +package org.myapp.main; + +import org.lib.Lib; + +public class Main { + public static void main(String[] args) { + Lib.doIt(); + } +} diff --git a/test/langtools/tools/jnativescan/cases/modules/org.service/module-info.java b/test/langtools/tools/jnativescan/cases/modules/org.service/module-info.java new file mode 100644 index 00000000000..431dd64172d --- /dev/null +++ b/test/langtools/tools/jnativescan/cases/modules/org.service/module-info.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 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 + * 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. + */ +module org.service { + requires org.lib; + + provides org.lib.Service with org.service.ServiceImpl; +} diff --git a/test/langtools/tools/jnativescan/cases/modules/org.service/org/service/ServiceImpl.java b/test/langtools/tools/jnativescan/cases/modules/org.service/org/service/ServiceImpl.java new file mode 100644 index 00000000000..6e643f1c649 --- /dev/null +++ b/test/langtools/tools/jnativescan/cases/modules/org.service/org/service/ServiceImpl.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 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 + * 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. + */ +package org.service; + +import org.lib.Service; + +import java.lang.foreign.MemorySegment; + +public class ServiceImpl implements Service { + public void doIt() { + MemorySegment.ofAddress(1234).reinterpret(10); + } + + private native void m(); +} diff --git a/test/langtools/tools/jnativescan/cases/modules/org.singlejar/module-info.java b/test/langtools/tools/jnativescan/cases/modules/org.singlejar/module-info.java new file mode 100644 index 00000000000..c9f96e4f771 --- /dev/null +++ b/test/langtools/tools/jnativescan/cases/modules/org.singlejar/module-info.java @@ -0,0 +1,25 @@ +/* + * Copyright (c) 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 + * 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. + */ +module org.singlejar { + +} diff --git a/test/langtools/tools/jnativescan/cases/modules/org.singlejar/org/singlejar/main/Main.java b/test/langtools/tools/jnativescan/cases/modules/org.singlejar/org/singlejar/main/Main.java new file mode 100644 index 00000000000..c6925eaf649 --- /dev/null +++ b/test/langtools/tools/jnativescan/cases/modules/org.singlejar/org/singlejar/main/Main.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 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 + * 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. + */ + +package org.singlejar.main; + +import java.lang.foreign.*; + +public class Main { + public static void main(String[] args) { + MemorySegment.ofAddress(1234).reinterpret(10); + } + + private static native void m(); +}