Skip to content

Commit 3f9ff5c

Browse files
authored
Fix crashes (#316)
* Fix NPE on inspecting non-existent files FileStore#packageName and FileStore#modified used to raise NPE when a specified file does not exist. This patch makes them return null so that callers can handle them. * Fix leaks of ReusableCompiler.Borrow ReusableCompiler.Borrow used to be leaked when a runtime exception is thrown in CompileBatch.needsAdditionalSources. This patch ensures to release it. It should fix "Compiler is already in-use!" errors that repeat after a runtime exception.
1 parent ca503f1 commit 3f9ff5c

7 files changed

Lines changed: 42 additions & 13 deletions

File tree

src/main/java/org/javacs/Cache.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ boolean needs(Path file, K k) {
5353
// If key was loaded before file was last modified, it needs to be reloaded
5454
var value = map.get(key);
5555
var modified = FileStore.modified(file);
56+
if (modified == null) return true;
5657
// TODO remove all keys associated with file when file changes
5758
return value.created.isBefore(modified);
5859
}

src/main/java/org/javacs/CompileBatch.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,11 @@ Set<Path> needsAdditionalSources() {
5858
if (!isValidFileRange(err)) continue;
5959
var className = errorText(err);
6060
var packageName = packageName(err);
61-
var location = findPackagePrivateClass(packageName, className);
62-
if (location != FILE_NOT_FOUND) {
63-
addFiles.add(location);
61+
if (packageName != null) {
62+
var location = findPackagePrivateClass(packageName, className);
63+
if (location != FILE_NOT_FOUND) {
64+
addFiles.add(location);
65+
}
6466
}
6567
}
6668
return addFiles;

src/main/java/org/javacs/FileStore.java

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,11 @@ static Instant modified(Path file) {
138138
readInfoFromDisk(file);
139139
}
140140
// Look up modified time from cache
141-
return javaSources.get(file).modified;
141+
var info = javaSources.get(file);
142+
if (info == null) {
143+
return null;
144+
}
145+
return info.modified;
142146
}
143147

144148
static String packageName(Path file) {
@@ -147,7 +151,11 @@ static String packageName(Path file) {
147151
readInfoFromDisk(file);
148152
}
149153
// Look up package name from cache
150-
return javaSources.get(file).packageName;
154+
var info = javaSources.get(file);
155+
if (info == null) {
156+
return null;
157+
}
158+
return info.packageName;
151159
}
152160

153161
public static String suggestedPackageName(Path file) {
@@ -157,7 +165,7 @@ public static String suggestedPackageName(Path file) {
157165
for (var sibling : javaSourcesIn(dir)) {
158166
if (sibling.equals(file)) continue;
159167
var packageName = packageName(sibling);
160-
if (packageName.isBlank()) continue;
168+
if (packageName == null || packageName.isBlank()) continue;
161169
var relativePath = dir.relativize(file.getParent());
162170
var relativePackage = relativePath.toString().replace(File.separatorChar, '.');
163171
if (!relativePackage.isEmpty()) {
@@ -337,7 +345,7 @@ private static String patch(String sourceText, TextDocumentContentChangeEvent ch
337345
int lines = change.range.end.line - change.range.start.line;
338346
int chars = change.range.end.character;
339347
for (int lineSkip = 0; lineSkip < lines; lineSkip++) {
340-
reader.readLine();
348+
reader.readLine();
341349
}
342350
reader.skip(chars);
343351
}

src/main/java/org/javacs/JavaCompilerService.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ private void loadCompile(Collection<? extends JavaFileObject> sources) {
6464
}
6565
cachedCompile.borrow.close();
6666
}
67+
cachedCompile = null;
6768
cachedCompile = doCompile(sources);
6869
cachedModified.clear();
6970
for (var f : sources) {
@@ -74,7 +75,14 @@ private void loadCompile(Collection<? extends JavaFileObject> sources) {
7475
private CompileBatch doCompile(Collection<? extends JavaFileObject> sources) {
7576
if (sources.isEmpty()) throw new RuntimeException("empty sources");
7677
var firstAttempt = new CompileBatch(this, sources);
77-
var addFiles = firstAttempt.needsAdditionalSources();
78+
Set<Path> addFiles;
79+
try {
80+
addFiles = firstAttempt.needsAdditionalSources();
81+
} catch (RuntimeException e) {
82+
firstAttempt.close();
83+
firstAttempt.borrow.close();
84+
throw e;
85+
}
7886
if (addFiles.isEmpty()) return firstAttempt;
7987
// If the compiler needs additional source files that contain package-private files
8088
LOG.info("...need to recompile with " + addFiles);
@@ -190,7 +198,7 @@ public List<String> publicTopLevelTypes() {
190198
if (!fileName.endsWith(".java")) continue;
191199
var className = fileName.substring(0, fileName.length() - ".java".length());
192200
var packageName = FileStore.packageName(file);
193-
if (!packageName.isEmpty()) {
201+
if (packageName != null && !packageName.isEmpty()) {
194202
className = packageName + "." + className;
195203
}
196204
all.add(className);
@@ -207,7 +215,8 @@ public List<String> packagePrivateTopLevelTypes(String packageName) {
207215

208216
private boolean containsImport(Path file, String className) {
209217
var packageName = packageName(className);
210-
if (FileStore.packageName(file).equals(packageName)) return true;
218+
// Note: FileStore.packageName may return null.
219+
if (packageName.equals(FileStore.packageName(file))) return true;
211220
var star = packageName + ".*";
212221
for (var i : readImports(file)) {
213222
if (i.equals(className) || i.equals(star)) return true;

src/main/java/org/javacs/SourceFileManager.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public String inferBinaryName(Location location, JavaFileObject file) {
4444
var source = (SourceFileObject) file;
4545
var packageName = FileStore.packageName(source.path);
4646
var className = removeExtension(source.path.getFileName().toString());
47-
if (!packageName.isEmpty()) className = packageName + "." + className;
47+
if (packageName != null && !packageName.isEmpty()) className = packageName + "." + className;
4848
return className;
4949
} else {
5050
return super.inferBinaryName(location, file);

src/main/java/org/javacs/SourceFileObject.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,9 @@ public long getLastModified() {
119119
if (contents != null) {
120120
return modified.toEpochMilli();
121121
}
122-
return FileStore.modified(path).toEpochMilli();
122+
var fileModified = FileStore.modified(path);
123+
if (fileModified == null) return 0;
124+
return fileModified.toEpochMilli();
123125
}
124126

125127
@Override

src/test/java/org/javacs/FileStoreTest.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package org.javacs;
22

3-
import static org.hamcrest.Matchers.equalTo;
3+
import static org.hamcrest.Matchers.*;
44
import static org.junit.Assert.assertThat;
55

66
import java.util.Set;
@@ -19,4 +19,11 @@ public void packageName() {
1919
var file = FindResource.path("/org/javacs/example/Goto.java");
2020
assertThat(FileStore.suggestedPackageName(file), equalTo("org.javacs.example"));
2121
}
22+
23+
@Test
24+
public void missingFile() {
25+
var file = FindResource.path("/org/javacs/example/NoSuchFile.java");
26+
assertThat(FileStore.packageName(file), nullValue());
27+
assertThat(FileStore.modified(file), nullValue());
28+
}
2229
}

0 commit comments

Comments
 (0)