8317611: Add a tool like jdeprscan to find usage of restricted methods

Reviewed-by: alanb, ihse, mcimadamore, jlahoda, jwaters
This commit is contained in:
Jorn Vernee 2024-07-08 12:39:33 +00:00
parent 953c35eb5b
commit cec222e460
37 changed files with 2250 additions and 15 deletions

View File

@ -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, \
))

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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<JavaFileObject> {
.noneMatch(n -> n.equals(release))) {
return false;
}
JavaFileManager fm = pp.getPlatform(release, "").getFileManager();
JavaFileManager fm = pp.getPlatformTrusted(release).getFileManager();
List<String> classNames = new ArrayList<>();
for (JavaFileObject fo : fm.list(StandardLocation.PLATFORM_CLASS_PATH,
"",

View File

@ -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<byte[]> 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<byte[]> 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<byte[]> 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<byte[]> 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);
}
});
}
}
}

View File

@ -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<ClassFileSource> sources, Runtime.Version version) throws IOException {
Map<ClassDesc, Info> classMap = new HashMap<>();
for (ClassFileSource source : sources) {
try (Stream<byte[]> 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<ClassDesc, ClassResolver.Info> action);
public abstract Optional<ClassResolver.Info> lookup(ClassDesc desc);
@Override
public abstract void close() throws IOException;
private static class SimpleClassResolver extends ClassResolver {
private final Map<ClassDesc, ClassResolver.Info> classMap;
public SimpleClassResolver(Map<ClassDesc, Info> classMap) {
this.classMap = classMap;
}
public void forEach(BiConsumer<ClassDesc, ClassResolver.Info> action) {
classMap.forEach(action);
}
public Optional<ClassResolver.Info> 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<String, String> packageToSystemModule;
private final Map<ClassDesc, Info> cache = new HashMap<>();
public SystemModuleClassResolver(JavaFileManager platformFileManager) {
this.platformFileManager = platformFileManager;
this.packageToSystemModule = packageToSystemModule(platformFileManager);
}
private static Map<String, String> packageToSystemModule(JavaFileManager platformFileManager) {
try {
Set<JavaFileManager.Location> locations = platformFileManager.listLocationsForModules(
StandardLocation.SYSTEM_MODULES).iterator().next();
Map<String, String> 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<ClassDesc, Info> action) {
throw new UnsupportedOperationException("NYI");
}
@Override
public Optional<Info> 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();
}
}
}

View File

@ -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);
}
}

View File

@ -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<Path> classPaths;
private final List<Path> modulePaths;
private final List<String> cmdRootModules;
private final Runtime.Version version;
private final Action action;
public JNativeScanTask(PrintWriter out, List<Path> classPaths, List<Path> modulePaths,
List<String> 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<ClassFileSource> toScan = new ArrayList<>(findAllClassPathJars());
ModuleFinder moduleFinder = ModuleFinder.of(modulePaths.toArray(Path[]::new));
List<String> 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<ClassFileSource, SortedMap<ClassDesc, List<RestrictedUse>>> 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<ClassFileSource> findAllClassPathJars() throws JNativeScanFatalError {
List<ClassFileSource> result = new ArrayList<>();
for (Path path : classPaths) {
if (isJarFile(path)) {
Deque<Path> 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<String> allModuleNames(ModuleFinder finder) {
return finder.findAll().stream().map(mr -> mr.descriptor().name()).toList();
}
private void printNativeAccess(SortedMap<ClassFileSource, SortedMap<ClassDesc, List<RestrictedUse>>> allRestrictedMethods) {
String nativeAccess = allRestrictedMethods.keySet().stream()
.map(ClassFileSource::moduleName)
.distinct()
.collect(Collectors.joining(","));
out.println(nativeAccess);
}
private void dumpAll(SortedMap<ClassFileSource, SortedMap<ClassDesc, List<RestrictedUse>>> allRestrictedMethods) {
if (allRestrictedMethods.isEmpty()) {
out.println(" <no restricted methods>");
} 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<MethodRef> 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();
}
}

View File

@ -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<Void> helpOpt = parser.acceptsAll(List.of("?", "h", "help"), "help").forHelp();
OptionSpec<Void> versionOpt = parser.accepts("version", "Print version information and exit");
OptionSpec<Path> classPathOpt = parser.accepts(
"class-path",
"The class path as used at runtime")
.withRequiredArg()
.withValuesSeparatedBy(File.pathSeparatorChar)
.withValuesConvertedBy(PARSE_PATH);
OptionSpec<Path> modulePathOpt = parser.accepts(
"module-path",
"The module path as used at runtime")
.withRequiredArg()
.withValuesSeparatedBy(File.pathSeparatorChar)
.withValuesConvertedBy(PARSE_PATH);
OptionSpec<Runtime.Version> releaseOpt = parser.accepts(
"release",
"The runtime version that will run the application")
.withRequiredArg()
.withValuesConvertedBy(PARSE_VERSION);
OptionSpec<String> addModulesOpt = parser.accepts(
"add-modules",
"List of root modules to scan")
.requiredIf(modulePathOpt)
.withRequiredArg()
.withValuesSeparatedBy(',');
OptionSpec<Void> 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<Path> classPathJars = optionSet.valuesOf(classPathOpt);
List<Path> modulePaths = optionSet.valuesOf(modulePathOpt);
List<String> 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<Path> PARSE_PATH = new ValueConverter<>() {
@Override
public Path convert(String value) {
return Path.of(value);
}
@Override
public Class<? extends Path> valueType() {
return Path.class;
}
@Override
public String valuePattern() {
return "Path";
}
};
private static final ValueConverter<Runtime.Version> 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<? extends Runtime.Version> valueType() {
return Runtime.Version.class;
}
@Override
public String valuePattern() {
return "Version";
}
};
}

View File

@ -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();
}
}

