Skip to content

Commit

Permalink
Fixed Bug 434326 - [compile][generics] Slow compilation of test cases
Browse files Browse the repository at this point in the history
with a significant amount of generics

Signed-off-by: Sasikanth Bharadwaj <[email protected]>
  • Loading branch information
asbharadwaj authored and ssankaran committed Aug 19, 2014
1 parent 3d73760 commit 510e796
Show file tree
Hide file tree
Showing 2 changed files with 126 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -101,31 +101,20 @@ public ParameterizedTypeBinding getParameterizedType(ReferenceBinding genericTyp

if (genericType.hasTypeAnnotations()) // @NonNull (List<String>) and not (@NonNull List)<String>
throw new IllegalStateException();

ParameterizedTypeBinding nakedType = null;
TypeBinding[] derivedTypes = getDerivedTypes(genericType);
for (int i = 0, length = derivedTypes.length; i < length; i++) {
TypeBinding derivedType = derivedTypes[i];
if (derivedType == null)
break;
if (!derivedType.isParameterizedType() || derivedType.actualType() != genericType) //$IDENTITY-COMPARISON$
continue;
if (derivedType.enclosingType() != enclosingType || !Util.effectivelyEqual(derivedType.typeArguments(), typeArguments)) //$IDENTITY-COMPARISON$
continue;
if (Util.effectivelyEqual(annotations, derivedType.getTypeAnnotations()))
return (ParameterizedTypeBinding) derivedType;
if (!derivedType.hasTypeAnnotations())
nakedType = (ParameterizedTypeBinding) derivedType;
}
if (nakedType == null)
nakedType = super.getParameterizedType(genericType, typeArguments, enclosingType);

ParameterizedTypeBinding parameterizedType = this.parameterizedTypes.get(genericType, typeArguments, enclosingType, annotations);
if (parameterizedType != null)
return parameterizedType;

ParameterizedTypeBinding nakedType = super.getParameterizedType(genericType, typeArguments, enclosingType);

if (!haveTypeAnnotations(genericType, enclosingType, typeArguments, annotations))
return nakedType;

TypeBinding parameterizedType = new ParameterizedTypeBinding(genericType, typeArguments, enclosingType, this.environment);
parameterizedType = new ParameterizedTypeBinding(genericType, typeArguments, enclosingType, this.environment);
parameterizedType.id = nakedType.id;
parameterizedType.setTypeAnnotations(annotations, this.isAnnotationBasedNullAnalysisEnabled);
this.parameterizedTypes.put(genericType, typeArguments, enclosingType, parameterizedType);
return (ParameterizedTypeBinding) cacheDerivedType(genericType, nakedType, parameterizedType);
}

