mirror of
https://github.com/openjdk/jdk.git
synced 2026-03-14 01:43:13 +00:00
8353917: jnativescan: Simplify ClassResolver
Reviewed-by: mcimadamore
This commit is contained in:
parent
36069f6efa
commit
8bb0ca4971
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -27,6 +27,8 @@ package com.sun.tools.jnativescan;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.lang.classfile.ClassFile;
|
||||
import java.lang.classfile.ClassModel;
|
||||
import java.lang.module.ModuleReader;
|
||||
import java.lang.module.ModuleReference;
|
||||
import java.net.URI;
|
||||
@ -40,7 +42,12 @@ sealed interface ClassFileSource {
|
||||
String moduleName();
|
||||
Path path();
|
||||
|
||||
Stream<byte[]> classFiles(Runtime.Version version) throws IOException;
|
||||
Stream<byte[]> classFiles() throws IOException;
|
||||
|
||||
default Stream<ClassModel> classModels() throws IOException {
|
||||
ClassFile classFile = ClassFile.of();
|
||||
return classFiles().map(classFile::parse);
|
||||
}
|
||||
|
||||
record Module(ModuleReference reference) implements ClassFileSource {
|
||||
@Override
|
||||
@ -55,7 +62,7 @@ sealed interface ClassFileSource {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<byte[]> classFiles(Runtime.Version version) throws IOException {
|
||||
public Stream<byte[]> classFiles() throws IOException {
|
||||
ModuleReader reader = reference().open();
|
||||
return reader.list()
|
||||
.filter(resourceName -> resourceName.endsWith(".class"))
|
||||
@ -75,14 +82,14 @@ sealed interface ClassFileSource {
|
||||
}
|
||||
}
|
||||
|
||||
record ClassPathJar(Path path) implements ClassFileSource {
|
||||
record ClassPathJar(Path path, Runtime.Version version) implements ClassFileSource {
|
||||
@Override
|
||||
public String moduleName() {
|
||||
return "ALL-UNNAMED";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<byte[]> classFiles(Runtime.Version version) throws IOException {
|
||||
public Stream<byte[]> classFiles() throws IOException {
|
||||
JarFile jf = new JarFile(path().toFile(), false, ZipFile.OPEN_READ, version);
|
||||
return jf.versionedStream()
|
||||
.filter(je -> je.getName().endsWith(".class"))
|
||||
@ -109,7 +116,7 @@ sealed interface ClassFileSource {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<byte[]> classFiles(Runtime.Version version) throws IOException {
|
||||
public Stream<byte[]> classFiles() throws IOException {
|
||||
return Files.walk(path)
|
||||
.filter(file -> Files.isRegularFile(file) && file.toString().endsWith(".class"))
|
||||
.map(file -> {
|
||||
|
||||
@ -1,163 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package com.sun.tools.jnativescan;
|
||||
|
||||
import com.sun.tools.javac.platform.PlatformDescription;
|
||||
import com.sun.tools.javac.platform.PlatformProvider;
|
||||
|
||||
import javax.tools.JavaFileManager;
|
||||
import javax.tools.JavaFileObject;
|
||||
import javax.tools.StandardLocation;
|
||||
import java.io.IOException;
|
||||
import java.lang.classfile.ClassFile;
|
||||
import java.lang.classfile.ClassModel;
|
||||
import java.lang.constant.ClassDesc;
|
||||
import java.lang.module.ModuleDescriptor;
|
||||
import java.util.*;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
abstract class ClassResolver implements AutoCloseable {
|
||||
|
||||
static ClassResolver forClassFileSources(List<ClassFileSource> sources, Runtime.Version version) throws IOException {
|
||||
Map<ClassDesc, Info> classMap = new HashMap<>();
|
||||
for (ClassFileSource source : sources) {
|
||||
try (Stream<byte[]> classFiles = source.classFiles(version)) {
|
||||
classFiles.forEach(bytes -> {
|
||||
ClassModel model = ClassFile.of().parse(bytes);
|
||||
ClassDesc desc = model.thisClass().asSymbol();
|
||||
classMap.put(desc, new Info(source, model));
|
||||
});
|
||||
}
|
||||
}
|
||||
return new SimpleClassResolver(classMap);
|
||||
}
|
||||
|
||||
static ClassResolver forSystemModules(Runtime.Version version) {
|
||||
String platformName = String.valueOf(version.feature());
|
||||
PlatformProvider platformProvider = ServiceLoader.load(PlatformProvider.class).findFirst().orElseThrow();
|
||||
PlatformDescription platform;
|
||||
try {
|
||||
platform = platformProvider.getPlatform(platformName, null);
|
||||
} catch (PlatformProvider.PlatformNotSupported e) {
|
||||
throw new JNativeScanFatalError("Release: " + platformName + " not supported", e);
|
||||
}
|
||||
JavaFileManager fm = platform.getFileManager();
|
||||
return new SystemModuleClassResolver(fm);
|
||||
}
|
||||
|
||||
record Info(ClassFileSource source, ClassModel model) {}
|
||||
|
||||
public abstract void forEach(BiConsumer<ClassDesc, ClassResolver.Info> action);
|
||||
public abstract Optional<ClassResolver.Info> lookup(ClassDesc desc);
|
||||
|
||||
@Override
|
||||
public abstract void close() throws IOException;
|
||||
|
||||
private static class SimpleClassResolver extends ClassResolver {
|
||||
|
||||
private final Map<ClassDesc, ClassResolver.Info> classMap;
|
||||
|
||||
public SimpleClassResolver(Map<ClassDesc, Info> classMap) {
|
||||
this.classMap = classMap;
|
||||
}
|
||||
|
||||
public void forEach(BiConsumer<ClassDesc, ClassResolver.Info> action) {
|
||||
classMap.forEach(action);
|
||||
}
|
||||
|
||||
public Optional<ClassResolver.Info> lookup(ClassDesc desc) {
|
||||
return Optional.ofNullable(classMap.get(desc));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {}
|
||||
}
|
||||
|
||||
private static class SystemModuleClassResolver extends ClassResolver {
|
||||
|
||||
private final JavaFileManager platformFileManager;
|
||||
private final Map<String, String> packageToSystemModule;
|
||||
private final Map<ClassDesc, Info> cache = new HashMap<>();
|
||||
|
||||
public SystemModuleClassResolver(JavaFileManager platformFileManager) {
|
||||
this.platformFileManager = platformFileManager;
|
||||
this.packageToSystemModule = packageToSystemModule(platformFileManager);
|
||||
}
|
||||
|
||||
private static Map<String, String> packageToSystemModule(JavaFileManager platformFileManager) {
|
||||
try {
|
||||
Set<JavaFileManager.Location> locations = platformFileManager.listLocationsForModules(
|
||||
StandardLocation.SYSTEM_MODULES).iterator().next();
|
||||
|
||||
Map<String, String> result = new HashMap<>();
|
||||
for (JavaFileManager.Location loc : locations) {
|
||||
JavaFileObject jfo = platformFileManager.getJavaFileForInput(loc, "module-info", JavaFileObject.Kind.CLASS);
|
||||
ModuleDescriptor descriptor = ModuleDescriptor.read(jfo.openInputStream());
|
||||
for (ModuleDescriptor.Exports export : descriptor.exports()) {
|
||||
if (!export.isQualified()) {
|
||||
result.put(export.source(), descriptor.name());
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEach(BiConsumer<ClassDesc, Info> action) {
|
||||
throw new UnsupportedOperationException("NYI");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Info> lookup(ClassDesc desc) {
|
||||
return Optional.ofNullable(cache.computeIfAbsent(desc, _ -> {
|
||||
String qualName = JNativeScanTask.qualName(desc);
|
||||
String moduleName = packageToSystemModule.get(desc.packageName());
|
||||
if (moduleName != null) {
|
||||
try {
|
||||
JavaFileManager.Location loc = platformFileManager.getLocationForModule(StandardLocation.SYSTEM_MODULES, moduleName);
|
||||
JavaFileObject jfo = platformFileManager.getJavaFileForInput(loc, qualName, JavaFileObject.Kind.CLASS);
|
||||
if (jfo == null) {
|
||||
throw new JNativeScanFatalError("System class can not be found: " + qualName);
|
||||
}
|
||||
ClassModel model = ClassFile.of().parse(jfo.openInputStream().readAllBytes());
|
||||
return new Info(null, model);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
platformFileManager.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -26,17 +26,18 @@ package com.sun.tools.jnativescan;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.lang.classfile.ClassModel;
|
||||
import java.lang.constant.ClassDesc;
|
||||
import java.lang.module.Configuration;
|
||||
import java.lang.module.ModuleFinder;
|
||||
import java.lang.module.ResolvedModule;
|
||||
import java.net.URI;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.jar.Manifest;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
class JNativeScanTask {
|
||||
@ -76,11 +77,26 @@ class JNativeScanTask {
|
||||
Set<String> errors = new LinkedHashSet<>();
|
||||
Diagnostics diagnostics = (context, error) ->
|
||||
errors.add("Error while processing method: " + context + ": " + error.getMessage());
|
||||
SortedMap<ClassFileSource, SortedMap<ClassDesc, List<RestrictedUse>>> allRestrictedMethods;
|
||||
try(ClassResolver classesToScan = ClassResolver.forClassFileSources(toScan, version);
|
||||
ClassResolver systemClassResolver = ClassResolver.forSystemModules(version)) {
|
||||
NativeMethodFinder finder = NativeMethodFinder.create(diagnostics, classesToScan, systemClassResolver);
|
||||
allRestrictedMethods = finder.findAll();
|
||||
SortedMap<ClassFileSource, SortedMap<ClassDesc, List<RestrictedUse>>> allRestrictedMethods
|
||||
= new TreeMap<>(Comparator.comparing(ClassFileSource::path));
|
||||
try(SystemClassResolver systemClassResolver = SystemClassResolver.forRuntimeVersion(version)) {
|
||||
NativeMethodFinder finder = NativeMethodFinder.create(diagnostics, systemClassResolver);
|
||||
|
||||
for (ClassFileSource source : toScan) {
|
||||
SortedMap<ClassDesc, List<RestrictedUse>> perClass
|
||||
= new TreeMap<>(Comparator.comparing(JNativeScanTask::qualName));
|
||||
try (Stream<ClassModel> stream = source.classModels()) {
|
||||
stream.forEach(classModel -> {
|
||||
List<RestrictedUse> restrictedUses = finder.find(classModel);
|
||||
if (!restrictedUses.isEmpty()) {
|
||||
perClass.put(classModel.thisClass().asSymbol(), restrictedUses);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (!perClass.isEmpty()) {
|
||||
allRestrictedMethods.put(source, perClass);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
@ -115,7 +131,7 @@ class JNativeScanTask {
|
||||
jarsToScan.offer(otherJar);
|
||||
}
|
||||
}
|
||||
result.add(new ClassFileSource.ClassPathJar(jar));
|
||||
result.add(new ClassFileSource.ClassPathJar(jar, version));
|
||||
}
|
||||
} else if (Files.isDirectory(path)) {
|
||||
result.add(new ClassFileSource.ClassPathDirectory(path));
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -33,7 +33,6 @@ import java.lang.classfile.Attributes;
|
||||
import java.lang.classfile.ClassModel;
|
||||
import java.lang.classfile.MethodModel;
|
||||
import java.lang.classfile.instruction.InvokeInstruction;
|
||||
import java.lang.constant.ClassDesc;
|
||||
import java.lang.constant.MethodTypeDesc;
|
||||
import java.lang.reflect.AccessFlag;
|
||||
import java.util.*;
|
||||
@ -46,61 +45,46 @@ class NativeMethodFinder {
|
||||
|
||||
private final Map<MethodRef, Boolean> cache = new HashMap<>();
|
||||
private final Diagnostics diagnostics;
|
||||
private final ClassResolver classesToScan;
|
||||
private final ClassResolver systemClassResolver;
|
||||
private final SystemClassResolver systemClassResolver;
|
||||
|
||||
private NativeMethodFinder(Diagnostics diagnostics, ClassResolver classesToScan, ClassResolver systemClassResolver) {
|
||||
private NativeMethodFinder(Diagnostics diagnostics, SystemClassResolver systemClassResolver) {
|
||||
this.diagnostics = diagnostics;
|
||||
this.classesToScan = classesToScan;
|
||||
this.systemClassResolver = systemClassResolver;
|
||||
}
|
||||
|
||||
public static NativeMethodFinder create(Diagnostics diagnostics, ClassResolver classesToScan,
|
||||
ClassResolver systemClassResolver) throws JNativeScanFatalError, IOException {
|
||||
return new NativeMethodFinder(diagnostics, classesToScan, systemClassResolver);
|
||||
public static NativeMethodFinder create(Diagnostics diagnostics, SystemClassResolver systemClassResolver) throws JNativeScanFatalError, IOException {
|
||||
return new NativeMethodFinder(diagnostics, systemClassResolver);
|
||||
}
|
||||
|
||||
public SortedMap<ClassFileSource, SortedMap<ClassDesc, List<RestrictedUse>>> findAll() throws JNativeScanFatalError {
|
||||
SortedMap<ClassFileSource, SortedMap<ClassDesc, List<RestrictedUse>>> restrictedMethods
|
||||
= new TreeMap<>(Comparator.comparing(ClassFileSource::path));
|
||||
classesToScan.forEach((_, info) -> {
|
||||
ClassModel classModel = info.model();
|
||||
List<RestrictedUse> perClass = new ArrayList<>();
|
||||
classModel.methods().forEach(methodModel -> {
|
||||
public List<RestrictedUse> find(ClassModel model) throws JNativeScanFatalError {
|
||||
return model.methods().stream().<RestrictedUse>mapMulti((methodModel, sink) -> {
|
||||
if (methodModel.flags().has(AccessFlag.NATIVE)) {
|
||||
perClass.add(new NativeMethodDecl(MethodRef.ofModel(methodModel)));
|
||||
sink.accept(new NativeMethodDecl(MethodRef.ofModel(methodModel)));
|
||||
} else {
|
||||
SortedSet<MethodRef> perMethod = new TreeSet<>(Comparator.comparing(MethodRef::toString));
|
||||
methodModel.code().ifPresent(code -> {
|
||||
code.forEach(e -> {
|
||||
switch (e) {
|
||||
case InvokeInstruction invoke -> {
|
||||
MethodRef ref = MethodRef.ofInvokeInstruction(invoke);
|
||||
try {
|
||||
if (isRestrictedMethod(ref)) {
|
||||
perMethod.add(ref);
|
||||
}
|
||||
} catch (JNativeScanFatalError ex) {
|
||||
diagnostics.error(MethodRef.ofModel(methodModel), ex);
|
||||
}
|
||||
}
|
||||
default -> {
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
SortedSet<MethodRef> perMethod = findRestrictedMethodInvocations(methodModel);
|
||||
if (!perMethod.isEmpty()) {
|
||||
perClass.add(new RestrictedMethodRefs(MethodRef.ofModel(methodModel), perMethod));
|
||||
sink.accept(new RestrictedMethodRefs(MethodRef.ofModel(methodModel), perMethod));
|
||||
}
|
||||
}
|
||||
});
|
||||
if (!perClass.isEmpty()) {
|
||||
restrictedMethods.computeIfAbsent(info.source(),
|
||||
_ -> new TreeMap<>(Comparator.comparing(JNativeScanTask::qualName)))
|
||||
.put(classModel.thisClass().asSymbol(), perClass);
|
||||
})
|
||||
.toList();
|
||||
}
|
||||
|
||||
private SortedSet<MethodRef> findRestrictedMethodInvocations(MethodModel methodModel) {
|
||||
SortedSet<MethodRef> perMethod = new TreeSet<>(Comparator.comparing(MethodRef::toString));
|
||||
methodModel.code().ifPresent(code -> code.forEach(e -> {
|
||||
if (e instanceof InvokeInstruction invoke) {
|
||||
MethodRef ref = MethodRef.ofInvokeInstruction(invoke);
|
||||
try {
|
||||
if (isRestrictedMethod(ref)) {
|
||||
perMethod.add(ref);
|
||||
}
|
||||
} catch (JNativeScanFatalError ex) {
|
||||
diagnostics.error(MethodRef.ofModel(methodModel), ex);
|
||||
}
|
||||
}
|
||||
});
|
||||
return restrictedMethods;
|
||||
}));
|
||||
return perMethod;
|
||||
}
|
||||
|
||||
private boolean isRestrictedMethod(MethodRef ref) throws JNativeScanFatalError {
|
||||
@ -109,11 +93,14 @@ class NativeMethodFinder {
|
||||
// no restricted methods in arrays atm, and we can't look them up since they have no class file
|
||||
return false;
|
||||
}
|
||||
Optional<ClassResolver.Info> info = systemClassResolver.lookup(methodRef.owner());
|
||||
if (!info.isPresent()) {
|
||||
Optional<ClassModel> modelOpt = systemClassResolver.lookup(methodRef.owner());
|
||||
if (!modelOpt.isPresent()) {
|
||||
// The target class is not in a system module. Since only system modules
|
||||
// contain classes with restricted methods, we can safely assume that
|
||||
// the target method is not restricted.
|
||||
return false;
|
||||
}
|
||||
ClassModel classModel = info.get().model();
|
||||
ClassModel classModel = modelOpt.get();
|
||||
Optional<MethodModel> methodModel = findMethod(classModel, methodRef.name(), methodRef.type());
|
||||
if (!methodModel.isPresent()) {
|
||||
// If we are here, the method was referenced through a subclass of the class containing the actual
|
||||
|
||||
@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package com.sun.tools.jnativescan;
|
||||
|
||||
import com.sun.tools.javac.platform.PlatformDescription;
|
||||
import com.sun.tools.javac.platform.PlatformProvider;
|
||||
|
||||
import javax.tools.JavaFileManager;
|
||||
import javax.tools.JavaFileObject;
|
||||
import javax.tools.StandardLocation;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.classfile.ClassFile;
|
||||
import java.lang.classfile.ClassModel;
|
||||
import java.lang.constant.ClassDesc;
|
||||
import java.lang.module.ModuleDescriptor;
|
||||
import java.util.*;
|
||||
|
||||
class SystemClassResolver implements AutoCloseable {
|
||||
|
||||
private final JavaFileManager platformFileManager;
|
||||
private final Map<String, String> packageToSystemModule;
|
||||
private final Map<ClassDesc, ClassModel> cache = new HashMap<>();
|
||||
|
||||
private SystemClassResolver(JavaFileManager platformFileManager) {
|
||||
this.platformFileManager = platformFileManager;
|
||||
this.packageToSystemModule = packageToSystemModule(platformFileManager);
|
||||
}
|
||||
|
||||
public static SystemClassResolver forRuntimeVersion(Runtime.Version version) {
|
||||
String platformName = String.valueOf(version.feature());
|
||||
PlatformProvider platformProvider = ServiceLoader.load(PlatformProvider.class).findFirst().orElseThrow();
|
||||
PlatformDescription platform;
|
||||
try {
|
||||
platform = platformProvider.getPlatform(platformName, null);
|
||||
} catch (PlatformProvider.PlatformNotSupported e) {
|
||||
throw new JNativeScanFatalError("Release: " + platformName + " not supported", e);
|
||||
}
|
||||
JavaFileManager fm = platform.getFileManager();
|
||||
return new SystemClassResolver(fm);
|
||||
}
|
||||
|
||||
private static Map<String, String> packageToSystemModule(JavaFileManager platformFileManager) {
|
||||
try {
|
||||
Set<JavaFileManager.Location> locations = platformFileManager.listLocationsForModules(
|
||||
StandardLocation.SYSTEM_MODULES).iterator().next();
|
||||
|
||||
Map<String, String> result = new HashMap<>();
|
||||
for (JavaFileManager.Location loc : locations) {
|
||||
JavaFileObject jfo = platformFileManager.getJavaFileForInput(loc, "module-info", JavaFileObject.Kind.CLASS);
|
||||
ModuleDescriptor descriptor = ModuleDescriptor.read(jfo.openInputStream());
|
||||
for (ModuleDescriptor.Exports export : descriptor.exports()) {
|
||||
if (!export.isQualified()) {
|
||||
result.put(export.source(), descriptor.name());
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public Optional<ClassModel> lookup(ClassDesc desc) {
|
||||
return Optional.ofNullable(cache.computeIfAbsent(desc, _ -> {
|
||||
String qualName = JNativeScanTask.qualName(desc);
|
||||
String moduleName = packageToSystemModule.get(desc.packageName());
|
||||
if (moduleName != null) {
|
||||
try {
|
||||
JavaFileManager.Location loc = platformFileManager.getLocationForModule(StandardLocation.SYSTEM_MODULES, moduleName);
|
||||
JavaFileObject jfo = platformFileManager.getJavaFileForInput(loc, qualName, JavaFileObject.Kind.CLASS);
|
||||
if (jfo == null) {
|
||||
throw new JNativeScanFatalError("System class can not be found: " + qualName);
|
||||
}
|
||||
try (InputStream inputStream = jfo.openInputStream()) {
|
||||
return ClassFile.of().parse(inputStream.readAllBytes());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
platformFileManager.close();
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user