Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -94,15 +94,20 @@ private class NativeImageDebugCodeInfo implements DebugCodeInfo {
private Path fullFilePath;
private final Path cachePath;

@SuppressWarnings("try")
NativeImageDebugCodeInfo(HostedMethod method, CompilationResult compilation) {
this.method = method;
HostedType declaringClass = method.getDeclaringClass();
Class<?> clazz = declaringClass.getJavaClass();
this.javaType = declaringClass.getWrapped();
this.compilation = compilation;
SourceManager sourceManager = ImageSingletons.lookup(SourceManager.class);
fullFilePath = sourceManager.findAndCacheSource(javaType, clazz);
this.cachePath = sourceManager.getCachePathForSource(javaType);
try (DebugContext.Scope s = debugContext.scope("DebugCodeInfo", declaringClass)) {
fullFilePath = sourceManager.findAndCacheSource(javaType, clazz, debugContext);
this.cachePath = sourceManager.getCachePathForSource(javaType);
} catch (Throwable e) {
throw debugContext.handle(e);
}
}

@SuppressWarnings("try")
Expand Down Expand Up @@ -296,6 +301,7 @@ public int line() {
return -1;
}

@SuppressWarnings("try")
private void computeFullFilePathAndCachePath() {
ResolvedJavaType declaringClass = method.getDeclaringClass();
Class<?> clazz = null;
Expand All @@ -312,8 +318,13 @@ private void computeFullFilePathAndCachePath() {
if (declaringClass instanceof AnalysisType) {
declaringClass = ((AnalysisType) declaringClass).getWrapped();
}
fullFilePath = ImageSingletons.lookup(SourceManager.class).findAndCacheSource(declaringClass, clazz);
cachePath = ImageSingletons.lookup(SourceManager.class).getCachePathForSource(declaringClass);
SourceManager sourceManager = ImageSingletons.lookup(SourceManager.class);
try (DebugContext.Scope s = debugContext.scope("DebugCodeInfo", declaringClass)) {
fullFilePath = sourceManager.findAndCacheSource(declaringClass, clazz, debugContext);
cachePath = sourceManager.getCachePathForSource(declaringClass);
} catch (Throwable e) {
throw debugContext.handle(e);
}
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,15 @@
import java.nio.file.FileSystem;
import java.nio.file.FileSystemNotFoundException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import com.oracle.svm.core.util.VMError;
import org.graalvm.compiler.serviceprovider.JavaVersionUtil;

public class JDKSourceCache extends SourceCache {

Expand All @@ -48,23 +53,33 @@ protected final SourceCacheType getType() {
* properties needed to locate relevant JDK and app source roots
*/
private static final String JAVA_HOME_PROP = "java.home";
private static final String JAVA_SPEC_VERSION_PROP = "java.specification.version";

/**
* Modules needing special case root processing.
*/
private static final String[] specialRootModules = {
"jdk.internal.vm.ci",
"jdk.internal.vm.compiler",
};

/**
* Extra root directories for files in the jdk.internal.vm.ci/compiler modules.
*/
private HashMap<String, List<Path>> specialSrcRoots;

@Override
protected void initSrcRoots() {
String javaHome = System.getProperty(JAVA_HOME_PROP);
assert javaHome != null;
Path javaHomePath = Paths.get("", javaHome);
Path srcZipPath;
String javaSpecVersion = System.getProperty(JAVA_SPEC_VERSION_PROP);
if (javaSpecVersion.equals("1.8")) {
if (JavaVersionUtil.JAVA_SPEC < 11) {
Path srcZipDir = javaHomePath.getParent();
if (srcZipDir == null) {
VMError.shouldNotReachHere("Cannot resolve parent directory of " + javaHome);
}
srcZipPath = srcZipDir.resolve("src.zip");
} else {
assert javaSpecVersion.matches("[1-9][0-9]");
srcZipPath = javaHomePath.resolve("lib").resolve("src.zip");
}
if (srcZipPath.toFile().exists()) {
Expand All @@ -73,12 +88,79 @@ protected void initSrcRoots() {
for (Path root : srcFileSystem.getRootDirectories()) {
srcRoots.add(root);
}
specialSrcRoots = new HashMap<>();
if (JavaVersionUtil.JAVA_SPEC >= 11) {
for (Path root : srcFileSystem.getRootDirectories()) {
// add dirs named "src" as extra roots for special modules
for (String specialRootModule : specialRootModules) {
ArrayList<Path> rootsList = new ArrayList<>();
specialSrcRoots.put(specialRootModule, rootsList);
Path specialModuleRoot = root.resolve(specialRootModule);
Files.find(specialModuleRoot, 2, (path, attributes) -> path.endsWith("src")).forEach(rootsList::add);
}
}
}
} catch (IOException | FileSystemNotFoundException ioe) {
/* ignore this entry */
}
}
}

@Override
public Path checkCacheFile(Path filePath) {
if (JavaVersionUtil.JAVA_SPEC >= 11) {
for (String specialRootModule : specialRootModules) {
if (filePath.startsWith(specialRootModule)) {
// handle this module specially as it has intermediate dirs
Path targetPath = cachedPath(filePath);
Path filePathNoModule = filePath.subpath(1, filePath.getNameCount());
for (Path srcRoot : specialSrcRoots.get(specialRootModule)) {
String srcRootGroup = srcRoot.subpath(1, 2).toString().replace(".", filePath.getFileSystem().getSeparator());
if (filePathNoModule.toString().startsWith(srcRootGroup)) {
Path sourcePath = extendPath(srcRoot, filePathNoModule);
try {
if (tryCheckCacheFile(sourcePath, targetPath)) {
return filePath;
}
} catch (IOException e) {
/* delete the target file as it is invalid */
targetPath.toFile().delete();
/* have another go at caching it */
return tryCacheFile(filePath);
}
}
}
}
}
}
// try the usual check instead
return super.checkCacheFile(filePath);
}

@Override
public Path tryCacheFile(Path filePath) {
if (JavaVersionUtil.JAVA_SPEC >= 11) {
for (String specialRootModule : specialRootModules) {
if (filePath.startsWith(specialRootModule)) {
// handle this module specially as it has intermediate dirs
Path targetPath = cachedPath(filePath);
Path filePathNoModule = filePath.subpath(1, filePath.getNameCount());
for (Path srcRoot : specialSrcRoots.get(specialRootModule)) {
String srcRootGroup = srcRoot.subpath(1, 2).toString().replace(".", filePath.getFileSystem().getSeparator());
if (filePathNoModule.toString().startsWith(srcRootGroup)) {
Path sourcePath = extendPath(srcRoot, filePathNoModule);
if (tryCacheFileFromRoot(sourcePath, targetPath)) {
return filePath;
}
}
}
}
}
}
// try the usual lookup instead
return super.tryCacheFile(filePath);
}

@Override
protected void trySourceRoot(Path sourceRoot, boolean fromClassPath) {
VMError.shouldNotReachHere();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ public abstract class SourceCache {
* A list of all entries in the classpath used by the native image classloader.
*/
protected static final List<String> sourcePathEntries = new ArrayList<>();

/**
* A list of root directories which may contain source files from which this cache can be
* populated.
Expand Down Expand Up @@ -166,24 +167,31 @@ public File findCandidate(Path filePath) {
* @return the supplied path if the file has been located and copied to the local sources
* directory or null if it was not found or the copy failed.
*/
public Path tryCacheFile(Path filePath) {
protected Path tryCacheFile(Path filePath) {
for (Path root : srcRoots) {
Path targetPath = cachedPath(filePath);
Path sourcePath = extendPath(root, filePath);
try {
if (checkSourcePath(sourcePath)) {
ensureTargetDirs(targetPath.getParent());
Files.copy(sourcePath, targetPath, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES);
// return the original filePath
// we don't want the sources/jdk prefix to go into the debuginfo
return filePath;
}
} catch (IOException e) {
if (tryCacheFileFromRoot(sourcePath, targetPath)) {
// return the original filePath
// we don't want the sources/jdk prefix to go into the debuginfo
return filePath;
}
}
return null;
}

protected boolean tryCacheFileFromRoot(Path sourcePath, Path targetPath) {
try {
if (checkSourcePath(sourcePath)) {
ensureTargetDirs(targetPath.getParent());
Files.copy(sourcePath, targetPath, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES);
return true;
}
} catch (IOException ioe) {
}
return false;
}

/**
* Check whether the copy of a given source file in the local source cache is up to date with
* respect to any original located in this cache's and if not copy the original to the
Expand All @@ -194,23 +202,12 @@ public Path tryCacheFile(Path filePath) {
* @return the supplied path if the file is up to date or if an updated version has been copied
* to the local sources directory or null if was not found or the copy failed.
*/
public Path checkCacheFile(Path filePath) {
protected Path checkCacheFile(Path filePath) {
Path targetPath = cachedPath(filePath);
for (Path root : srcRoots) {
Path sourcePath = extendPath(root, filePath);
try {
if (checkSourcePath(sourcePath)) {
FileTime sourceTime = Files.getLastModifiedTime(sourcePath);
FileTime destTime = Files.getLastModifiedTime(targetPath);
if (destTime.compareTo(sourceTime) < 0) {
try {
Files.copy(sourcePath, targetPath, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES);
} catch (IOException e) {
/* delete the target file as it is invalid */
targetPath.toFile().delete();
return null;
}
}
if (tryCheckCacheFile(sourcePath, targetPath)) {
return filePath;
}
} catch (IOException e) {
Expand All @@ -226,8 +223,20 @@ public Path checkCacheFile(Path filePath) {
return null;
}

protected boolean tryCheckCacheFile(Path sourcePath, Path targetPath) throws IOException {
if (checkSourcePath(sourcePath)) {
FileTime sourceTime = Files.getLastModifiedTime(sourcePath);
FileTime destTime = Files.getLastModifiedTime(targetPath);
if (destTime.compareTo(sourceTime) < 0) {
Files.copy(sourcePath, targetPath, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES);
}
return true;
}
return false;
}

/**
* Create and intialize the source cache used to locate and cache sources of a given type as
* Create and initialize the source cache used to locate and cache sources of a given type as
* determined by the supplied key.
*
* @param type an enum identifying both the type of Java sources cached by the returned cache
Expand Down Expand Up @@ -298,7 +307,7 @@ protected File cachedFile(Path candidate) {
* @param sourcePath the path to check
* @return true if the path identifies a file or false if no such file can be found.
*/
private static boolean checkSourcePath(Path sourcePath) {
protected static boolean checkSourcePath(Path sourcePath) {
return Files.isRegularFile(sourcePath);
}

Expand All @@ -307,7 +316,7 @@ private static boolean checkSourcePath(Path sourcePath) {
*
* @param targetDir a path to the desired directory
*/
private static void ensureTargetDirs(Path targetDir) {
protected static void ensureTargetDirs(Path targetDir) {
if (targetDir != null) {
File targetFile = targetDir.toFile();
if (!targetFile.exists()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,13 @@
import com.oracle.svm.core.SubstrateOptions;
import com.oracle.svm.util.ModuleSupport;
import jdk.vm.ci.meta.ResolvedJavaType;
import org.graalvm.compiler.debug.DebugContext;
import org.graalvm.compiler.serviceprovider.JavaVersionUtil;

import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.stream.Stream;

/**
* A singleton class responsible for locating source files for classes included in a native image
Expand All @@ -45,10 +48,11 @@ public class SourceManager {
*
* @param resolvedType the Java type whose source file should be located and cached
* @param clazz the Java class associated with the resolved type
* @param debugContext context for logging details of any lookup failure
* @return a path identifying the location of a successfully cached file for inclusion in the
* generated debug info or null if a source file cannot be found or cached.
*/
public Path findAndCacheSource(ResolvedJavaType resolvedType, Class<?> clazz) {
public Path findAndCacheSource(ResolvedJavaType resolvedType, Class<?> clazz, DebugContext debugContext) {
/* short circuit if we have already seen this type */
Path path = verifiedPaths.get(resolvedType);
if (path != null) {
Expand All @@ -69,6 +73,9 @@ public Path findAndCacheSource(ResolvedJavaType resolvedType, Class<?> clazz) {
path = locateSource(fileName, packageName, sourceCacheType, clazz);
if (path == null) {
// as a last ditch effort derive path from the Java class name
if (debugContext.areScopesEnabled()) {
debugContext.log(DebugContext.INFO_LEVEL, "Failed to find source file for class %s\n", resolvedType.toJavaName());
}
if (packageName.length() > 0) {
path = Paths.get("", packageName.split("\\."));
path = path.resolve(fileName);
Expand Down Expand Up @@ -173,9 +180,9 @@ private static Path computePrototypeName(String fileName, String packageName, So
}

/**
* A whitelist of packages prefixes used to pre-filter JDK runtime class lookups.
* An accept list of packages prefixes used to pre-filter JDK8 runtime class lookups.
*/
public static final String[] JDK_SRC_PACKAGE_PREFIXES = {
public static final String[] JDK8_SRC_PACKAGE_PREFIXES = {
"java.",
"jdk.",
"javax.",
Expand All @@ -187,8 +194,16 @@ private static Path computePrototypeName(String fileName, String packageName, So
"org.w3c.",
"org.xml",
};

/**
* A variant accept list of packages prefixes used to pre-filter JDK11+ runtime class lookups.
*/
public static final String[] JDK_SRC_PACKAGE_PREFIXES = Stream.concat(Stream.of(JDK8_SRC_PACKAGE_PREFIXES),
Stream.of("org.graalvm.compiler"))
.toArray(String[]::new);

/**
* A whitelist of packages prefixes used to pre-filter GraalVM class lookups.
* An accept list of packages prefixes used to pre-filter GraalVM class lookups.
*/
public static final String[] GRAALVM_SRC_PACKAGE_PREFIXES = {
"com.oracle.graal.",
Expand All @@ -199,15 +214,15 @@ private static Path computePrototypeName(String fileName, String packageName, So
};

/**
* Check a package name against a whitelist of acceptable packages.
* Check a package name against an accept list of acceptable packages.
*
* @param packageName the package name of the class to be checked
* @param whitelist a list of prefixes one of which may form the initial prefix of the package
* @param acceptList a list of prefixes one of which may form the initial prefix of the package
* name being checked
* @return true if the package name matches an entry in the whitelist otherwise false
* @return true if the package name matches an entry in the acceptlist otherwise false
*/
private static boolean whiteListPackage(String packageName, String[] whitelist) {
for (String prefix : whitelist) {
private static boolean acceptList(String packageName, String[] acceptList) {
for (String prefix : acceptList) {
if (packageName.startsWith(prefix)) {
return true;
}
Expand All @@ -223,10 +238,10 @@ private static boolean whiteListPackage(String packageName, String[] whitelist)
* @return the corresponding source cache type
*/
private static SourceCacheType sourceCacheType(String packageName) {
if (whiteListPackage(packageName, JDK_SRC_PACKAGE_PREFIXES)) {
if (acceptList(packageName, (JavaVersionUtil.JAVA_SPEC >= 11 ? JDK_SRC_PACKAGE_PREFIXES : JDK8_SRC_PACKAGE_PREFIXES))) {
return SourceCacheType.JDK;
}
if (whiteListPackage(packageName, GRAALVM_SRC_PACKAGE_PREFIXES)) {
if (acceptList(packageName, GRAALVM_SRC_PACKAGE_PREFIXES)) {
return SourceCacheType.GRAALVM;
}
return SourceCacheType.APPLICATION;
Expand Down