Skip to content

Commit cec222e

Browse files
committedJul 8, 2024
8317611: Add a tool like jdeprscan to find usage of restricted methods
Reviewed-by: alanb, ihse, mcimadamore, jlahoda, jwaters
1 parent 953c35e commit cec222e

File tree

37 files changed

+2250
-15
lines changed

37 files changed

+2250
-15
lines changed
 

‎make/modules/jdk.jdeps/Launcher.gmk

+9
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,12 @@ $(eval $(call SetupBuildLauncher, jdeprscan, \
5151
MAIN_CLASS := com.sun.tools.jdeprscan.Main, \
5252
CFLAGS := -DEXPAND_CLASSPATH_WILDCARDS, \
5353
))
54+
55+
################################################################################
56+
## Build jnativescan
57+
################################################################################
58+
59+
$(eval $(call SetupBuildLauncher, jnativescan, \
60+
MAIN_CLASS := com.sun.tools.jnativescan.Main, \
61+
CFLAGS := -DEXPAND_CLASSPATH_WILDCARDS, \
62+
))

‎src/jdk.compiler/share/classes/com/sun/tools/javac/platform/JDKPlatformProvider.java

+9-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -83,7 +83,14 @@ public Iterable<String> getSupportedPlatformNames() {
8383
}
8484

