Bug 544921 - [regression] Compilation failures after perf optimization
After the performance patch, errors of the form may happen:
The class file EnumMap<K,V> contains a signature
'Ljava/util/EnumMap<TK;TV;>.EnumMapIterator<Ljava/util/Map$Entry<TK;TV;>;>;'
ill-formed at position 27
Reentrant queries for member types caused trouble when sorting was still
in progress.
Also disabled memory-hungry tests in low memory env and increased bundle
version number.
Change-Id: I68edca65ade19a3166436b53ec2828dcf06bf0b2
Signed-off-by: Sebastian Zarnekow <sebastian.zarnekow@gmail.com>
Signed-off-by: Andrey Loskutov <loskutov@gmx.de>
diff --git a/org.eclipse.jdt.core.tests.builder/META-INF/MANIFEST.MF b/org.eclipse.jdt.core.tests.builder/META-INF/MANIFEST.MF
index ef6269e..92bb29b 100644
--- a/org.eclipse.jdt.core.tests.builder/META-INF/MANIFEST.MF
+++ b/org.eclipse.jdt.core.tests.builder/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@
Bundle-ManifestVersion: 2
Bundle-Name: %pluginName
Bundle-SymbolicName: org.eclipse.jdt.core.tests.builder; singleton:=true
-Bundle-Version: 3.10.600.qualifier
+Bundle-Version: 3.10.700.qualifier
Bundle-Vendor: %providerName
Bundle-Localization: plugin
Export-Package: org.eclipse.jdt.core.tests.builder
diff --git a/org.eclipse.jdt.core.tests.builder/pom.xml b/org.eclipse.jdt.core.tests.builder/pom.xml
index e0ce495..e612ab0 100644
--- a/org.eclipse.jdt.core.tests.builder/pom.xml
+++ b/org.eclipse.jdt.core.tests.builder/pom.xml
@@ -19,7 +19,7 @@
</parent>
<groupId>org.eclipse.jdt</groupId>
<artifactId>org.eclipse.jdt.core.tests.builder</artifactId>
- <version>3.10.600-SNAPSHOT</version>
+ <version>3.10.700-SNAPSHOT</version>
<packaging>eclipse-test-plugin</packaging>
<properties>
diff --git a/org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/Bug544921Test.java b/org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/Bug544921Test.java
index 1740794..5ffa764 100644
--- a/org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/Bug544921Test.java
+++ b/org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/Bug544921Test.java
@@ -16,6 +16,7 @@
import org.eclipse.core.runtime.IPath;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.tests.util.Util;
+
import junit.framework.Test;
public class Bug544921Test extends BuilderTests {
@@ -27,6 +28,27 @@
return buildTestSuite(Bug544921Test.class);
}
+ public void testCompilerRegression() throws JavaModelException, Exception {
+ IPath projectPath = env.addProject("Bug544921Test", "1.8"); //$NON-NLS-1$
+
+ env.addExternalJars(projectPath, Util.getJavaClassLibs());
+ env.addClass(projectPath, "a", "Test", //$NON-NLS-1$ //$NON-NLS-2$
+ "package a;\n" +
+ "import java.util.EnumMap;\n" +
+ "enum E {}\n" +
+ "public class Test {\n" +
+ " Object x = new EnumMap<E, String>(E.class) {\n" +
+ " static final long serialVersionUID = 1;\n" +
+ " {\n" +
+ " E.values();\n" +
+ " }\n" +
+ " };\n" +
+ "}" //$NON-NLS-1$
+ );
+ fullBuild();
+ expectingNoProblems();
+ }
+
public void testBuildLargeFile_01() throws JavaModelException, Exception {
IPath projectPath = env.addProject("Bug544921Test", "1.8"); //$NON-NLS-1$
scaffoldProject(projectPath, 1, 10, 64);
@@ -48,32 +70,46 @@
expectingNoProblems();
}
+ private boolean hasEnoughMemory(long required) {
+ long bytes = Runtime.getRuntime().maxMemory();
+ long megabytes = bytes / 1024 / 1024;
+ return megabytes > required;
+ }
+
public void testBuildLargeFile_04() throws JavaModelException, Exception {
- IPath projectPath = env.addProject("Bug544921Test", "1.8"); //$NON-NLS-1$
- scaffoldProject(projectPath, 4, 500, 64);
- fullBuild();
- expectingNoProblems();
+ if (hasEnoughMemory(2048)) {
+ IPath projectPath = env.addProject("Bug544921Test", "1.8"); //$NON-NLS-1$
+ scaffoldProject(projectPath, 4, 500, 64);
+ fullBuild();
+ expectingNoProblems();
+ }
}
public void testBuildLargeFile_05() throws JavaModelException, Exception {
- IPath projectPath = env.addProject("Bug544921Test", "1.8"); //$NON-NLS-1$
- scaffoldProject(projectPath, 5, 500, 64);
- fullBuild();
- expectingNoProblems();
+ if (hasEnoughMemory(2048)) {
+ IPath projectPath = env.addProject("Bug544921Test", "1.8"); //$NON-NLS-1$
+ scaffoldProject(projectPath, 5, 500, 64);
+ fullBuild();
+ expectingNoProblems();
+ }
}
public void testBuildLargeFile_08() throws JavaModelException, Exception {
- IPath projectPath = env.addProject("Bug544921Test", "1.8"); //$NON-NLS-1$
- scaffoldProject(projectPath, 8, 500, 64);
- fullBuild();
- expectingNoProblems();
+ if (hasEnoughMemory(2048)) {
+ IPath projectPath = env.addProject("Bug544921Test", "1.8"); //$NON-NLS-1$
+ scaffoldProject(projectPath, 8, 500, 64);
+ fullBuild();
+ expectingNoProblems();
+ }
}
- public void _testBuildLargeFile_10() throws JavaModelException, Exception {
- IPath projectPath = env.addProject("Bug544921Test", "1.8"); //$NON-NLS-1$
- scaffoldProject(projectPath, 10, 500, 64);
- fullBuild();
- expectingNoProblems();
+ public void testBuildLargeFile_10() throws JavaModelException, Exception {
+ if (hasEnoughMemory(2048)) {
+ IPath projectPath = env.addProject("Bug544921Test", "1.8"); //$NON-NLS-1$
+ scaffoldProject(projectPath, 10, 500, 64);
+ fullBuild();
+ expectingNoProblems();
+ }
}
private void scaffoldProject(IPath projectPath, int maxPeripheral, int maxRegister, int maxFields) throws JavaModelException {
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java
index 50a3633..5299130 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java
@@ -48,7 +48,6 @@
package org.eclipse.jdt.internal.compiler.lookup;
import java.util.ArrayList;
-import java.util.Arrays;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
@@ -59,7 +58,16 @@
import org.eclipse.jdt.internal.compiler.classfmt.NonNullDefaultAwareTypeAnnotationWalker;
import org.eclipse.jdt.internal.compiler.classfmt.TypeAnnotationWalker;
import org.eclipse.jdt.internal.compiler.codegen.ConstantPool;
-import org.eclipse.jdt.internal.compiler.env.*;
+import org.eclipse.jdt.internal.compiler.env.ClassSignature;
+import org.eclipse.jdt.internal.compiler.env.EnumConstantSignature;
+import org.eclipse.jdt.internal.compiler.env.IBinaryAnnotation;
+import org.eclipse.jdt.internal.compiler.env.IBinaryElementValuePair;
+import org.eclipse.jdt.internal.compiler.env.IBinaryField;
+import org.eclipse.jdt.internal.compiler.env.IBinaryMethod;
+import org.eclipse.jdt.internal.compiler.env.IBinaryNestedType;
+import org.eclipse.jdt.internal.compiler.env.IBinaryType;
+import org.eclipse.jdt.internal.compiler.env.IBinaryTypeAnnotation;
+import org.eclipse.jdt.internal.compiler.env.ITypeAnnotationWalker;
import org.eclipse.jdt.internal.compiler.impl.BooleanConstant;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import org.eclipse.jdt.internal.compiler.impl.Constant;
@@ -1287,39 +1295,30 @@
return memberType == null ? null : this.environment.createMemberType(memberType, this);
}
- ReferenceBinding[] members = sortedMemberTypes();
- int memberTypeIndex = unresolvedTypesAwareBinarySearch(typeName, members);
- if (memberTypeIndex >= 0) {
- ReferenceBinding memberType = members[memberTypeIndex];
- if (memberType instanceof UnresolvedReferenceBinding) {
- return members[memberTypeIndex] = (ReferenceBinding) resolveType(memberType, this.environment, false /* no raw conversion for now */);
+ ReferenceBinding[] members = maybeSortedMemberTypes();
+ // do not try to binary search while we are still resolving and the array is not necessarily sorted
+ if (!this.memberTypesSorted) {
+ for (int i = members.length; --i >= 0;) {
+ ReferenceBinding memberType = members[i];
+ if (memberType instanceof UnresolvedReferenceBinding) {
+ char[] name = memberType.sourceName; // source name is qualified with enclosing type name
+ int prefixLength = this.compoundName[this.compoundName.length - 1].length + 1; // enclosing$
+ if (name.length == (prefixLength + typeName.length)) // enclosing $ typeName
+ if (CharOperation.fragmentEquals(typeName, name, prefixLength, true)) // only check trailing portion
+ return members[i] = (ReferenceBinding) resolveType(memberType, this.environment, false /* no raw conversion for now */);
+ } else if (CharOperation.equals(typeName, memberType.sourceName)) {
+ return memberType;
+ }
}
- return memberType;
+ return null;
+ }
+ int memberTypeIndex = ReferenceBinding.binarySearch(typeName, members);
+ if (memberTypeIndex >= 0) {
+ return members[memberTypeIndex];
}
return null;
}
-private int unresolvedTypesAwareBinarySearch(char[] name, ReferenceBinding[] sortedMemberTypes) {
- if (sortedMemberTypes == null)
- return -1;
- int max = sortedMemberTypes.length;
- if (max == 0)
- return -1;
- int left = 0, right = max - 1, nameLength = name.length;
- int mid = 0;
- char[] midName;
- while (left <= right) {
- mid = left + (right - left) /2;
- int compare = compare(name, midName = getSourceName(sortedMemberTypes[mid]), nameLength, midName.length);
- if (compare < 0) {
- right = mid-1;
- } else if (compare > 0) {
- left = mid+1;
- } else {
- return mid;
- }
- }
- return -1;
-}
+
// NOTE: the return type, arg & exception types of each method of a binary type are resolved when needed
@Override
public MethodBinding[] getMethods(char[] selector) {
@@ -1563,44 +1562,29 @@
}
if ((this.tagBits & TagBits.HasUnresolvedMemberTypes) == 0) {
- return sortedMemberTypes();
+ return maybeSortedMemberTypes();
}
for (int i = this.memberTypes.length; --i >= 0;)
this.memberTypes[i] = (ReferenceBinding) resolveType(this.memberTypes[i], this.environment, false /* no raw conversion for now */);
this.tagBits &= ~TagBits.HasUnresolvedMemberTypes;
- return sortedMemberTypes();
+ return maybeSortedMemberTypes();
}
-private ReferenceBinding[] sortedMemberTypes() {
+private ReferenceBinding[] maybeSortedMemberTypes() {
+ // do not try to sort while we are still resolving
+ if ((this.tagBits & TagBits.HasUnresolvedMemberTypes) != 0) {
+ return this.memberTypes;
+ }
if (!this.memberTypesSorted) {
// lazily sort member types
int length = this.memberTypes.length;
if (length > 1)
- unresolvedAwareSortMemberTypes(this.memberTypes, 0, length);
+ sortMemberTypes(this.memberTypes, 0, length);
this.memberTypesSorted = true;
}
return this.memberTypes;
}
-private void unresolvedAwareSortMemberTypes(ReferenceBinding[] sortedMemberTypes, int left, int right) {
- Arrays.sort(sortedMemberTypes, left, right, this::unresolvedAwareCompare);
-}
-
-private int unresolvedAwareCompare(ReferenceBinding o1, ReferenceBinding o2) {
- char[] n1 = getSourceName(o1);
- char[] n2 = getSourceName(o2);
- return ReferenceBinding.compare(n1, n2, n1.length, n2.length);
-}
-
-private char[] getSourceName(ReferenceBinding memberType) {
- if (memberType instanceof UnresolvedReferenceBinding) {
- char[] name = memberType.sourceName; // source name is qualified with enclosing type name
- int prefixLength = this.compoundName[this.compoundName.length - 1].length + 1; // enclosing$
- return CharOperation.subarray(name, prefixLength, name.length);
- }
- return memberType.sourceName;
-}
-
// NOTE: the return type, arg & exception types of each method of a binary type are resolved when needed
@Override
public MethodBinding[] methods() {
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java
index 241874b..5c1e31f 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java
@@ -235,6 +235,19 @@
}
/**
+ * Sort the member types using a quicksort
+ */
+static void sortMemberTypes(ReferenceBinding[] sortedMemberTypes, int left, int right) {
+ Arrays.sort(sortedMemberTypes, left, right, BASIC_MEMBER_TYPES_COMPARATOR);
+}
+
+static final Comparator<ReferenceBinding> BASIC_MEMBER_TYPES_COMPARATOR = (ReferenceBinding o1, ReferenceBinding o2) -> {
+ char[] n1 = o1.sourceName;
+ char[] n2 = o2.sourceName;
+ return ReferenceBinding.compare(n1, n2, n1.length, n2.length);
+};
+
+/**
* Return the array of resolvable fields (resilience)
*/
public FieldBinding[] availableFields() {
@@ -1055,14 +1068,14 @@
*/
public ReferenceBinding getMemberType(char[] typeName) {
ReferenceBinding[] memberTypes = memberTypes();
- int memberTypeIndex = ReferenceBinding.binarySearch(typeName, memberTypes);
+ int memberTypeIndex = binarySearch(typeName, memberTypes);
if (memberTypeIndex >= 0) {
return memberTypes[memberTypeIndex];
}
return null;
}
-private static int binarySearch(char[] name, ReferenceBinding[] sortedMemberTypes) {
+static int binarySearch(char[] name, ReferenceBinding[] sortedMemberTypes) {
if (sortedMemberTypes == null)
return -1;
int max = sortedMemberTypes.length;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java
index 5158fb9..003e110 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java
@@ -53,9 +53,7 @@
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup;
-import java.util.Arrays;
import java.util.Collection;
-import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@@ -1545,19 +1543,6 @@
return this.memberTypes;
}
-/**
- * Sort the member types using a quicksort
- */
-private static void sortMemberTypes(ReferenceBinding[] sortedMemberTypes, int left, int right) {
- Arrays.sort(sortedMemberTypes, left, right, BASIC_MEMBER_TYPES_COMPARATOR);
-}
-
-private static final Comparator<ReferenceBinding> BASIC_MEMBER_TYPES_COMPARATOR = (ReferenceBinding o1, ReferenceBinding o2) -> {
- char[] n1 = o1.sourceName;
- char[] n2 = o2.sourceName;
- return ReferenceBinding.compare(n1, n2, n1.length, n2.length);
-};
-
@Override
public boolean hasMemberTypes() {
if (!isPrototype())