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
69 changes: 38 additions & 31 deletions src/jdk.jshell/share/classes/jdk/jshell/SourceCodeAnalysisImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,6 @@
import static jdk.internal.jshell.debug.InternalDebugControl.DBG_COMPA;

import java.io.IOException;
import java.net.URI;
import java.nio.file.DirectoryStream;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
Expand All @@ -100,6 +99,7 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.ProviderNotFoundException;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Arrays;
import java.util.Collection;
Expand Down Expand Up @@ -1997,12 +1997,22 @@ public void resumeIndexing() {
//update indexes, either initially or after a classpath change:
private void refreshIndexes(int version) {
try {
Collection<Path> paths = new ArrayList<>();
MemoryFileManager fm = proc.taskFactory.fileManager();

appendPaths(fm, StandardLocation.PLATFORM_CLASS_PATH, paths);
appendPaths(fm, StandardLocation.CLASS_PATH, paths);
appendPaths(fm, StandardLocation.SOURCE_PATH, paths);
Collection<Path> paths = proc.taskFactory.parse("", task -> {
MemoryFileManager fm = proc.taskFactory.fileManager();
Collection<Path> _paths = new ArrayList<>();
try {
appendPaths(fm, StandardLocation.PLATFORM_CLASS_PATH, _paths);
appendPaths(fm, StandardLocation.CLASS_PATH, _paths);
appendPaths(fm, StandardLocation.SOURCE_PATH, _paths);
appendModulePaths(fm, StandardLocation.SYSTEM_MODULES, _paths);
appendModulePaths(fm, StandardLocation.UPGRADE_MODULE_PATH, _paths);
appendModulePaths(fm, StandardLocation.MODULE_PATH, _paths);
return _paths;
} catch (Exception ex) {
proc.debug(ex, "SourceCodeAnalysisImpl.refreshIndexes(" + version + ")");
return List.of();
}
});

Map<Path, ClassIndex> newIndexes = new HashMap<>();

Expand Down Expand Up @@ -2055,27 +2065,24 @@ private void appendPaths(MemoryFileManager fm, Location loc, Collection<Path> pa
}
}

private void appendModulePaths(MemoryFileManager fm, Location loc, Collection<Path> paths) throws IOException {
for (Set<Location> moduleLocations : fm.listLocationsForModules(loc)) {
for (Location moduleLocation : moduleLocations) {
Iterable<? extends Path> modulePaths = fm.getLocationAsPaths(moduleLocation);

if (modulePaths == null) {
continue;
}

modulePaths.forEach(paths::add);
}
}
}

//create/update index a given JavaFileManager entry (which may be a JDK installation, a jar/zip file or a directory):
//if an index exists for the given entry, the existing index is kept unless the timestamp is modified
private ClassIndex indexForPath(Path path) {
if (isJRTMarkerFile(path)) {
FileSystem jrtfs = FileSystems.getFileSystem(URI.create("jrt:/"));
Path modules = jrtfs.getPath("modules");
return PATH_TO_INDEX.compute(path, (p, index) -> {
try {
long lastModified = Files.getLastModifiedTime(modules).toMillis();
if (index == null || index.timestamp != lastModified) {
try (DirectoryStream<Path> stream = Files.newDirectoryStream(modules)) {
index = doIndex(lastModified, path, stream);
}
}
return index;
} catch (IOException ex) {
proc.debug(ex, "SourceCodeAnalysisImpl.indexesForPath(" + path.toString() + ")");
return new ClassIndex(-1, path, Collections.emptySet(), Collections.emptyMap());
}
});
} else if (!Files.isDirectory(path)) {
if (!Files.isDirectory(path)) {
if (Files.exists(path)) {
return PATH_TO_INDEX.compute(path, (p, index) -> {
try {
Expand All @@ -2088,7 +2095,7 @@ private ClassIndex indexForPath(Path path) {
}
}
return index;
} catch (IOException ex) {
} catch (IOException | ProviderNotFoundException ex) {
proc.debug(ex, "SourceCodeAnalysisImpl.indexesForPath(" + path.toString() + ")");
return new ClassIndex(-1, path, Collections.emptySet(), Collections.emptyMap());
}
Expand All @@ -2107,10 +2114,6 @@ private ClassIndex indexForPath(Path path) {
}
}

static boolean isJRTMarkerFile(Path path) {
return path.equals(Paths.get(System.getProperty("java.home"), "lib", "modules"));
}

//create an index based on the content of the given dirs; the original JavaFileManager entry is originalPath.
private ClassIndex doIndex(long timestamp, Path originalPath, Iterable<? extends Path> dirs) {
Set<String> packages = new HashSet<>();
Expand Down Expand Up @@ -2195,13 +2198,17 @@ public void waitBackgroundTaskFinished() throws Exception {
upToDate = classpathVersion == indexVersion;
}
while (!upToDate) {
INDEXER.submit(() -> {}).get();
waitCurrentBackgroundTasksFinished();
synchronized (currentIndexes) {
upToDate = classpathVersion == indexVersion;
}
}
}

public static void waitCurrentBackgroundTasksFinished() throws Exception {
INDEXER.submit(() -> {}).get();
}

/**
* A candidate for continuation of the given user's input.
*/
Expand Down
8 changes: 7 additions & 1 deletion test/langtools/jdk/jshell/Compiler.java
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,17 @@ public void jar(String jarName, String...files) {
}

public void jar(Path directory, String jarName, String...files) {
Path classDirPath = getClassDir();
Path baseDir = classDirPath.resolve(directory);
Path jarPath = baseDir.resolve(jarName);
jar(directory, jarPath, files);
}

public void jar(Path directory, Path jarPath, String...files) {
Manifest manifest = new Manifest();
manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
Path classDirPath = getClassDir();
Path baseDir = classDirPath.resolve(directory);
Path jarPath = baseDir.resolve(jarName);
new JarTask(tb, jarPath.toString())
.manifest(manifest)
.baseDir(baseDir.toString())
Expand Down
16 changes: 16 additions & 0 deletions test/langtools/jdk/jshell/CompletionSuggestionTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -834,4 +834,20 @@ public void testModuleImport() {
assertCompletionIncludesExcludes("import module ja|", Set.of("java.base"), Set.of("jdk.compiler"));
assertCompletion("import module java/*c*/./*c*/ba|", "java.base");
}

public void testCustomClassPathIndexing() {
Path p1 = outDir.resolve("dir1");
compiler.compile(p1,
"package p1.p2;\n" +
"public class Test {\n" +
"}",
"package p1.p3;\n" +
"public class Test {\n" +
"}");
String jarName = "test.jar";
compiler.jar(p1, jarName, "p1/p2/Test.class", "p1/p3/Test.class");
addToClasspath(compiler.getPath(p1.resolve(jarName)));

assertCompletion("p1.|", "p2.", "p3.");
}
}
33 changes: 32 additions & 1 deletion test/langtools/jdk/jshell/ReplToolTesting.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 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
Expand All @@ -25,6 +25,7 @@
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
Expand Down Expand Up @@ -566,6 +567,36 @@ public void assertOutput(String got, String expected, String display) {
}
}

public void assertCompletions(boolean after, String input, String expectedCompletionsPattern) {
if (!after) {
try {
Class<?> sourceCodeAnalysisImpl = Class.forName("jdk.jshell.SourceCodeAnalysisImpl");
Method waitBackgroundTaskFinished = sourceCodeAnalysisImpl.getDeclaredMethod("waitCurrentBackgroundTasksFinished");

waitBackgroundTaskFinished.setAccessible(true);
waitBackgroundTaskFinished.invoke(null);
} catch (ReflectiveOperationException ex) {
throw new AssertionError(ex.getMessage(), ex);
}

setCommandInput(input + "\t");
} else {
assertOutput(getCommandOutput().trim(), "", "command output: " + input);
assertOutput(getCommandErrorOutput(), "", "command error: " + input);
assertOutput(getUserOutput(), "", "user output: " + input);
assertOutput(getUserErrorOutput(), "", "user error: " + input);
String actualOutput = getTerminalOutput();
Pattern compiledPattern =
Pattern.compile(expectedCompletionsPattern, Pattern.DOTALL);
if (!compiledPattern.asMatchPredicate().test(actualOutput)) {
throw new AssertionError("Actual output:\n" +
actualOutput + "\n" +
"does not match expected pattern: " +
expectedCompletionsPattern);
}
}
}

private String normalizeLineEndings(String text) {
return normalizeLineEndings(System.getProperty("line.separator"), text);
}
Expand Down
Loading