Skip to content

Commit 3b617e5

Browse files
committed
[performance] optimized ClasspathDirectory
* java.io.File.list() returns null if the file is not a directory, so File.isDirectory() is not needed. * use ConcurrentHashMap instead of Hashtable * avoid rawtypes ClasspathDirectory.directoryList(String) is a hotspot for example during NullTypeAnnotationTest
1 parent 8d1cb65 commit 3b617e5

File tree

1 file changed

+38
-40
lines changed

1 file changed

+38
-40
lines changed

org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/batch/ClasspathDirectory.java

Lines changed: 38 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,18 @@
2626
import java.nio.file.SimpleFileVisitor;
2727
import java.nio.file.attribute.BasicFileAttributes;
2828
import java.util.HashSet;
29-
import java.util.Hashtable;
3029
import java.util.List;
3130
import java.util.Map;
3231
import java.util.Set;
32+
import java.util.concurrent.ConcurrentHashMap;
3333
import java.util.function.Function;
3434
import java.util.stream.Stream;
3535
import org.eclipse.jdt.core.compiler.CharOperation;
3636
import org.eclipse.jdt.internal.compiler.CompilationResult;
3737
import org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies;
3838
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
3939
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
40+
import org.eclipse.jdt.internal.compiler.batch.FileSystem.Classpath;
4041
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader;
4142
import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException;
4243
import org.eclipse.jdt.internal.compiler.classfmt.ExternalAnnotationProvider;
@@ -51,18 +52,17 @@
5152
import org.eclipse.jdt.internal.compiler.problem.ProblemReporter;
5253
import org.eclipse.jdt.internal.compiler.util.Util;
5354