Expand Down Expand Up @@ -281,6 +270,9 @@ public TypeBinding getAnnotatedType(TypeBinding type, AnnotationBinding[][] anno
Likewise so the bindings for @Readonly List<@NonNull String> != @Readonly List<@Nullable String> != @Readonly List<@Interned String>
*/
private TypeBinding getAnnotatedType(TypeBinding type, TypeBinding enclosingType, AnnotationBinding[] annotations) {
if (type.kind() == Binding.PARAMETERIZED_TYPE) {
return getParameterizedType(type.actualType(), type.typeArguments(), (ReferenceBinding) enclosingType, annotations);
}
TypeBinding nakedType = null;
TypeBinding[] derivedTypes = getDerivedTypes(type);
for (int i = 0, length = derivedTypes.length; i < length; i++) {
Expand All @@ -295,10 +287,6 @@ private TypeBinding getAnnotatedType(TypeBinding type, TypeBinding enclosingType
if (!derivedType.isArrayType() || derivedType.dimensions() != type.dimensions() || derivedType.leafComponentType() != type.leafComponentType()) //$IDENTITY-COMPARISON$
continue;
break;
case Binding.PARAMETERIZED_TYPE:
if (!derivedType.isParameterizedType() || derivedType.actualType() != type.actualType()) //$IDENTITY-COMPARISON$
continue;
break;
case Binding.RAW_TYPE:
if (!derivedType.isRawType() || derivedType.actualType() != type.actualType()) //$IDENTITY-COMPARISON$
continue;
Expand All @@ -312,7 +300,6 @@ private TypeBinding getAnnotatedType(TypeBinding type, TypeBinding enclosingType
default:
switch(derivedType.kind()) {
case Binding.ARRAY_TYPE:
case Binding.PARAMETERIZED_TYPE:
case Binding.RAW_TYPE:
case Binding.WILDCARD_TYPE:
case Binding.INTERSECTION_CAST_TYPE:
Expand Down Expand Up @@ -344,7 +331,6 @@ private TypeBinding getAnnotatedType(TypeBinding type, TypeBinding enclosingType
case Binding.ARRAY_TYPE:
keyType = type.leafComponentType();
break;
case Binding.PARAMETERIZED_TYPE:
case Binding.RAW_TYPE:
case Binding.WILDCARD_TYPE:
keyType = type.actualType();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup;

import java.util.HashMap;

import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.util.SimpleLookupTable;
import org.eclipse.jdt.internal.compiler.util.Util;
Expand Down Expand Up @@ -57,8 +59,98 @@ derivation in some form (by being type arguments say) get tracked and id'd here.
*/
public class TypeSystem {

public final class HashedParameterizedTypes {

private final class TypeParameterization {

private ReferenceBinding genericType; // unannotated.
private TypeBinding [] typeArguments; // unannotated.
private ReferenceBinding enclosingType; // unannotated

public TypeParameterization(ReferenceBinding genericType, TypeBinding[] typeArguments, ReferenceBinding enclosingType) {
this.genericType = genericType;
this.typeArguments = typeArguments;
this.enclosingType = enclosingType;
}

public boolean equals(Object other) {
TypeParameterization that = (TypeParameterization) other; // homogeneous container.
return this.genericType == that.genericType && this.enclosingType == that.enclosingType && Util.effectivelyEqual(this.typeArguments, that.typeArguments); //$IDENTITY-COMPARISON$
}

public int hashCode() {
int hashCode = this.genericType.hashCode() + 13 * (this.enclosingType != null ? this.enclosingType.hashCode() : 0);
for (int i = 0, length = this.typeArguments == null ? 0 : this.typeArguments.length; i < length; i++) {
hashCode += (i + 1) * this.typeArguments[i].id * this.typeArguments[i].hashCode();
}
return hashCode;
}
}

HashMap<TypeParameterization, ParameterizedTypeBinding []> hashedParameterizedTypes = new HashMap<TypeParameterization, ParameterizedTypeBinding[]>(256);

ParameterizedTypeBinding get(ReferenceBinding genericType, TypeBinding[] typeArguments, ReferenceBinding enclosingType, AnnotationBinding[] annotations) {

ReferenceBinding unannotatedGenericType = (ReferenceBinding) getUnannotatedType(genericType);
int typeArgumentsLength = typeArguments == null ? 0: typeArguments.length;
TypeBinding [] unannotatedTypeArguments = typeArguments == null ? null : new TypeBinding[typeArgumentsLength];
for (int i = 0; i < typeArgumentsLength; i++) {
unannotatedTypeArguments[i] = getUnannotatedType(typeArguments[i]);
}
ReferenceBinding unannotatedEnclosingType = enclosingType == null ? null : (ReferenceBinding) getUnannotatedType(enclosingType);

TypeParameterization typeParameterization = new TypeParameterization(unannotatedGenericType, unannotatedTypeArguments, unannotatedEnclosingType);
ReferenceBinding genericTypeToMatch = unannotatedGenericType, enclosingTypeToMatch = unannotatedEnclosingType;
TypeBinding [] typeArgumentsToMatch = unannotatedTypeArguments;
if (TypeSystem.this instanceof AnnotatableTypeSystem) {
genericTypeToMatch = genericType;
enclosingTypeToMatch = enclosingType;
typeArgumentsToMatch = typeArguments;
}
ParameterizedTypeBinding [] parameterizedTypeBindings = this.hashedParameterizedTypes.get(typeParameterization);
for (int i = 0, length = parameterizedTypeBindings == null ? 0 : parameterizedTypeBindings.length; i < length; i++) {
ParameterizedTypeBinding parameterizedType = parameterizedTypeBindings[i];
if (parameterizedType.actualType() != genericTypeToMatch) { //$IDENTITY-COMPARISON$
continue;
}
if (parameterizedType.enclosingType() != enclosingTypeToMatch //$IDENTITY-COMPARISON$
|| !Util.effectivelyEqual(parameterizedType.typeArguments(), typeArgumentsToMatch))
continue;
if (Util.effectivelyEqual(annotations, parameterizedType.getTypeAnnotations()))
return parameterizedType;
}

return null;
}

void put (ReferenceBinding genericType, TypeBinding[] typeArguments, ReferenceBinding enclosingType, ParameterizedTypeBinding parameterizedType) {
ReferenceBinding unannotatedGenericType = (ReferenceBinding) getUnannotatedType(genericType);
int typeArgumentsLength = typeArguments == null ? 0: typeArguments.length;
TypeBinding [] unannotatedTypeArguments = typeArguments == null ? null : new TypeBinding[typeArgumentsLength];
for (int i = 0; i < typeArgumentsLength; i++) {
unannotatedTypeArguments[i] = getUnannotatedType(typeArguments[i]);
}
ReferenceBinding unannotatedEnclosingType = enclosingType == null ? null : (ReferenceBinding) getUnannotatedType(enclosingType);

TypeParameterization typeParameterization = new TypeParameterization(unannotatedGenericType, unannotatedTypeArguments, unannotatedEnclosingType);

ParameterizedTypeBinding [] parameterizedTypeBindings = this.hashedParameterizedTypes.get(typeParameterization);
int slot;
if (parameterizedTypeBindings == null) {
slot = 0;
parameterizedTypeBindings = new ParameterizedTypeBinding[1];
} else {
slot = parameterizedTypeBindings.length;
System.arraycopy(parameterizedTypeBindings, 0, parameterizedTypeBindings = new ParameterizedTypeBinding[slot + 1], 0, slot);
}
parameterizedTypeBindings[slot] = parameterizedType;
this.hashedParameterizedTypes.put(typeParameterization, parameterizedTypeBindings);
}
}

private int typeid = TypeIds.T_LastWellKnownTypeId;
private TypeBinding [][] types;
protected HashedParameterizedTypes parameterizedTypes; // auxiliary fast lookup table for parameterized types.
private SimpleLookupTable annotationTypes; // cannot store in types, since AnnotationBinding is not a TypeBinding and we don't want types to operate at Binding level.
private LookupEnvironment environment;

Expand All @@ -67,6 +159,7 @@ public TypeSystem(LookupEnvironment environment) {
this.annotationTypes = new SimpleLookupTable(16);
this.typeid = TypeIds.T_LastWellKnownTypeId;
this.types = new TypeBinding[TypeIds.T_LastWellKnownTypeId * 2][];
this.parameterizedTypes = new HashedParameterizedTypes();
}

// Given a type, answer its unannotated aka naked prototype. This is also a convenient way to "register" a type with TypeSystem and have it id stamped.
Expand Down Expand Up @@ -144,32 +237,21 @@ public ParameterizedTypeBinding getParameterizedType(ReferenceBinding genericTyp
unannotatedTypeArguments[i] = getUnannotatedType(typeArguments[i]);
}
ReferenceBinding unannotatedEnclosingType = enclosingType == null ? null : (ReferenceBinding) getUnannotatedType(enclosingType);

TypeBinding[] derivedTypes = this.types[unannotatedGenericType.id];
int i, length = derivedTypes.length;
for (i = 0 ; i < length; i++) {
TypeBinding derivedType = derivedTypes[i];
if (derivedType == null)
break;
if (!derivedType.isParameterizedType() || derivedType.actualType() != unannotatedGenericType || derivedType.hasTypeAnnotations()) //$IDENTITY-COMPARISON$
continue;
if (derivedType.enclosingType() == unannotatedEnclosingType && Util.effectivelyEqual(derivedType.typeArguments(), unannotatedTypeArguments)) //$IDENTITY-COMPARISON$
return (ParameterizedTypeBinding) derivedType;
}

if (i == length) {
System.arraycopy(derivedTypes, 0, derivedTypes = new TypeBinding[length * 2], 0, length);
this.types[unannotatedGenericType.id] = derivedTypes;
}
TypeBinding parameterizedType = derivedTypes[i] = new ParameterizedTypeBinding(unannotatedGenericType, unannotatedTypeArguments, unannotatedEnclosingType, this.environment);

ParameterizedTypeBinding parameterizedType = this.parameterizedTypes.get(unannotatedGenericType, unannotatedTypeArguments, unannotatedEnclosingType, Binding.NO_ANNOTATIONS);
if (parameterizedType != null)
return parameterizedType;

parameterizedType = new ParameterizedTypeBinding(unannotatedGenericType, unannotatedTypeArguments, unannotatedEnclosingType, this.environment);
cacheDerivedType(unannotatedGenericType, parameterizedType);
this.parameterizedTypes.put(genericType, typeArguments, enclosingType, parameterizedType);
int typesLength = this.types.length;
if (this.typeid == typesLength)
System.arraycopy(this.types, 0, this.types = new TypeBinding[typesLength * 2][], 0, typesLength);
this.types[this.typeid] = new TypeBinding[1];
return (ParameterizedTypeBinding) (this.types[parameterizedType.id = this.typeid++][0] = parameterizedType);
}

public ParameterizedTypeBinding getParameterizedType(ReferenceBinding genericType, TypeBinding[] typeArguments, ReferenceBinding enclosingType, AnnotationBinding[] annotations) {
return getParameterizedType(genericType, typeArguments, enclosingType);
}
Expand Down Expand Up @@ -270,10 +352,20 @@ private TypeBinding cacheDerivedType(TypeBinding keyType, TypeBinding derivedTyp
throw new IllegalStateException();

TypeBinding[] derivedTypes = this.types[keyType.id];
int i = 0, length = derivedTypes.length;
while (i < length && derivedTypes[i] != null) {
i++;
}
// binary search for the *earliest* slot with a null reference. By design and construction, a null value will never be followed by a valid derived type.
int first, last,length = derivedTypes.length;
first = 0; last = length;
int i = (first + last) / 2;
do {
if (derivedTypes[i] == null) {
if (i == first || i > 0 && derivedTypes[i - 1] != null)
break;
last = i - 1;
} else {
first = i + 1;
}
i = (first + last) / 2;
} while (i < length && first <= last);
if (i == length) {
System.arraycopy(derivedTypes, 0, derivedTypes = new TypeBinding[length * 2], 0, length);
this.types[keyType.id] = derivedTypes;
Expand Down

0 comments on commit 510e796

Please sign in to comment.