View File

@ -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<MethodRef, Boolean> 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<ClassFileSource, SortedMap<ClassDesc, List<RestrictedUse>>> findAll() throws JNativeScanFatalError {
SortedMap<ClassFileSource, SortedMap<ClassDesc, List<RestrictedUse>>> restrictedMethods
= new TreeMap<>(Comparator.comparing(ClassFileSource::path));
classesToScan.forEach((_, info) -> {
ClassModel classModel = info.model();
List<RestrictedUse> perClass = new ArrayList<>();
classModel.methods().forEach(methodModel -> {
if (methodModel.flags().has(AccessFlag.NATIVE)) {
perClass.add(new NativeMethodDecl(MethodRef.ofModel(methodModel)));
} else {
SortedSet<MethodRef> perMethod = new TreeSet<>(Comparator.comparing(MethodRef::toString));
methodModel.code().ifPresent(code -> {
try {
code.forEach(e -> {
switch (e) {
case InvokeInstruction invoke -> {
MethodRef ref = MethodRef.ofInvokeInstruction(invoke);
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<ClassResolver.Info> info = systemClassResolver.lookup(methodRef.owner());
if (!info.isPresent()) {
return false;
}
ClassModel classModel = info.get().model();
Optional<MethodModel> 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<MethodModel> 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();
}
}

View File

@ -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<MethodRef> referees) implements RestrictedUse {}
record NativeMethodDecl(MethodRef decl) implements RestrictedUse {}
}

View File

@ -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 <em>{@index jdeps jdeps tool}</em>,
* <em>{@index javap javap tool}</em>, and
* <em>{@index jdeprscan jdeprscan tool}</em> tools.
* <em>{@index javap javap tool}</em>,
* <em>{@index jdeprscan jdeprscan tool}</em>, and
* <em>{@index jnativescan jnativescan tool}</em> tools.
*
* <p>
* This module provides the equivalent of command-line access to the
* <em>javap</em> and <em>jdeps</em> tools via the
* <em>javap</em>, <em>jdeps</em>, and <em>jnativescan</em> tools via the
* {@link java.util.spi.ToolProvider ToolProvider} service provider
* interface (SPI)</p>
*
@ -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;
}

View File

@ -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.

View File

@ -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.

View File

@ -63,6 +63,10 @@ langtools_jdeps = \
tools/all \
tools/jdeps
langtools_jnativescan = \
tools/all \
tools/jnativescan
langtools_slow = \
jdk/internal/shellsupport/doc/FullJavadocHelperTest.java

View File

@ -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;
}
}

View File

@ -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("<no restricted methods>");
}
}

View File

@ -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<String> 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());
}
}

View File

@ -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");
}
}

View File

@ -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("<no restricted methods>");
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}

View File

@ -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();
}
}

View File

@ -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();
}

View File

@ -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<String> l = List.of(args);
l.stream(); // List does not declare stream()
}
}

View File

@ -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();
}

View File

@ -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;
}

View File

@ -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();
}

View File

@ -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 {
}

View File

@ -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;
}

View File

@ -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();
}
}

View File

@ -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;
}

View File

@ -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();
}

View File

@ -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 {
}

View File

@ -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();
}