Bug 501283 - Lots of hash collisions during indexing (legacy indexer)
backport to 4.6.x branch, original commit by Stefan Xenos <sxenos@gmail.com>
Change-Id: Iff29aebed516daee8028e3872199e98c5becc72e
Signed-off-by: Igor Fedorenko <igor@ifedorenko.com>
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/AbstractRegressionTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/AbstractRegressionTest.java
index 052a8c1..d2e0f7e 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/AbstractRegressionTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/AbstractRegressionTest.java
@@ -35,12 +35,15 @@
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Processor;
@@ -88,8 +91,6 @@
import org.eclipse.jdt.internal.core.util.Messages;
import org.osgi.framework.Bundle;
-import java.util.regex.Pattern;
-
@SuppressWarnings({ "unchecked", "rawtypes" })
public abstract class AbstractRegressionTest extends AbstractCompilerTest implements StopableTestCase {
@@ -948,10 +949,11 @@
String computedProblemLog = Util.convertToIndependantLineDelimiter(requestor.problemLog.toString());
if (this.shouldSwallowCaptureId)
computedProblemLog = Pattern.compile("capture#(\\d+)").matcher(computedProblemLog).replaceAll("capture");
-
+
+ ProblemLog problemLog = new ProblemLog(computedProblemLog);
int i;
for (i = 0; i < alternatePlatformIndependantExpectedLogs.length; i++) {
- if (alternatePlatformIndependantExpectedLogs[i].equals(computedProblemLog))
+ if (problemLog.sameAs(alternatePlatformIndependantExpectedLogs[i]))
return; // OK
}
logTestTitle();
@@ -962,6 +964,48 @@
}
}
+ /**
+ * Used for performing order-independent log comparisons.
+ */
+ private static final class ProblemLog {
+ final Set<String> logEntry = new HashSet<>();
+
+ public ProblemLog(String log) {
+ String[] entries = log.split("----------\n");
+ Pattern pattern = Pattern.compile("\\A(\\d*\\. )");
+
+ for (String entry : entries) {
+ Matcher matcher = pattern.matcher(entry);
+ if (matcher.find()) {
+ entry = entry.substring(matcher.end());
+ }
+ this.logEntry.add(entry);
+ }
+ }
+
+ public boolean sameAs(String toTest) {
+ ProblemLog log = new ProblemLog(toTest);
+ return equals(log);
+ }
+
+ @Override
+ public int hashCode() {
+ return this.logEntry.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ ProblemLog other = (ProblemLog) obj;
+ return this.logEntry.equals(other.logEntry);
+ }
+ }
+
protected void dualPrintln(String message) {
System.out.println(message);
javacFullLog.println(message);
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AbstractJavaModelTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AbstractJavaModelTests.java
index a04e279..674ad0c 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AbstractJavaModelTests.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AbstractJavaModelTests.java
@@ -365,8 +365,19 @@
protected void assertSearchResults(String message, String expected, Object collector) {
assertSearchResults(message, expected, collector, true /* assertion */);
}
- protected void assertSearchResults(String message, String expected, Object collector, boolean assertion) {
- String actual = collector.toString();
+ private static String sortLines(String toSplit) {
+ String[] split = toSplit.split("\n");
+ Arrays.sort(split);
+ StringBuilder reJoined = new StringBuilder();
+ for (int i = 0; i < split.length; i++) {
+ reJoined.append(split[i]);
+ if (i < split.length -1) reJoined.append('\n');
+ }
+ return reJoined.toString();
+ }
+ protected void assertSearchResults(String message, String expectedString, Object collector, boolean assertion) {
+ String expected = sortLines(expectedString);
+ String actual = sortLines(collector.toString());
if (!expected.equals(actual)) {
if (this.displayName) System.out.println(getName()+" actual result is:");
System.out.print(displayString(actual, this.tabs));
@@ -656,6 +667,9 @@
assertElementsEqual(message, expected, elements, false/*don't show key*/);
}
protected void assertElementsEqual(String message, String expected, IJavaElement[] elements, boolean showResolvedInfo) {
+ assertElementsEqual(message, expected, elements, showResolvedInfo, false);
+ }
+ protected void assertElementsEqual(String message, String expected, IJavaElement[] elements, boolean showResolvedInfo, boolean sorted) {
StringBuffer buffer = new StringBuffer();
if (elements != null) {
for (int i = 0, length = elements.length; i < length; i++){
@@ -671,6 +685,9 @@
buffer.append("<null>");
}
String actual = buffer.toString();
+ if (sorted) {
+ actual = sortLines(actual);
+ }
if (!expected.equals(actual)) {
if (this.displayName) System.out.println(getName()+" actual result is:");
System.out.println(displayString(actual, this.tabs) + this.endChar);
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/JavadocMethodCompletionModelTest.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/JavadocMethodCompletionModelTest.java
index 8278f54..50bad4c 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/JavadocMethodCompletionModelTest.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/JavadocMethodCompletionModelTest.java
@@ -338,7 +338,7 @@
" }\n" +
"}\n";
completeInJavadoc("/Completion/src/javadoc/methods/tags/BasicTestMethods.java", source, true, "I");
- assertResults(
+ assertSortedResults(
"IllegalMonitorStateException[TYPE_REF]{IllegalMonitorStateException, java.lang, Ljava.lang.IllegalMonitorStateException;, null, null, "+this.positions+R_DRICUNRE+"}\n" +
"InterruptedException[TYPE_REF]{InterruptedException, java.lang, Ljava.lang.InterruptedException;, null, null, "+this.positions+R_DRICUNRE+"}"
);
@@ -357,9 +357,9 @@
" }\n" +
"}\n";
completeInJavadoc("/Completion/src/javadoc/methods/tags/BasicTestMethods.java", source, true, "java.lang.I");
- assertResults(
- "IllegalMonitorStateException[TYPE_REF]{IllegalMonitorStateException, java.lang, Ljava.lang.IllegalMonitorStateException;, null, null, "+this.positions+R_DRICNRE+"}\n" +
- "InterruptedException[TYPE_REF]{InterruptedException, java.lang, Ljava.lang.InterruptedException;, null, null, "+this.positions+R_DRICNREEET+"}"
+ assertSortedResults(
+ "InterruptedException[TYPE_REF]{InterruptedException, java.lang, Ljava.lang.InterruptedException;, null, null, "+this.positions+R_DRICNREEET+"}\n" +
+ "IllegalMonitorStateException[TYPE_REF]{IllegalMonitorStateException, java.lang, Ljava.lang.IllegalMonitorStateException;, null, null, "+this.positions+R_DRICNRE+"}"
);
}
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ResolveTests2.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ResolveTests2.java
index fc42448..0c82ecd 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ResolveTests2.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ResolveTests2.java
@@ -926,7 +926,9 @@
"Unexpected elements",
"IResource [in IResource.class [in test1 [in "+outputDirectory+File.separator+"bug232880a.jar]]]\n" +
"IResource [in IResource.class [in test2 [in "+outputDirectory+File.separator+"bug232880a.jar]]]",
- elements
+ elements,
+ false,
+ true
);
} finally {
this.deleteExternalFile(externalJar1);
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/CharOperation.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/CharOperation.java
index 7eafeee..23b3817 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/CharOperation.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/CharOperation.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2015 IBM Corporation and others.
+ * Copyright (c) 2000, 2016 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -9,9 +9,12 @@
* IBM Corporation - initial API and implementation
* Luiz-Otavio Zorzella <zorzella at gmail dot com> - Improve CamelCase algorithm
* Gábor Kövesdán - Contribution for Bug 350000 - [content assist] Include non-prefix matches in auto-complete suggestions
+ * Stefan Xenos <sxenos@gmail.com> (Google) - Bug 501283 - Lots of hash collisions during indexing
*******************************************************************************/
package org.eclipse.jdt.core.compiler;
+import java.util.Arrays;
+
import org.eclipse.jdt.internal.compiler.parser.ScannerHelper;
/**
@@ -2281,19 +2284,9 @@
*
* @param array the array for which a hashcode is required
* @return the hashcode
- * @throws NullPointerException if array is null
*/
public static final int hashCode(char[] array) {
- int length = array.length;
- int hash = length == 0 ? 31 : array[0];
- if (length < 8) {
- for (int i = length; --i > 0;)
- hash = (hash * 31) + array[i];
- } else {
- // 8 characters is enough to compute a decent hash code, don't waste time examining every character
- for (int i = length - 1, last = i > 16 ? i - 16 : 0; i > last; i -= 2)
- hash = (hash * 31) + array[i];
- }
+ int hash = Arrays.hashCode(array);
return hash & 0x7FFFFFFF;
}