8585
@Override
86-
public PlatformDescription getPlatform(String platformName, String options) {
86+
public PlatformDescription getPlatform(String platformName, String options) throws PlatformNotSupported {
87+
if (!SUPPORTED_JAVA_PLATFORM_VERSIONS.contains(platformName)) {
88+
throw new PlatformNotSupported();
89+
}
90+
return getPlatformTrusted(platformName);
91+
}
92+
93+
public PlatformDescription getPlatformTrusted(String platformName) {
8794
return new PlatformDescriptionImpl(platformName);
8895
}
8996

‎src/jdk.internal.opt/share/classes/module-info.java

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -31,11 +31,13 @@
3131
module jdk.internal.opt {
3232
exports jdk.internal.joptsimple to
3333
jdk.jlink,
34-
jdk.jshell;
34+
jdk.jshell,
35+
jdk.jdeps;
3536
exports jdk.internal.opt to
3637
jdk.compiler,
3738
jdk.jartool,
3839
jdk.javadoc,
3940
jdk.jlink,
40-
jdk.jpackage;
41+
jdk.jpackage,
42+
jdk.jdeps;
4143
}

‎src/jdk.jdeps/share/classes/com/sun/tools/jdeprscan/Main.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -414,7 +414,7 @@ boolean processRelease(String release, Collection<String> classes) throws IOExce
414414
.noneMatch(n -> n.equals(release))) {
415415
return false;
416416
}
417-
JavaFileManager fm = pp.getPlatform(release, "").getFileManager();
417+
JavaFileManager fm = pp.getPlatformTrusted(release).getFileManager();
418418
List<String> classNames = new ArrayList<>();
419419
for (JavaFileObject fo : fm.list(StandardLocation.PLATFORM_CLASS_PATH,
420420
"",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
/*
2+
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package com.sun.tools.jnativescan;
26+
27+
import java.io.IOException;
28+
import java.io.InputStream;
29+
import java.io.UncheckedIOException;
30+
import java.lang.module.ModuleReader;
31+
import java.lang.module.ModuleReference;
32+
import java.net.URI;
33+
import java.nio.file.Files;
34+
import java.nio.file.Path;
35+
import java.util.jar.JarFile;
36+
import java.util.stream.Stream;
37+
import java.util.zip.ZipFile;
38+
39+
sealed interface ClassFileSource {
40+
String moduleName();
41+
Path path();
42+
43+
Stream<byte[]> classFiles(Runtime.Version version) throws IOException;
44+
45+
record Module(ModuleReference reference) implements ClassFileSource {
46+
@Override
47+
public String moduleName() {
48+
return reference.descriptor().name();
49+
}
50+
51+
@Override
52+
public Path path() {
53+
URI location = reference.location().orElseThrow();
54+
return Path.of(location);
55+
}
56+
57+
@Override
58+
public Stream<byte[]> classFiles(Runtime.Version version) throws IOException {
59+
ModuleReader reader = reference().open();
60+
return reader.list()
61+
.filter(resourceName -> resourceName.endsWith(".class"))
62+
.map(resourceName -> {
63+
try (InputStream stream = reader.open(resourceName).orElseThrow()) {
64+
return stream.readAllBytes();
65+
} catch (IOException e) {
66+
throw new UncheckedIOException(e);
67+
}
68+
}).onClose(() -> {
69+
try {
70+
reader.close();
71+
} catch (IOException e) {
72+
throw new UncheckedIOException(e);
73+
}
74+
});
75+
}
76+
}
77+
78+
record ClassPathJar(Path path) implements ClassFileSource {
79+
@Override
80+
public String moduleName() {
81+
return "ALL-UNNAMED";
82+
}
83+
84+
@Override
85+
public Stream<byte[]> classFiles(Runtime.Version version) throws IOException {
86+
JarFile jf = new JarFile(path().toFile(), false, ZipFile.OPEN_READ, version);
87+
return jf.versionedStream()
88+
.filter(je -> je.getName().endsWith(".class"))
89+
.map(je -> {
90+
try (InputStream stream = jf.getInputStream(je)){
91+
return stream.readAllBytes();
92+
} catch (IOException e) {
93+
throw new UncheckedIOException(e);
94+
}
95+
}).onClose(() -> {
96+
try {
97+
jf.close();
98+
} catch (IOException e) {
99+
throw new UncheckedIOException(e);
100+
}
101+
});
102+
}
103+
}
104+
105+
record ClassPathDirectory(Path path) implements ClassFileSource {
106+
@Override
107+
public String moduleName() {
108+
return "ALL-UNNAMED";
109+
}
110+
111+
@Override
112+
public Stream<byte[]> classFiles(Runtime.Version version) throws IOException {
113+
return Files.walk(path)
114+
.filter(file -> Files.isRegularFile(file) && file.toString().endsWith(".class"))
115+
.map(file -> {
116+
try (InputStream stream = Files.newInputStream(file)){
117+
return stream.readAllBytes();
118+
} catch (IOException e) {
119+
throw new UncheckedIOException(e);
120+
}
121+
});
122+
}
123+
}
124+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
/*
2+
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package com.sun.tools.jnativescan;
26+
27+
import com.sun.tools.javac.platform.PlatformDescription;
28+
import com.sun.tools.javac.platform.PlatformProvider;
29+
30+
import javax.tools.JavaFileManager;
31+
import javax.tools.JavaFileObject;
32+
import javax.tools.StandardLocation;
33+
import java.io.IOException;
34+
import java.lang.classfile.ClassFile;
35+
import java.lang.classfile.ClassModel;
36+
import java.lang.constant.ClassDesc;
37+
import java.lang.module.ModuleDescriptor;
38+
import java.util.*;
39+
import java.util.function.BiConsumer;
40+
import java.util.stream.Stream;
41+
42+
abstract class ClassResolver implements AutoCloseable {
43+
44+
static ClassResolver forClassFileSources(List<ClassFileSource> sources, Runtime.Version version) throws IOException {
45+
Map<ClassDesc, Info> classMap = new HashMap<>();
46+
for (ClassFileSource source : sources) {
47+
try (Stream<byte[]> classFiles = source.classFiles(version)) {
48+
classFiles.forEach(bytes -> {
49+
ClassModel model = ClassFile.of().parse(bytes);
50+
ClassDesc desc = model.thisClass().asSymbol();
51+
classMap.put(desc, new Info(source, model));
52+
});
53+
}
54+
}
55+
return new SimpleClassResolver(classMap);
56+
}
57+
58+
static ClassResolver forSystemModules(Runtime.Version version) {
59+
String platformName = String.valueOf(version.feature());
60+
PlatformProvider platformProvider = ServiceLoader.load(PlatformProvider.class).findFirst().orElseThrow();
61+
PlatformDescription platform;
62+
try {
63+
platform = platformProvider.getPlatform(platformName, null);
64+
} catch (PlatformProvider.PlatformNotSupported e) {
65+
throw new JNativeScanFatalError("Release: " + platformName + " not supported", e);
66+
}
67+
JavaFileManager fm = platform.getFileManager();
68+
return new SystemModuleClassResolver(fm);
69+
}
70+
71+
record Info(ClassFileSource source, ClassModel model) {}
72+
73+
public abstract void forEach(BiConsumer<ClassDesc, ClassResolver.Info> action);
74+
public abstract Optional<ClassResolver.Info> lookup(ClassDesc desc);
75+
76+
@Override
77+
public abstract void close() throws IOException;
78+
79+
private static class SimpleClassResolver extends ClassResolver {
80+
81+
private final Map<ClassDesc, ClassResolver.Info> classMap;
82+
83+
public SimpleClassResolver(Map<ClassDesc, Info> classMap) {
84+
this.classMap = classMap;
85+
}
86+
87+
public void forEach(BiConsumer<ClassDesc, ClassResolver.Info> action) {
88+
classMap.forEach(action);
89+
}
90+
91+
public Optional<ClassResolver.Info> lookup(ClassDesc desc) {
92+
return Optional.ofNullable(classMap.get(desc));
93+
}
94+
95+
@Override
96+
public void close() {}
97+
}
98+
99+
private static class SystemModuleClassResolver extends ClassResolver {
100+
101+
private final JavaFileManager platformFileManager;
102+
private final Map<String, String> packageToSystemModule;
103+
private final Map<ClassDesc, Info> cache = new HashMap<>();
104+
105+
public SystemModuleClassResolver(JavaFileManager platformFileManager) {
106+
this.platformFileManager = platformFileManager;
107+
this.packageToSystemModule = packageToSystemModule(platformFileManager);
108+
}
109+
110+
private static Map<String, String> packageToSystemModule(JavaFileManager platformFileManager) {
111+
try {
112+
Set<JavaFileManager.Location> locations = platformFileManager.listLocationsForModules(
113+
StandardLocation.SYSTEM_MODULES).iterator().next();
114+
115+
Map<String, String> result = new HashMap<>();
116+
for (JavaFileManager.Location loc : locations) {
117+
JavaFileObject jfo = platformFileManager.getJavaFileForInput(loc, "module-info", JavaFileObject.Kind.CLASS);
118+
ModuleDescriptor descriptor = ModuleDescriptor.read(jfo.openInputStream());
119+
for (ModuleDescriptor.Exports export : descriptor.exports()) {
120+
if (!export.isQualified()) {
121+
result.put(export.source(), descriptor.name());
122+
}
123+
}
124+
}
125+
return result;
126+
} catch (IOException e) {
127+
throw new RuntimeException(e);
128+
}
129+
}
130+
131+
@Override
132+
public void forEach(BiConsumer<ClassDesc, Info> action) {
133+
throw new UnsupportedOperationException("NYI");
134+
}
135+
136+
@Override
137+
public Optional<Info> lookup(ClassDesc desc) {
138+
return Optional.ofNullable(cache.computeIfAbsent(desc, _ -> {
139+
String qualName = JNativeScanTask.qualName(desc);
140+
String moduleName = packageToSystemModule.get(desc.packageName());
141+
if (moduleName != null) {
142+
try {
143+
JavaFileManager.Location loc = platformFileManager.getLocationForModule(StandardLocation.SYSTEM_MODULES, moduleName);
144+
JavaFileObject jfo = platformFileManager.getJavaFileForInput(loc, qualName, JavaFileObject.Kind.CLASS);
145+
if (jfo == null) {
146+
throw new JNativeScanFatalError("System class can not be found: " + qualName);
147+
}
148+
ClassModel model = ClassFile.of().parse(jfo.openInputStream().readAllBytes());
149+
return new Info(null, model);
150+
} catch (IOException e) {
151+
throw new RuntimeException(e);
152+
}
153+
}
154+
return null;
155+
}));
156+
}
157+
158+
@Override
159+
public void close() throws IOException {
160+
platformFileManager.close();
161+
}
162+
}
163+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package com.sun.tools.jnativescan;
26+
27+
import java.io.Serial;
28+
29+
// Exception used in case of fatal error that is reasonably expected and handled.
30+
public class JNativeScanFatalError extends RuntimeException {
31+
@Serial
32+
private static final long serialVersionUID = 1L;
33+
34+
public JNativeScanFatalError(String message) {
35+
super(message);
36+
}
37+
38+
public JNativeScanFatalError(String message, Throwable cause) {
39+
super(message, cause);
40+
}
41+
42+
public JNativeScanFatalError(Throwable cause) {
43+
super(cause);
44+
}
45+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
/*
2+
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package com.sun.tools.jnativescan;
26+
27+
import java.io.IOException;
28+
import java.io.PrintWriter;
29+
import java.lang.constant.ClassDesc;
30+
import java.lang.module.Configuration;
31+
import java.lang.module.ModuleFinder;
32+
import java.lang.module.ResolvedModule;
33+
import java.net.URI;
34+
import java.nio.file.Files;
35+
import java.nio.file.Path;
36+
import java.util.*;
37+
import java.util.jar.JarFile;
38+
import java.util.jar.Manifest;
39+
import java.util.stream.Collectors;
40+
import java.util.zip.ZipFile;
41+
42+
class JNativeScanTask {
43+
44+
private final PrintWriter out;
45+
private final List<Path> classPaths;
46+
private final List<Path> modulePaths;
47+
private final List<String> cmdRootModules;
48+
private final Runtime.Version version;
49+
private final Action action;
50+
51+
public JNativeScanTask(PrintWriter out, List<Path> classPaths, List<Path> modulePaths,
52+
List<String> cmdRootModules, Runtime.Version version, Action action) {
53+
this.out = out;
54+
this.classPaths = classPaths;
55+
this.modulePaths = modulePaths;
56+
this.version = version;
57+
this.action = action;
58+
this.cmdRootModules = cmdRootModules;
59+
}
60+
61+
public void run() throws JNativeScanFatalError {
62+
List<ClassFileSource> toScan = new ArrayList<>(findAllClassPathJars());
63+
64+
ModuleFinder moduleFinder = ModuleFinder.of(modulePaths.toArray(Path[]::new));
65+
List<String> rootModules = cmdRootModules;
66+
if (rootModules.contains("ALL-MODULE-PATH")) {
67+
rootModules = allModuleNames(moduleFinder);
68+
}
69+
Configuration config = systemConfiguration().resolveAndBind(ModuleFinder.of(), moduleFinder, rootModules);
70+
for (ResolvedModule m : config.modules()) {
71+
toScan.add(new ClassFileSource.Module(m.reference()));
72+
}
73+
74+
SortedMap<ClassFileSource, SortedMap<ClassDesc, List<RestrictedUse>>> allRestrictedMethods;
75+
try(ClassResolver classesToScan = ClassResolver.forClassFileSources(toScan, version);
76+
ClassResolver systemClassResolver = ClassResolver.forSystemModules(version)) {
77+
NativeMethodFinder finder = NativeMethodFinder.create(classesToScan, systemClassResolver);
78+
allRestrictedMethods = finder.findAll();
79+
} catch (IOException e) {
80+
throw new RuntimeException(e);
81+
}
82+
83+
switch (action) {
84+
case PRINT -> printNativeAccess(allRestrictedMethods);
85+
case DUMP_ALL -> dumpAll(allRestrictedMethods);
86+
}
87+
}
88+
89+
private List<ClassFileSource> findAllClassPathJars() throws JNativeScanFatalError {
90+
List<ClassFileSource> result = new ArrayList<>();
91+
for (Path path : classPaths) {
92+
if (isJarFile(path)) {
93+
Deque<Path> jarsToScan = new ArrayDeque<>();
94+
jarsToScan.offer(path);
95+
96+
// recursively look for all class path jars, starting at the root jars
97+
// in this.classPaths, and recursively following all Class-Path manifest
98+
// attributes
99+
while (!jarsToScan.isEmpty()) {
100+
Path jar = jarsToScan.poll();
101+
String[] classPathAttribute = classPathAttribute(jar);
102+
Path parentDir = jar.getParent();
103+
for (String classPathEntry : classPathAttribute) {
104+
Path otherJar = parentDir != null
105+
? parentDir.resolve(classPathEntry)
106+
: Path.of(classPathEntry);
107+
if (Files.exists(otherJar)) {
108+
// Class-Path attribute specifies that jars that
109+
// are not found are simply ignored. Do the same here
110+
jarsToScan.offer(otherJar);
111+
}
112+
}
113+
result.add(new ClassFileSource.ClassPathJar(jar));
114+
}
115+
} else if (Files.isDirectory(path)) {
116+
result.add(new ClassFileSource.ClassPathDirectory(path));
117+
} else {
118+
throw new JNativeScanFatalError(
119+
"Path does not appear to be a jar file, or directory containing classes: " + path);
120+
}
121+
}
122+
return result;
123+
}
124+
125+
private String[] classPathAttribute(Path jar) {
126+
try (JarFile jf = new JarFile(jar.toFile(), false, ZipFile.OPEN_READ, version)) {
127+
Manifest manifest = jf.getManifest();
128+
if (manifest != null) {
129+
String attrib = manifest.getMainAttributes().getValue("Class-Path");
130+
if (attrib != null) {
131+
return attrib.split("\\s+");
132+
}
133+
}
134+
return new String[0];
135+
} catch (IOException e) {
136+
throw new RuntimeException(e);
137+
}
138+
}
139+
140+
private Configuration systemConfiguration() {
141+
ModuleFinder systemFinder = ModuleFinder.ofSystem();
142+
Configuration system = Configuration.resolve(systemFinder, List.of(Configuration.empty()), ModuleFinder.of(),
143+
allModuleNames(systemFinder)); // resolve all of them
144+
return system;
145+
}
146+
147+
private List<String> allModuleNames(ModuleFinder finder) {
148+
return finder.findAll().stream().map(mr -> mr.descriptor().name()).toList();
149+
}
150+
151+
private void printNativeAccess(SortedMap<ClassFileSource, SortedMap<ClassDesc, List<RestrictedUse>>> allRestrictedMethods) {
152+
String nativeAccess = allRestrictedMethods.keySet().stream()
153+
.map(ClassFileSource::moduleName)
154+
.distinct()
155+
.collect(Collectors.joining(","));
156+
out.println(nativeAccess);
157+
}
158+
159+
private void dumpAll(SortedMap<ClassFileSource, SortedMap<ClassDesc, List<RestrictedUse>>> allRestrictedMethods) {
160+
if (allRestrictedMethods.isEmpty()) {
161+
out.println(" <no restricted methods>");
162+
} else {
163+
allRestrictedMethods.forEach((module, perClass) -> {
164+
out.println(module.path() + " (" + module.moduleName() + "):");
165+
perClass.forEach((classDesc, restrictedUses) -> {
166+
out.println(" " + qualName(classDesc) + ":");
167+
restrictedUses.forEach(use -> {
168+
switch (use) {
169+
case RestrictedUse.NativeMethodDecl(MethodRef nmd) ->
170+
out.println(" " + nmd + " is a native method declaration");
171+
case RestrictedUse.RestrictedMethodRefs(MethodRef referent, Set<MethodRef> referees) -> {
172+
out.println(" " + referent + " references restricted methods:");
173+
referees.forEach(referee -> out.println(" " + referee));
174+
}
175+
}
176+
});
177+
});
178+
});
179+
}
180+
}
181+
182+
private static boolean isJarFile(Path path) throws JNativeScanFatalError {
183+
return Files.exists(path) && Files.isRegularFile(path) && path.toString().endsWith(".jar");
184+
}
185+
186+
public enum Action {
187+
DUMP_ALL,
188+
PRINT
189+
}
190+
191+
public static String qualName(ClassDesc desc) {
192+
String packagePrefix = desc.packageName().isEmpty() ? "" : desc.packageName() + ".";
193+
return packagePrefix + desc.displayName();
194+
}
195+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
/*
2+
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package com.sun.tools.jnativescan;
26+
27+
import jdk.internal.joptsimple.*;
28+
import jdk.internal.opt.CommandLine;
29+
30+
import java.io.File;
31+
import java.io.IOException;
32+
import java.io.PrintWriter;
33+
import java.nio.file.Path;
34+
import java.util.List;
35+
import java.util.Optional;
36+
import java.util.function.Function;
37+
import java.util.spi.ToolProvider;
38+
39+
public class Main {
40+
41+
private static boolean DEBUG = Boolean.getBoolean("com.sun.tools.jnativescan.DEBUG");
42+
43+
private static final int SUCCESS_CODE = 0;
44+
private static final int FATAL_ERROR_CODE = 1;
45+
46+
private final PrintWriter out;
47+
private final PrintWriter err;
48+
49+
private Main(PrintWriter out, PrintWriter err) {
50+
this.out = out;
51+
this.err = err;
52+
}
53+
54+
private void printError(String message) {
55+
err.println("ERROR: " + message);
56+
}
57+
58+
private void printUsage() {
59+
out.print("""
60+
Use 'jnativescan --help' for help
61+
""");
62+
}
63+
64+
private void printVersion() {
65+
out.println(System.getProperty("java.version"));
66+
}
67+
68+
public int run(String[] args) {
69+
if (args.length < 1) {
70+
printUsage();
71+
return FATAL_ERROR_CODE;
72+
}
73+
74+
try {
75+
String[] expandedArgs = expandArgFiles(args);
76+
parseOptionsAndRun(expandedArgs);
77+
} catch (JNativeScanFatalError fatalError) {
78+
printError(fatalError.getMessage());
79+
for (Throwable cause = fatalError.getCause();
80+
cause instanceof JNativeScanFatalError jNativeScanFatalError;
81+
cause = jNativeScanFatalError.getCause()) {
82+
err.println("CAUSED BY: " + jNativeScanFatalError.getMessage());
83+
}
84+
if (DEBUG) {
85+
fatalError.printStackTrace(err);
86+
}
87+
return FATAL_ERROR_CODE;
88+
} catch (Throwable e) {
89+
printError("Unexpected exception encountered");
90+
e.printStackTrace(err);
91+
return FATAL_ERROR_CODE;
92+
}
93+
94+
return SUCCESS_CODE;
95+
}
96+
97+
private void parseOptionsAndRun(String[] expandedArgs) throws JNativeScanFatalError {
98+
OptionParser parser = new OptionParser(false);
99+
OptionSpec<Void> helpOpt = parser.acceptsAll(List.of("?", "h", "help"), "help").forHelp();
100+
OptionSpec<Void> versionOpt = parser.accepts("version", "Print version information and exit");
101+
OptionSpec<Path> classPathOpt = parser.accepts(
102+
"class-path",
103+
"The class path as used at runtime")
104+
.withRequiredArg()
105+
.withValuesSeparatedBy(File.pathSeparatorChar)
106+
.withValuesConvertedBy(PARSE_PATH);
107+
OptionSpec<Path> modulePathOpt = parser.accepts(
108+
"module-path",
109+
"The module path as used at runtime")
110+
.withRequiredArg()
111+
.withValuesSeparatedBy(File.pathSeparatorChar)
112+
.withValuesConvertedBy(PARSE_PATH);
113+
OptionSpec<Runtime.Version> releaseOpt = parser.accepts(
114+
"release",
115+
"The runtime version that will run the application")
116+
.withRequiredArg()
117+
.withValuesConvertedBy(PARSE_VERSION);
118+
OptionSpec<String> addModulesOpt = parser.accepts(
119+
"add-modules",
120+
"List of root modules to scan")
121+
.requiredIf(modulePathOpt)
122+
.withRequiredArg()
123+
.withValuesSeparatedBy(',');
124+
OptionSpec<Void> printNativeAccessOpt = parser.accepts(
125+
"print-native-access",
126+
"print a comma separated list of modules that may perform native access operations." +
127+
" ALL-UNNAMED is used to indicate unnamed modules.");
128+
129+
OptionSet optionSet;
130+
try {
131+
optionSet = parser.parse(expandedArgs);
132+
} catch (OptionException oe) {
133+
throw new JNativeScanFatalError("Parsing options failed: " + oe.getMessage(), oe);
134+
}
135+
136+
if (optionSet.nonOptionArguments().size() != 0) {
137+
throw new JNativeScanFatalError("jnativescan does not accept positional arguments");
138+
}
139+
140+
if (optionSet.has(helpOpt)) {
141+
out.println("""
142+
The jnativescan tool can be used to find methods that may access native functionality when
143+
run. This includes restricted method calls and 'native' method declarations.
144+
""");
145+
try {
146+
parser.printHelpOn(out);
147+
return;
148+
} catch (IOException e) {
149+
throw new IllegalStateException(e);
150+
}
151+
}
152+
153+
if (optionSet.has(versionOpt)) {
154+
printVersion();
155+
return;
156+
}
157+
158+
List<Path> classPathJars = optionSet.valuesOf(classPathOpt);
159+
List<Path> modulePaths = optionSet.valuesOf(modulePathOpt);
160+
List<String> rootModules = optionSet.valuesOf(addModulesOpt);
161+
Runtime.Version version = Optional.ofNullable(optionSet.valueOf(releaseOpt)).orElse(Runtime.version());
162+
163+
JNativeScanTask.Action action = JNativeScanTask.Action.DUMP_ALL;
164+
if (optionSet.has(printNativeAccessOpt)) {
165+
action = JNativeScanTask.Action.PRINT;
166+
}
167+
168+
new JNativeScanTask(out, classPathJars, modulePaths, rootModules, version, action).run();
169+
}
170+
171+
private static String[] expandArgFiles(String[] args) throws JNativeScanFatalError {
172+
try {
173+
return CommandLine.parse(args);
174+
} catch (IOException e) { // file not found
175+
throw new JNativeScanFatalError(e.getMessage(), e);
176+
}
177+
}
178+
179+
public static void main(String[] args) {
180+
System.exit(new Main.Provider().run(System.out, System.err, args));
181+
}
182+
183+
public static class Provider implements ToolProvider {
184+
185+
@Override
186+
public String name() {
187+
return "jnativescan";
188+
}
189+
190+
@Override
191+
public int run(PrintWriter out, PrintWriter err, String... args) {
192+
return new Main(out, err).run(args);
193+
}
194+
}
195+
196+
// where
197+
private static final ValueConverter<Path> PARSE_PATH = new ValueConverter<>() {
198+
@Override
199+
public Path convert(String value) {
200+
return Path.of(value);
201+
}
202+
203+
@Override
204+
public Class<? extends Path> valueType() {
205+
return Path.class;
206+
}
207+
208+
@Override
209+
public String valuePattern() {
210+
return "Path";
211+
}
212+
};
213+
214+
private static final ValueConverter<Runtime.Version> PARSE_VERSION = new ValueConverter<>() {
215+
@Override
216+
public Runtime.Version convert(String value) {
217+
try {
218+
return Runtime.Version.parse(value);
219+
} catch (IllegalArgumentException e) {
220+
throw new JNativeScanFatalError("Invalid release: " + value + ": " + e.getMessage());
221+
}
222+
}
223+
224+
@Override
225+
public Class<? extends Runtime.Version> valueType() {
226+
return Runtime.Version.class;
227+
}
228+
229+
@Override
230+
public String valuePattern() {
231+
return "Version";
232+
}
233+
};
234+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package com.sun.tools.jnativescan;
26+
27+
import java.lang.classfile.MethodModel;
28+
import java.lang.classfile.constantpool.MemberRefEntry;
29+
import java.lang.classfile.instruction.InvokeInstruction;
30+
import java.lang.constant.ClassDesc;
31+
import java.lang.constant.MethodTypeDesc;
32+
33+
record MethodRef(ClassDesc owner, String name, MethodTypeDesc type) {
34+
public static MethodRef ofModel(MethodModel model) {
35+
return new MethodRef(model.parent().orElseThrow().thisClass().asSymbol(),
36+
model.methodName().stringValue(), model.methodTypeSymbol());
37+
}
38+
39+
public static MethodRef ofInvokeInstruction(InvokeInstruction instruction) {
40+
return new MethodRef(instruction.owner().asSymbol(),
41+
instruction.name().stringValue(), instruction.typeSymbol());
42+
}
43+
44+
@Override
45+
public String toString() {
46+
return JNativeScanTask.qualName(owner) + "::" + name + type.displayDescriptor();
47+
}
48+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
/*
2+
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package com.sun.tools.jnativescan;
26+
27+
import com.sun.tools.jnativescan.RestrictedUse.NativeMethodDecl;
28+
import com.sun.tools.jnativescan.RestrictedUse.RestrictedMethodRefs;
29+
30+
import java.io.IOException;
31+
import java.lang.classfile.Attributes;
32+
import java.lang.classfile.ClassModel;
33+
import java.lang.classfile.MethodModel;
34+
import java.lang.classfile.instruction.InvokeInstruction;
35+
import java.lang.constant.ClassDesc;
36+
import java.lang.constant.MethodTypeDesc;
37+
import java.lang.reflect.AccessFlag;
38+
import java.util.*;
39+
40+
class NativeMethodFinder {
41+
42+
// ct.sym uses this fake name for the restricted annotation instead
43+
// see make/langtools/src/classes/build/tools/symbolgenerator/CreateSymbols.java
44+
private static final String RESTRICTED_NAME = "Ljdk/internal/javac/Restricted+Annotation;";
45+
46+
private final Map<MethodRef, Boolean> cache = new HashMap<>();
47+
private final ClassResolver classesToScan;
48+
private final ClassResolver systemClassResolver;
49+
50+
private NativeMethodFinder(ClassResolver classesToScan, ClassResolver systemClassResolver) {
51+
this.classesToScan = classesToScan;
52+
this.systemClassResolver = systemClassResolver;
53+
}
54+
55+
public static NativeMethodFinder create(ClassResolver classesToScan, ClassResolver systemClassResolver) throws JNativeScanFatalError, IOException {
56+
return new NativeMethodFinder(classesToScan, systemClassResolver);
57+
}
58+
59+
public SortedMap<ClassFileSource, SortedMap<ClassDesc, List<RestrictedUse>>> findAll() throws JNativeScanFatalError {
60+
SortedMap<ClassFileSource, SortedMap<ClassDesc, List<RestrictedUse>>> restrictedMethods
61+
= new TreeMap<>(Comparator.comparing(ClassFileSource::path));
62+
classesToScan.forEach((_, info) -> {
63+
ClassModel classModel = info.model();
64+
List<RestrictedUse> perClass = new ArrayList<>();
65+
classModel.methods().forEach(methodModel -> {
66+
if (methodModel.flags().has(AccessFlag.NATIVE)) {
67+
perClass.add(new NativeMethodDecl(MethodRef.ofModel(methodModel)));
68+
} else {
69+
SortedSet<MethodRef> perMethod = new TreeSet<>(Comparator.comparing(MethodRef::toString));
70+
methodModel.code().ifPresent(code -> {
71+
try {
72+
code.forEach(e -> {
73+
switch (e) {
74+
case InvokeInstruction invoke -> {
75+
MethodRef ref = MethodRef.ofInvokeInstruction(invoke);
76+
if (isRestrictedMethod(ref)) {
77+
perMethod.add(ref);
78+
}
79+
}
80+
default -> {
81+
}
82+
}
83+
});
84+
} catch (JNativeScanFatalError e) {
85+
throw new JNativeScanFatalError("Error while processing method: " +
86+
MethodRef.ofModel(methodModel), e);
87+
}
88+
});
89+
if (!perMethod.isEmpty()) {
90+
perClass.add(new RestrictedMethodRefs(MethodRef.ofModel(methodModel), perMethod));
91+
}
92+
}
93+
});
94+
if (!perClass.isEmpty()) {
95+
restrictedMethods.computeIfAbsent(info.source(),
96+
_ -> new TreeMap<>(Comparator.comparing(JNativeScanTask::qualName)))
97+
.put(classModel.thisClass().asSymbol(), perClass);
98+
}
99+
});
100+
return restrictedMethods;
101+
}
102+
103+
private boolean isRestrictedMethod(MethodRef ref) throws JNativeScanFatalError {
104+
return cache.computeIfAbsent(ref, methodRef -> {
105+
if (methodRef.owner().isArray()) {
106+
// no restricted methods in arrays atm, and we can't look them up since they have no class file
107+
return false;
108+
}
109+
Optional<ClassResolver.Info> info = systemClassResolver.lookup(methodRef.owner());
110+
if (!info.isPresent()) {
111+
return false;
112+
}
113+
ClassModel classModel = info.get().model();
114+
Optional<MethodModel> methodModel = findMethod(classModel, methodRef.name(), methodRef.type());
115+
if (!methodModel.isPresent()) {
116+
// If we are here, the method was referenced through a subclass of the class containing the actual
117+
// method declaration. We could implement a method resolver (that needs to be version aware
118+
// as well) to find the method model of the declaration, but it's not really worth it.
119+
// None of the restricted methods (atm) are exposed through more than 1 public type, so it's not
120+
// possible for user code to reference them through a subclass.
121+
return false;
122+
}
123+
124+
return hasRestrictedAnnotation(methodModel.get());
125+
});
126+
}
127+
128+
private static boolean hasRestrictedAnnotation(MethodModel method) {
129+
return method.findAttribute(Attributes.runtimeVisibleAnnotations())
130+
.map(rva -> rva.annotations().stream().anyMatch(ann ->
131+
ann.className().stringValue().equals(RESTRICTED_NAME)))
132+
.orElse(false);
133+
}
134+
135+
private static Optional<MethodModel> findMethod(ClassModel classModel, String name, MethodTypeDesc type) {
136+
return classModel.methods().stream()
137+
.filter(m -> m.methodName().stringValue().equals(name)
138+
&& m.methodType().stringValue().equals(type.descriptorString()))
139+
.findFirst();
140+
}
141+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package com.sun.tools.jnativescan;
26+
27+
import java.util.SortedSet;
28+
29+
sealed interface RestrictedUse {
30+
record RestrictedMethodRefs(MethodRef referent, SortedSet<MethodRef> referees) implements RestrictedUse {}
31+
record NativeMethodDecl(MethodRef decl) implements RestrictedUse {}
32+
}

‎src/jdk.jdeps/share/classes/module-info.java

+15-8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -28,12 +28,13 @@
2828
/**
2929
* Defines tools for analysing dependencies in Java libraries and programs,
3030
* including the <em>{@index jdeps jdeps tool}</em>,
31-
* <em>{@index javap javap tool}</em>, and
32-
* <em>{@index jdeprscan jdeprscan tool}</em> tools.
31+
* <em>{@index javap javap tool}</em>,
32+
* <em>{@index jdeprscan jdeprscan tool}</em>, and
33+
* <em>{@index jnativescan jnativescan tool}</em> tools.
3334
*
3435
* <p>
3536
* This module provides the equivalent of command-line access to the
36-
* <em>javap</em> and <em>jdeps</em> tools via the
37+
* <em>javap</em>, <em>jdeps</em>, and <em>jnativescan</em> tools via the
3738
* {@link java.util.spi.ToolProvider ToolProvider} service provider
3839
* interface (SPI)</p>
3940
*
@@ -49,12 +50,14 @@
4950
* @toolGuide javap
5051
* @toolGuide jdeprscan
5152
* @toolGuide jdeps
53+
* @toolGuide jnativescan
5254
*
5355
* @provides java.util.spi.ToolProvider
54-
* Use {@link java.util.spi.ToolProvider#findFirst ToolProvider.findFirst("javap")}
55-
* or {@link java.util.spi.ToolProvider#findFirst ToolProvider.findFirst("jdeps")}
56+
* Use {@link java.util.spi.ToolProvider#findFirst ToolProvider.findFirst("javap")},
57+
* {@link java.util.spi.ToolProvider#findFirst ToolProvider.findFirst("jdeps")},
58+
* or {@link java.util.spi.ToolProvider#findFirst ToolProvider.findFirst("jnativescan")}
5659
* to obtain an instance of a {@code ToolProvider} that provides the equivalent
57-
* of command-line access to the {@code javap} or {@code jdeps} tool.
60+
* of command-line access to the {@code javap}, {@code jdeps}, {@code jnativescan} tool.
5861
*
5962
* @moduleGraph
6063
* @since 9
@@ -63,10 +66,14 @@
6366
module jdk.jdeps {
6467
requires java.compiler;
6568
requires jdk.compiler;
69+
requires jdk.internal.opt;
70+
71+
uses com.sun.tools.javac.platform.PlatformProvider;
6672

6773
exports com.sun.tools.classfile to jdk.jlink;
6874

6975
provides java.util.spi.ToolProvider with
7076
com.sun.tools.javap.Main.JavapToolProvider,
71-
com.sun.tools.jdeps.Main.JDepsToolProvider;
77+
com.sun.tools.jdeps.Main.JDepsToolProvider,
78+
com.sun.tools.jnativescan.Main.Provider;
7279
}

‎src/jdk.jdeps/share/man/jnativescan.1

+220
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
.\" Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
2+
.\" DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
3+
.\"
4+
.\" This code is free software; you can redistribute it and/or modify it
5+
.\" under the terms of the GNU General Public License version 2 only, as
6+
.\" published by the Free Software Foundation.
7+
.\"
8+
.\" This code is distributed in the hope that it will be useful, but WITHOUT
9+
.\" ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10+
.\" FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
11+
.\" version 2 for more details (a copy is included in the LICENSE file that
12+
.\" accompanied this code).
13+
.\"
14+
.\" You should have received a copy of the GNU General Public License version
15+
.\" 2 along with this work; if not, write to the Free Software Foundation,
16+
.\" Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
17+
.\"
18+
.\" Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
19+
.\" or visit www.oracle.com if you need additional information or have any
20+
.\" questions.
21+
.\"
22+
.\" Automatically generated by Pandoc 2.19.2
23+
.\"
24+
.\" Define V font for inline verbatim, using C font in formats
25+
.\" that render this, and otherwise B font.
26+
.ie "\f[CB]x\f[R]"x" \{\
27+
. ftr V B
28+
. ftr VI BI
29+
. ftr VB B
30+
. ftr VBI BI
31+
.\}
32+
.el \{\
33+
. ftr V CR
34+
. ftr VI CI
35+
. ftr VB CB
36+
. ftr VBI CBI
37+
.\}
38+
.TH "JNATIVESCAN" "1" "2025" "JDK 24-ea" "JDK Commands"
39+
.hy
40+
.SH NAME
41+
.PP
42+
jnativescan - static analysis tool that scans one or more jar files for
43+
uses of native functionalities, such as restricted method calls or
44+
\f[V]native\f[R] method declarations.
45+
.SH SYNOPSIS
46+
.PP
47+
\f[V]jnativescan\f[R] [\f[I]options\f[R]]
48+
.TP
49+
\f[I]options\f[R]
50+
See \f[B]Options for the jnativescan Command\f[R]
51+
.SH DESCRIPTION
52+
.PP
53+
The \f[V]jnative\f[R] tool is a static analysis tool provided by the JDK
54+
that scans a JAR file for uses of native functionalities, such as
55+
restricted method calls or \f[V]native\f[R] method declarations.
56+
.PP
57+
\f[V]jnativescan\f[R] accepts a runtime class path and module path
58+
configuration, as well as a set of root modules, and a target release.
59+
It scans the jars on the class and module paths, and reports uses of
60+
native functionalities either in a tree like structure, which also
61+
identifies that calling classes and methods, or as a list of module
62+
names when the \f[V]--print-native-access\f[R] flag is specified.
63+
.SH OPTIONS FOR THE JNATIVESCAN COMMAND
64+
.PP
65+
The following options are available:
66+
.TP
67+
\f[V]--class-path\f[R] \f[I]path\f[R]
68+
Used to specify a list of paths pointing to jar files to be scanned.
69+
.PP
70+
All jar files specified through this list will be scanned.
71+
If a jar file contains a \f[V]Class-Path\f[R] attribute in its manifest,
72+
jar files listed there will be scanned as well.
73+
Jar files listed in the \f[V]Class-Path\f[R] manifest attribute that can
74+
not be found are ignored.
75+
All the jar files found are treated as if they belonged to the unnamed
76+
module.
77+
.TP
78+
\f[V]--module-path\f[R] \f[I]path\f[R]
79+
Used to specify a list of paths pointing to jar files or directories
80+
containing jar files, that the tool can use to find modules that need to
81+
be scanned.
82+
The list of jar files that will be scanned depends on the
83+
\f[V]--add-modules\f[R] option.
84+
.RS
85+
.PP
86+
For both the \f[V]--class-path\f[R] and \f[V]--module-path\f[R] options,
87+
\f[I]path\f[R] should be a search path that consists of one or more jar
88+
files, separated by the system-specific path separator.
89+
For example:
90+
.IP \[bu] 2
91+
\f[B]Linux and macOS:\f[R]
92+
.RS 2
93+
.RS
94+
.PP
95+
\f[V]--class-path /some/foo.jar:/another/different/bar.jar\f[R]
96+
.RE
97+
.RE
98+
.PP
99+
\f[B]Note:\f[R]
100+
.PP
101+
On Windows, use a semicolon (\f[V];\f[R]) as the separator instead of a
102+
colon (\f[V]:\f[R]).
103+
.IP \[bu] 2
104+
\f[B]Windows:\f[R]
105+
.RS 2
106+
.RS
107+
.PP
108+
\f[V]--class-path C:\[rs]some\[rs]foo.jar;C:\[rs]another\[rs]different\[rs]bar.jar\f[R]
109+
.RE
110+
.RE
111+
.RE
112+
.TP
113+
\f[V]--add-modules\f[R] \f[I]module[,module...]\f[R]
114+
Used to specify a comma-separated list of module names that indicate the
115+
root modules to scan.
116+
All the root modules will be scanned, as well as any modules that they
117+
depend on.
118+
This includes dependencies on service implementations specified through
119+
the \f[V]uses\f[R] directive in a module\[aq]s \f[V]module-info\f[R]
120+
file.
121+
All modules found on the module path that provide an implementation of
122+
such a service will be scanned as well.
123+
.TP
124+
\f[V]--release\f[R] \f[I]version\f[R]
125+
Used to specify the Java SE release that specifies the set of restricted
126+
methods to scan for.
127+
For multi-release jar files, this option also indicates the version of
128+
class file that should be loaded from the jar.
129+
This option should be set to the version of the runtime under which the
130+
application is eventually intended to be run.
131+
If this flag is omitted, the version of \f[V]jnativescan\f[R] is used as
132+
release version, which is the same as the version of the JDK that the
133+
tool belongs to.
134+
.TP
135+
\f[V]--print-native-access\f[R]
136+
Print a comma-separated list of module names that use native
137+
functionalities, instead of the default tree structure.
138+
.TP
139+
\f[V]--help\f[R] or \f[V]-h\f[R]
140+
Prints out a full help message.
141+
.TP
142+
\f[V]--version\f[R]
143+
Prints out the abbreviated version string of the tool.
144+
.SH EXAMPLE OF \f[V]jnativescan\f[R] USE
145+
.PP
146+
\f[V]jnativescan\f[R] accepts a runtime configuration in the form of a
147+
class path, module path, set of root modules, and a target release
148+
version.
149+
For the class path, the tool will scan all jar files, including those
150+
found recursively through the \f[V]Class-Path\f[R] manifest attribute.
151+
For the module path, the tool scans all root modules specified through
152+
\f[V]--add-modules\f[R], and any (transitive) dependence of the root
153+
modules, including any modules that contain service implementations that
154+
are used by a scanned module.
155+
.PP
156+
By default, the tool prints out which jars, classes, and methods use
157+
native functionalities, in a tree-like structure.
158+
The following is an example output:
159+
.IP
160+
.nf
161+
\f[CB]
162+
$ jnativescan --class-path app.jar
163+
app.jar (ALL-UNNAMED):
164+
foo.Main:
165+
foo.Main::main(String[])void references restricted methods:
166+
java.lang.foreign.MemorySegment::reinterpret(long)MemorySegment
167+
foo.Main::nativeMethod()void is a native method declaration
168+
\f[R]
169+
.fi
170+
.PP
171+
\f[V]app.jar (ALL-UNNAMED)\f[R] is the path to the jar file, with the
172+
module name in parentheses behind it.
173+
Since in this case the jar file appears on the class path,
174+
\f[V]ALL-UNNAMED\f[R] is printed to indicate the unnamed module.
175+
The second line of the output, \f[V]foo.Main\f[R], indicates that
176+
methods using native functionalities were found in the
177+
\f[V]foo.Main\f[R] class.
178+
The next line:
179+
.IP
180+
.nf
181+
\f[CB]
182+
foo.Main::main(String[])void references restricted methods:
183+
\f[R]
184+
.fi
185+
.PP
186+
Indicates that the \f[V]main(String[])\f[R] method in the
187+
\f[V]foo.Main\f[R] class references a restricted method, which is listed
188+
on the following line as:
189+
.IP
190+
.nf
191+
\f[CB]
192+
java.lang.foreign.MemorySegment::reinterpret(long)MemorySegment
193+
\f[R]
194+
.fi
195+
.PP
196+
Lastly, the text:
197+
.IP
198+
.nf
199+
\f[CB]
200+
foo.Main::nativeMethod()void is a native method declaration
201+
\f[R]
202+
.fi
203+
.PP
204+
Indicates that the \f[V]foo.Main\f[R] class contains a declaration of a
205+
\f[V]native\f[R] method named \f[V]nativeMethod\f[R].
206+
.PP
207+
If we add \f[V]--print-native-access\f[R] to the example command line,
208+
we instead get a list of the names of modules that contain accesses to
209+
native functionalities:
210+
.IP
211+
.nf
212+
\f[CB]
213+
$ jnativescan --class-path app.jar --print-native-access
214+
ALL-UNNAMED
215+
\f[R]
216+
.fi
217+
.PP
218+
In this case the output consists of just \f[V]ALL-UNNAMED\f[R], which
219+
indicates a jar file on the class path, that is, in the unnamed module,
220+
contains an access to native functionalities.

‎test/jdk/tools/launcher/HelpFlagsTest.java

+1
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ private static class ToolHelpSpec {
141141
new ToolHelpSpec("jlink", 1, 1, 1, 0, 0, 0, 2), // -?, -h, --help
142142
new ToolHelpSpec("jmap", 1, 1, 1, 0, 1, 0, 1), // -?, -h, --help, -help accepted but not documented.
143143
new ToolHelpSpec("jmod", 1, 1, 1, 0, 1, 0, 2), // -?, -h, --help, -help accepted but not documented.
144+
new ToolHelpSpec("jnativescan", 1, 1, 1, 0, 1, 0, 1), // -?, -h, --help, -help accepted but not documented.
144145
new ToolHelpSpec("jps", 1, 1, 1, 0, 1, 1, 1), // -?, -h, --help -help, Documents -help
145146
new ToolHelpSpec("jrunscript", 1, 1, 1, 0, 1, 1, 7), // -?, -h, --help -help, Documents -help
146147
new ToolHelpSpec("jshell", 1, 1, 1, 0, 1, 0, 1), // -?, -h, --help, -help accepted but not documented.

‎test/langtools/TEST.groups

+4
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ langtools_jdeps = \
6363
tools/all \
6464
tools/jdeps
6565

66+
langtools_jnativescan = \
67+
tools/all \
68+
tools/jnativescan
69+
6670
langtools_slow = \
6771
jdk/internal/shellsupport/doc/FullJavadocHelperTest.java
6872

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*
2+
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
import java.io.IOException;
25+
import java.io.PrintWriter;
26+
import java.nio.file.Path;
27+
import java.util.Arrays;
28+
29+
import java.io.StringWriter;
30+
import java.util.spi.ToolProvider;
31+
32+
import jdk.test.lib.process.OutputAnalyzer;
33+
import jdk.test.lib.util.JarUtils;
34+
35+
public class JNativeScanTestBase {
36+
37+
public static final String MODULE_PATH = "mods";
38+
39+
private static final ToolProvider JNATIVESCAN_TOOL = ToolProvider.findFirst("jnativescan")
40+
.orElseThrow(() -> new RuntimeException("jnativescan tool not found"));
41+
42+
public static OutputAnalyzer jnativescan(String... args) {
43+
return run(JNATIVESCAN_TOOL, args);
44+
}
45+
46+
private static OutputAnalyzer run(ToolProvider tp, String[] commands) {
47+
int rc;
48+
StringWriter sw = new StringWriter();
49+
StringWriter esw = new StringWriter();
50+
51+
try (PrintWriter pw = new PrintWriter(sw);
52+
PrintWriter epw = new PrintWriter(esw)) {
53+
System.out.println("Running " + tp.name() + ", Command: " + Arrays.toString(commands));
54+
rc = tp.run(pw, epw, commands);
55+
}
56+
OutputAnalyzer output = new OutputAnalyzer(sw.toString(), esw.toString(), rc);
57+
output.outputTo(System.out);
58+
output.errorTo(System.err);
59+
return output;
60+
}
61+
62+
public static Path makeModularJar(String moduleName) throws IOException {
63+
Path jarPath = Path.of(MODULE_PATH, moduleName + ".jar");
64+
Path moduleRoot = moduleRoot(moduleName);
65+
JarUtils.createJarFile(jarPath, moduleRoot);
66+
return jarPath;
67+
}
68+
69+
public static Path moduleRoot(String name) {
70+
return Path.of(System.getProperty("test.module.path")).resolve(name);
71+
}
72+
73+
public static OutputAnalyzer assertSuccess(OutputAnalyzer output) {
74+
if (output.getExitValue() != 0) {
75+
throw new IllegalStateException("tool run failed");
76+
}
77+
return output;
78+
}
79+
80+
public static OutputAnalyzer assertFailure(OutputAnalyzer output) {
81+
if (output.getExitValue() == 0) {
82+
throw new IllegalStateException("tool run succeeded");
83+
}
84+
return output;
85+
}
86+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
/*
25+
* @test
26+
* @library /test/lib .. ./cases/modules
27+
* @build JNativeScanTestBase
28+
* cases.classpath.arrayref.App
29+
* @run junit TestArrayTypeRefs
30+
*/
31+
32+
import jdk.test.lib.util.JarUtils;
33+
import org.junit.jupiter.api.BeforeAll;
34+
import org.junit.jupiter.api.Test;
35+
36+
import java.io.IOException;
37+
import java.nio.file.Path;
38+
39+
public class TestArrayTypeRefs extends JNativeScanTestBase {
40+
41+
static Path ARRAY_REF;
42+
43+
@BeforeAll
44+
public static void before() throws IOException {
45+
ARRAY_REF = Path.of("arrayref.jar");
46+
Path testClasses = Path.of(System.getProperty("test.classes", ""));
47+
JarUtils.createJarFile(ARRAY_REF, testClasses, Path.of("arrayref", "App.class"));
48+
}
49+
50+
@Test
51+
public void testSingleJarClassPath() {
52+
assertSuccess(jnativescan("--class-path", ARRAY_REF.toString()))
53+
.stderrShouldBeEmpty()
54+
.stdoutShouldContain("<no restricted methods>");
55+
}
56+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
/*
2+
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
/*
25+
* @test
26+
* @library /test/lib .. ./cases/modules
27+
* @build JNativeScanTestBase
28+
* org.singlejar/* org.lib/* org.myapp/* org.service/*
29+
* cases.classpath.singlejar.main.Main
30+
* cases.classpath.lib.Lib
31+
* cases.classpath.app.App
32+
* cases.classpath.unnamed_package.UnnamedPackage
33+
* @run junit TestJNativeScan
34+
*/
35+
36+
import jdk.test.lib.process.OutputAnalyzer;
37+
import jdk.test.lib.util.JarUtils;
38+
import org.junit.jupiter.api.BeforeAll;
39+
import org.junit.jupiter.api.Test;
40+
41+
import java.io.File;
42+
import java.io.IOException;
43+
import java.nio.file.Path;
44+
import java.util.HashSet;
45+
import java.util.Set;
46+
import java.util.jar.Attributes;
47+
import java.util.jar.Manifest;
48+
49+
import static org.junit.jupiter.api.Assertions.assertTrue;
50+
51+
class TestJNativeScan extends JNativeScanTestBase {
52+
53+
static Path TEST_CLASSES;
54+
55+
static Path CLASS_PATH_APP;
56+
static Path SINGLE_JAR_CLASS_PATH;
57+
static Path SINGLE_JAR_MODULAR;
58+
static Path ORG_MYAPP;
59+
static Path ORG_LIB;
60+
static Path UNNAMED_PACKAGE_JAR;
61+
static Path LIB_JAR;
62+
63+
@BeforeAll
64+
public static void before() throws IOException {
65+
SINGLE_JAR_CLASS_PATH = Path.of("singleJar.jar");
66+
TEST_CLASSES = Path.of(System.getProperty("test.classes", ""));
67+
JarUtils.createJarFile(SINGLE_JAR_CLASS_PATH, TEST_CLASSES, Path.of("main", "Main.class"));
68+
69+
LIB_JAR = Path.of("lib.jar");
70+
JarUtils.createJarFile(LIB_JAR, TEST_CLASSES, Path.of("lib", "Lib.class"));
71+
Manifest manifest = new Manifest();
72+
Attributes mainAttrs = manifest.getMainAttributes();
73+
mainAttrs.put(Attributes.Name.MANIFEST_VERSION, "1.0"); // need version or other attributes will be ignored
74+
mainAttrs.putValue("Class-Path", "lib.jar non-existent.jar");
75+
CLASS_PATH_APP = Path.of("app.jar");
76+
JarUtils.createJarFile(CLASS_PATH_APP, manifest, TEST_CLASSES, Path.of("app", "App.class"));
77+
78+
SINGLE_JAR_MODULAR = makeModularJar("org.singlejar");
79+
ORG_MYAPP = makeModularJar("org.myapp");
80+
ORG_LIB = makeModularJar("org.lib");
81+
makeModularJar("org.service");
82+
83+
UNNAMED_PACKAGE_JAR = Path.of("unnamed_package.jar");
84+
JarUtils.createJarFile(UNNAMED_PACKAGE_JAR, TEST_CLASSES, Path.of("UnnamedPackage.class"));
85+
}
86+
87+
@Test
88+
public void testSingleJarClassPath() {
89+
assertSuccess(jnativescan("--class-path", SINGLE_JAR_CLASS_PATH.toString()))
90+
.stderrShouldBeEmpty()
91+
.stdoutShouldContain("ALL-UNNAMED")
92+
.stdoutShouldContain("main.Main")
93+
.stdoutShouldContain("main.Main::m()void is a native method declaration")
94+
.stdoutShouldContain("main.Main::main(String[])void references restricted methods")
95+
.stdoutShouldContain("java.lang.foreign.MemorySegment::reinterpret(long)MemorySegment");
96+
}
97+
98+
@Test
99+
public void testSingleJarModulePath() {
100+
assertSuccess(jnativescan("--module-path", MODULE_PATH, "--add-modules", "org.singlejar"))
101+
.stderrShouldBeEmpty()
102+
.stdoutShouldContain("org.singlejar")
103+
.stdoutShouldContain("org.singlejar.main.Main")
104+
.stdoutShouldContain("org.singlejar.main.Main::m()void is a native method declaration")
105+
.stdoutShouldContain("org.singlejar.main.Main::main(String[])void references restricted methods")
106+
.stdoutShouldContain("java.lang.foreign.MemorySegment::reinterpret(long)MemorySegment");
107+
}
108+
109+
@Test
110+
public void testWithDepModule() {
111+
assertSuccess(jnativescan("--module-path", MODULE_PATH, "--add-modules", "org.myapp"))
112+
.stderrShouldBeEmpty()
113+
.stdoutShouldContain("org.lib")
114+
.stdoutShouldContain("org.lib.Lib")
115+
.stdoutShouldContain("org.lib.Lib::m()void is a native method declaration")
116+
.stdoutShouldContain("org.lib.Lib::doIt()void references restricted methods")
117+
.stdoutShouldContain("java.lang.foreign.MemorySegment::reinterpret(long)MemorySegment")
118+
.stdoutShouldContain("org.service")
119+
.stdoutShouldContain("org.service.ServiceImpl")
120+
.stdoutShouldContain("org.service.ServiceImpl::m()void is a native method declaration")
121+
.stdoutShouldContain("org.service.ServiceImpl::doIt()void references restricted methods")
122+
.stdoutShouldContain("java.lang.foreign.MemorySegment::reinterpret(long)MemorySegment");
123+
}
124+
125+
@Test
126+
public void testAllModulePath() {
127+
assertSuccess(jnativescan("--module-path", MODULE_PATH, "--add-modules", "ALL-MODULE-PATH"))
128+
.stderrShouldBeEmpty()
129+
.stdoutShouldContain("org.singlejar")
130+
.stdoutShouldContain("org.lib")
131+
.stdoutShouldContain("org.service");
132+
}
133+
134+
@Test
135+
public void testClassPathAttribute() {
136+
assertSuccess(jnativescan("--class-path", CLASS_PATH_APP.toString()))
137+
.stderrShouldBeEmpty()
138+
.stdoutShouldContain("ALL-UNNAMED")
139+
.stdoutShouldContain("lib.Lib")
140+
.stdoutShouldContain("lib.Lib::m()void is a native method declaration")
141+
.stdoutShouldContain("lib.Lib::doIt()void references restricted methods")
142+
.stdoutShouldContain("java.lang.foreign.MemorySegment::reinterpret(long)MemorySegment");
143+
}
144+
145+
@Test
146+
public void testInvalidRelease() {
147+
assertFailure(jnativescan("--module-path", MODULE_PATH, "--add-modules", "ALL-MODULE-PATH", "--release", "asdf"))
148+
.stderrShouldContain("Invalid release");
149+
}
150+
151+
@Test
152+
public void testReleaseNotSupported() {
153+
assertFailure(jnativescan("--module-path", MODULE_PATH, "--add-modules", "ALL-MODULE-PATH", "--release", "9999999"))
154+
.stderrShouldContain("Release: 9999999 not supported");
155+
}
156+
157+
@Test
158+
public void testFileDoesNotExist() {
159+
assertFailure(jnativescan("--class-path", "non-existent.jar"))
160+
.stderrShouldContain("Path does not appear to be a jar file, or directory containing classes");
161+
}
162+
163+
@Test
164+
public void testModuleNotAJarFile() {
165+
String modulePath = moduleRoot("org.myapp").toString() + File.pathSeparator + ORG_LIB.toString();
166+
assertSuccess(jnativescan("--module-path", modulePath,
167+
"--add-modules", "ALL-MODULE-PATH"))
168+
.stdoutShouldContain("lib.Lib")
169+
.stdoutShouldContain("lib.Lib::m()void is a native method declaration")
170+
.stdoutShouldContain("lib.Lib::doIt()void references restricted methods")
171+
.stdoutShouldContain("java.lang.foreign.MemorySegment::reinterpret(long)MemorySegment");
172+
}
173+
174+
@Test
175+
public void testPrintNativeAccess() {
176+
assertSuccess(jnativescan("--module-path", MODULE_PATH,
177+
"-add-modules", "org.singlejar,org.myapp",
178+
"--print-native-access"))
179+
.stdoutShouldMatch("org.lib,org.service,org.singlejar");
180+
}
181+
182+
@Test
183+
public void testNoDuplicateNames() {
184+
String classPath = SINGLE_JAR_CLASS_PATH + File.pathSeparator + CLASS_PATH_APP;
185+
OutputAnalyzer output = assertSuccess(jnativescan("--class-path", classPath, "--print-native-access"));
186+
String[] moduleNames = output.getStdout().split(",");
187+
Set<String> names = new HashSet<>();
188+
for (String name : moduleNames) {
189+
assertTrue(names.add(name.strip()));
190+
}
191+
}
192+
193+
@Test
194+
public void testUnnamedPackage() {
195+
assertSuccess(jnativescan("--class-path", UNNAMED_PACKAGE_JAR.toString()))
196+
.stderrShouldBeEmpty()
197+
.stdoutShouldContain("ALL-UNNAMED")
198+
.stdoutShouldNotContain(".UnnamedPackage")
199+
.stdoutShouldContain("UnnamedPackage")
200+
.stdoutShouldContain("UnnamedPackage::m()void is a native method declaration")
201+
.stdoutShouldContain("UnnamedPackage::main(String[])void references restricted methods")
202+
.stdoutShouldContain("java.lang.foreign.MemorySegment::reinterpret(long)MemorySegment");
203+
}
204+
205+
@Test
206+
public void testPositionalArguments() {
207+
assertFailure(jnativescan("foo"))
208+
.stdoutShouldBeEmpty()
209+
.stderrShouldContain("jnativescan does not accept positional arguments");
210+
}
211+
212+
@Test
213+
public void testMissingRootModules() {
214+
assertFailure(jnativescan("--module-path", MODULE_PATH))
215+
.stdoutShouldBeEmpty()
216+
.stderrShouldContain("Missing required option(s) [add-modules]");
217+
}
218+
219+
@Test
220+
public void testClassPathDirectory() {
221+
assertSuccess(jnativescan("--class-path", TEST_CLASSES.toString()))
222+
.stderrShouldBeEmpty()
223+
.stdoutShouldContain("ALL-UNNAMED")
224+
.stdoutShouldContain("UnnamedPackage")
225+
.stdoutShouldContain("UnnamedPackage::m()void is a native method declaration")
226+
.stdoutShouldContain("UnnamedPackage::main(String[])void references restricted methods")
227+
.stdoutShouldContain("main.Main")
228+
.stdoutShouldContain("main.Main::m()void is a native method declaration")
229+
.stdoutShouldContain("main.Main::main(String[])void references restricted methods")
230+
.stdoutShouldContain("lib.Lib")
231+
.stdoutShouldContain("lib.Lib::m()void is a native method declaration")
232+
.stdoutShouldContain("lib.Lib::doIt()void references restricted methods")
233+
.stdoutShouldContain("java.lang.foreign.MemorySegment::reinterpret(long)MemorySegment");
234+
}
235+
236+
@Test
237+
public void testMultipleClassPathJars() {
238+
// make sure all of these are reported, even when they are all in the ALL-UNNAMED module
239+
String classPath = UNNAMED_PACKAGE_JAR
240+
+ File.pathSeparator + SINGLE_JAR_CLASS_PATH
241+
+ File.pathSeparator + LIB_JAR;
242+
assertSuccess(jnativescan("--class-path", classPath))
243+
.stderrShouldBeEmpty()
244+
.stdoutShouldContain("ALL-UNNAMED")
245+
.stdoutShouldContain("UnnamedPackage")
246+
.stdoutShouldContain(UNNAMED_PACKAGE_JAR.toString())
247+
.stdoutShouldContain("lib.Lib")
248+
.stdoutShouldContain(LIB_JAR.toString())
249+
.stdoutShouldContain("main.Main")
250+
.stdoutShouldContain(SINGLE_JAR_CLASS_PATH.toString());
251+
}
252+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
/*
25+
* @test
26+
* @library /test/lib .. ./cases/modules
27+
* @build JNativeScanTestBase
28+
* @compile --release 20 cases/classpath/missingsystem/App.java
29+
* @run junit TestMissingSystemClass
30+
*/
31+
32+
import jdk.test.lib.util.JarUtils;
33+
import org.junit.jupiter.api.BeforeAll;
34+
import org.junit.jupiter.api.Test;
35+
36+
import java.io.IOException;
37+
import java.nio.file.Path;
38+
39+
public class TestMissingSystemClass extends JNativeScanTestBase {
40+
41+
static Path MISSING_SYSTEM;
42+
43+
@BeforeAll
44+
public static void before() throws IOException {
45+
MISSING_SYSTEM = Path.of("missingsystem.jar");
46+
Path testClasses = Path.of(System.getProperty("test.classes", ""));
47+
JarUtils.createJarFile(MISSING_SYSTEM, testClasses, Path.of("missingsystem", "App.class"));
48+
}
49+
50+
@Test
51+
public void testSingleJarClassPath() {
52+
assertFailure(jnativescan("--class-path", MISSING_SYSTEM.toString(), "--release", "21"))
53+
.stdoutShouldBeEmpty()
54+
.stderrShouldContain("Error while processing method")
55+
.stderrShouldContain("missingsystem.App::main(String[])void")
56+
.stderrShouldContain("CAUSED BY:")
57+
.stderrShouldContain("System class can not be found")
58+
.stderrShouldContain("java.lang.Compiler");
59+
}
60+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
/*
25+
* @test
26+
* @library /test/lib .. ./cases/modules
27+
* @build JNativeScanTestBase
28+
* cases.classpath.subclassref.App
29+
* @run junit TestSubclassRefs
30+
*/
31+
32+
import jdk.test.lib.util.JarUtils;
33+
import org.junit.jupiter.api.BeforeAll;
34+
import org.junit.jupiter.api.Test;
35+
36+
import java.io.IOException;
37+
import java.nio.file.Path;
38+
39+
public class TestSubclassRefs extends JNativeScanTestBase {
40+
41+
static Path SUBCLASS_REF;
42+
43+
@BeforeAll
44+
public static void before() throws IOException {
45+
SUBCLASS_REF = Path.of("subclassref.jar");
46+
Path testClasses = Path.of(System.getProperty("test.classes", ""));
47+
JarUtils.createJarFile(SUBCLASS_REF, testClasses, Path.of("subclassref", "App.class"));
48+
}
49+
50+
@Test
51+
public void testSingleJarClassPath() {
52+
assertSuccess(jnativescan("--class-path", SUBCLASS_REF.toString()))
53+
.stderrShouldBeEmpty()
54+
.stdoutShouldContain("<no restricted methods>");
55+
}
56+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
package app;
24+
25+
import lib.Lib;
26+
27+
public class App {
28+
public static void main(String[] args) {
29+
Lib.doIt();
30+
}
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
package arrayref;
24+
25+
public class App {
26+
public static void main(String[] args) {
27+
// reference an array method to see that
28+
// RestrictedMethodFinder correctly handles
29+
// references to array methods
30+
args.clone();
31+
}
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
package lib;
24+
25+
import java.lang.foreign.MemorySegment;
26+
27+
public class Lib {
28+
public static void doIt() {
29+
MemorySegment.ofAddress(1234).reinterpret(10);
30+
}
31+
32+
private static native void m();
33+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
package missingsystem;
24+
25+
public class App {
26+
public static void main(String[] args) {
27+
// this class was present in Java 20, but removed in 21
28+
// if we compile with --release 20, but run jnativescan
29+
// with --release 21, we should get an error
30+
java.lang.Compiler.enable();
31+
}
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
package main;
25+
26+
import java.lang.foreign.*;
27+
28+
public class Main {
29+
public static void main(String[] args) {
30+
MemorySegment.ofAddress(1234).reinterpret(10);
31+
}
32+
33+
private static native void m();
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
package subclassref;
24+
25+
import java.util.List;
26+
27+
public class App {
28+
public static void main(String[] args) {
29+
List<String> l = List.of(args);
30+
l.stream(); // List does not declare stream()
31+
}
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
import java.lang.foreign.*;
25+
26+
public class UnnamedPackage {
27+
public static void main(String[] args) {
28+
MemorySegment.ofAddress(1234).reinterpret(10);
29+
}
30+
31+
private static native void m();
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
module org.lib {
25+
exports org.lib;
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
package org.lib;
25+
26+
import java.lang.foreign.*;
27+
28+
public class Lib {
29+
public static void doIt() {
30+
MemorySegment.ofAddress(1234).reinterpret(10);
31+
}
32+
33+
private static native void m();
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
package org.lib;
24+
25+
public interface Service {
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
module org.myapp {
25+
requires org.lib;
26+
27+
uses org.lib.Service;
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
package org.myapp.main;
25+
26+
import org.lib.Lib;
27+
28+
public class Main {
29+
public static void main(String[] args) {
30+
Lib.doIt();
31+
}
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
module org.service {
24+
requires org.lib;
25+
26+
provides org.lib.Service with org.service.ServiceImpl;
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
package org.service;
24+
25+
import org.lib.Service;
26+
27+
import java.lang.foreign.MemorySegment;
28+
29+
public class ServiceImpl implements Service {
30+
public void doIt() {
31+
MemorySegment.ofAddress(1234).reinterpret(10);
32+
}
33+
34+
private native void m();
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
module org.singlejar {
24+
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
package org.singlejar.main;
25+
26+
import java.lang.foreign.*;
27+
28+
public class Main {
29+
public static void main(String[] args) {
30+
MemorySegment.ofAddress(1234).reinterpret(10);
31+
}
32+
33+
private static native void m();
34+
}

0 commit comments

Comments
 (0)
Please sign in to comment.