54-
@SuppressWarnings({"rawtypes", "unchecked"})
5555
public class ClasspathDirectory extends ClasspathLocation {
5656

57-
private Hashtable directoryCache;
58-
private final String[] missingPackageHolder = new String[1];
59-
private final int mode; // ability to only consider one kind of files (source vs. binaries), by default use both
60-
private final String encoding; // only useful if referenced in the source path
61-
private Hashtable<String, Hashtable<String, String>> packageSecondaryTypes = null;
62-
Map options;
57+
private final Map<String, String[]> directoryCache = new ConcurrentHashMap<>();
58+
private final String[] missingPackageHolder = new String[1];
59+
private final int mode; // ability to only consider one kind of files (source vs. binaries), by default use both
60+
private final String encoding; // only useful if referenced in the source path
61+
private final Map<String, Map<String, String>> packageSecondaryTypes = new ConcurrentHashMap<>();
62+
private final Map<String, String> options;
6363

6464
ClasspathDirectory(File directory, String encoding, int mode,
65-
AccessRuleSet accessRuleSet, String destinationPath, Map options) {
65+
AccessRuleSet accessRuleSet, String destinationPath, Map<String, String> options) {
6666
super(accessRuleSet, destinationPath);
6767
this.mode = mode;
6868
this.options = options;
@@ -74,43 +74,47 @@ public class ClasspathDirectory extends ClasspathLocation {
7474
}
7575
if (!this.path.endsWith(File.separator))
7676
this.path += File.separator;
77-
this.directoryCache = new Hashtable(11);
7877
this.encoding = encoding;
7978
}
80-
String[] directoryList(String qualifiedPackageName) {
81-
if (File.separatorChar != '/' && qualifiedPackageName.indexOf('/') != -1) {
82-
qualifiedPackageName = qualifiedPackageName.replace('/', File.separatorChar);
79+
80+
private String[] directoryList(String qualifiedPackageName) {
81+
String[] cached = this.directoryCache.computeIfAbsent(qualifiedPackageName, this::computeDirectoryList);
82+
if (cached == this.missingPackageHolder) {
83+
return null; // package exists in another classpath directory or jar
8384
}
84-
String[] dirList = (String[]) this.directoryCache.get(qualifiedPackageName);
85-
if (dirList == this.missingPackageHolder) return null; // package exists in another classpath directory or jar
86-
if (dirList != null) return dirList;
85+
return cached;
86+
}
8787

88-
File dir = new File(this.path + qualifiedPackageName);
89-
notFound : if (dir.isDirectory()) {
88+
private String[] computeDirectoryList(String qualifiedPackageName) {
89+
String qualifiedPackagePath = qualifiedPackageName.replace('/', File.separatorChar);
90+
File dir = new File(this.path + qualifiedPackagePath);
91+
String[] dirList = dir.list();
92+
notFound: if (dirList != null) { // if isDirectory
9093
// must protect against a case insensitive File call
9194
// walk the qualifiedPackageName backwards looking for an uppercase character before the '/'
92-
int index = qualifiedPackageName.length();
93-
int last = qualifiedPackageName.lastIndexOf(File.separatorChar);
94-
while (--index > last && !ScannerHelper.isUpperCase(qualifiedPackageName.charAt(index))){/*empty*/}
95+
int index = qualifiedPackagePath.length();
96+
int last = qualifiedPackagePath.lastIndexOf(File.separatorChar);
97+
while (--index > last && !ScannerHelper.isUpperCase(qualifiedPackagePath.charAt(index))) {
98+
/* empty */}
9599
if (index > last) {
96100
if (last == -1) {
97-
if (!doesFileExist(qualifiedPackageName, Util.EMPTY_STRING))
101+
if (!doesFileExist(qualifiedPackagePath, Util.EMPTY_STRING))
98102
break notFound;
99103
} else {
100-
String packageName = qualifiedPackageName.substring(last + 1);
101-
String parentPackage = qualifiedPackageName.substring(0, last);
104+
String packageName = qualifiedPackagePath.substring(last + 1);
105+
String parentPackage = qualifiedPackagePath.substring(0, last);
102106
if (!doesFileExist(packageName, parentPackage))
103107
break notFound;
104108
}
105109
}
106-
if ((dirList = dir.list()) == null)
110+
if (dirList.length == 0) {
107111
dirList = CharOperation.NO_STRINGS;
108-
this.directoryCache.put(qualifiedPackageName, dirList);
112+
}
109113
return dirList;
110114
}
111-
this.directoryCache.put(qualifiedPackageName, this.missingPackageHolder);
112-
return null;
115+
return this.missingPackageHolder;
113116
}
117+
114118
boolean doesFileExist(String fileName, String qualifiedPackageName) {
115119
String[] dirList = directoryList(qualifiedPackageName);
116120
if (dirList == null) return false; // most common case
@@ -121,7 +125,7 @@ boolean doesFileExist(String fileName, String qualifiedPackageName) {
121125
return false;
122126
}
123127
@Override
124-
public List fetchLinkedJars(FileSystem.ClasspathSectionProblemReporter problemReporter) {
128+
public List<Classpath> fetchLinkedJars(FileSystem.ClasspathSectionProblemReporter problemReporter) {
125129
return null;
126130
}
127131
private NameEnvironmentAnswer findClassInternal(char[] typeName, String qualifiedPackageName, String qualifiedBinaryFileName, boolean asBinaryOnly) {
@@ -202,10 +206,10 @@ public NameEnvironmentAnswer findClass(char[] typeName, String qualifiedPackageN
202206
/**
203207
* Add all the secondary types in the package
204208
*/
205-
private Hashtable<String, String> getSecondaryTypes(String qualifiedPackageName) {
206-
Hashtable<String, String> packageEntry = new Hashtable<>();
209+
private Map<String, String> getSecondaryTypes(String qualifiedPackageName) {
210+
Map<String, String> packageEntry = new ConcurrentHashMap<>();
207211

208-
String[] dirList = (String[]) this.directoryCache.get(qualifiedPackageName);
212+
String[] dirList = this.directoryCache.get(qualifiedPackageName);
209213
if (dirList == this.missingPackageHolder // package exists in another classpath directory or jar
210214
|| dirList == null)
211215
return packageEntry;
@@ -241,13 +245,7 @@ private Hashtable<String, String> getSecondaryTypes(String qualifiedPackageName)
241245
return packageEntry;
242246
}
243247
private NameEnvironmentAnswer findSourceSecondaryType(String typeName, String qualifiedPackageName, String qualifiedBinaryFileName) {
244-
245-
if (this.packageSecondaryTypes == null) this.packageSecondaryTypes = new Hashtable<>();
246-
Hashtable<String, String> packageEntry = this.packageSecondaryTypes.get(qualifiedPackageName);
247-
if (packageEntry == null) {
248-
packageEntry = getSecondaryTypes(qualifiedPackageName);
249-
this.packageSecondaryTypes.put(qualifiedPackageName, packageEntry);
250-
}
248+
Map<String, String> packageEntry = this.packageSecondaryTypes.computeIfAbsent(qualifiedPackageName, this::getSecondaryTypes);
251249
String fileName = packageEntry.get(typeName);
252250
return fileName != null ? new NameEnvironmentAnswer(new CompilationUnit(null,
253251
fileName, this.encoding, this.destinationPath),
@@ -360,7 +358,7 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
360358
@Override
361359
public void reset() {
362360
super.reset();
363-
this.directoryCache = new Hashtable(11);
361+
this.directoryCache.clear();
364362
}
365363
@Override
366364
public String toString() {

0 commit comments

Comments
 (0)