Bug 521438 - [1.8] Slow compilation in project with many wildcards
generics

Change-Id: Ifed39611382762a70b71c15890ae502ec9735a5a
(cherry picked from commit 48a3a3eb7ea3d5a31b91fda5c892849f5bc403c6)
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/AnnotatableTypeSystem.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/AnnotatableTypeSystem.java
index 14489de..54d0a7a 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/AnnotatableTypeSystem.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/AnnotatableTypeSystem.java
@@ -176,7 +176,8 @@
 			throw new IllegalStateException();
 		
 		WildcardBinding nakedType = null;
-		TypeBinding[] derivedTypes = getDerivedTypes(genericType);
+		boolean useDerivedTypesOfBound = bound instanceof TypeVariableBinding || bound instanceof ParameterizedTypeBinding;
+		TypeBinding[] derivedTypes = getDerivedTypes(useDerivedTypesOfBound ? bound : genericType);
 		for (int i = 0, length = derivedTypes.length; i < length; i++) {
 			TypeBinding derivedType = derivedTypes[i];
 			if (derivedType == null) 
@@ -200,7 +201,7 @@
 		WildcardBinding wildcard = new WildcardBinding(genericType, rank, bound, otherBounds, boundKind, this.environment);
 		wildcard.id = nakedType.id;
 		wildcard.setTypeAnnotations(annotations, this.isAnnotationBasedNullAnalysisEnabled);
-		return (WildcardBinding) cacheDerivedType(genericType, nakedType, wildcard);
+		return (WildcardBinding) cacheDerivedType(useDerivedTypesOfBound ? bound : genericType, nakedType, wildcard);
 	}
 
 	public WildcardBinding getWildcard(ReferenceBinding genericType, int rank, TypeBinding bound, TypeBinding[] otherBounds, int boundKind) {
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeSystem.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeSystem.java
index a50f020..d63ca66 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeSystem.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeSystem.java
@@ -65,27 +65,67 @@
 	
 	public final class HashedParameterizedTypes {
 		
-		private final class InternalParameterizedTypeBinding extends ParameterizedTypeBinding {
-						
-			public InternalParameterizedTypeBinding(ReferenceBinding genericType, TypeBinding[] typeArguments, ReferenceBinding enclosingType, LookupEnvironment environment) {
-				super(genericType, typeArguments, enclosingType, environment);
+		private final class PTBKey extends ReferenceBinding { // extends ReferenceBinding so it can be used as wrapper
+			protected ReferenceBinding type; // must ensure the type is resolved
+			public TypeBinding[] arguments;
+			private ReferenceBinding enclosingType;
+			public PTBKey(ReferenceBinding type, TypeBinding[] arguments, ReferenceBinding enclosingType, LookupEnvironment environment) {
+				this.type = type;
+				this.arguments = arguments;
+				this.enclosingType = enclosingType;
+
+				if(environment != null) {
+					// only add as wrapper when used in put()
+					if (type instanceof UnresolvedReferenceBinding)
+						((UnresolvedReferenceBinding) type).addWrapper(this, environment);
+					if (arguments != null) {
+						for (int i = 0, l = arguments.length; i < l; i++) {
+							if (arguments[i] instanceof UnresolvedReferenceBinding)
+								((UnresolvedReferenceBinding) arguments[i]).addWrapper(this, environment);
+							if (arguments[i].hasNullTypeAnnotations())
+								this.tagBits |= TagBits.HasNullTypeAnnotation;
+						}
+					}
+				}
 			}
-			
+			@Override
+			public void swapUnresolved(UnresolvedReferenceBinding unresolvedType, ReferenceBinding resolvedType, LookupEnvironment env) {
+				if (this.type == unresolvedType) { //$IDENTITY-COMPARISON$
+					this.type = resolvedType; // cannot be raw since being parameterized below
+					ReferenceBinding enclosing = resolvedType.enclosingType();
+					if (enclosing != null) {
+						this.enclosingType = (ReferenceBinding) env.convertUnresolvedBinaryToRawType(enclosing); // needed when binding unresolved member type
+					}
+				}
+				if (this.arguments != null) {
+					for (int i = 0, l = this.arguments.length; i < l; i++) {
+						if (this.arguments[i] == unresolvedType) { //$IDENTITY-COMPARISON$
+							this.arguments[i] = env.convertUnresolvedBinaryToRawType(resolvedType);
+						}
+					}
+				}
+			}
 			public boolean equals(Object other) {
-				ParameterizedTypeBinding that = (ParameterizedTypeBinding) other;  // homogeneous container. 
+				PTBKey that = (PTBKey) other;  // homogeneous container. 
 				return this.type == that.type && this.enclosingType == that.enclosingType && Util.effectivelyEqual(this.arguments, that.arguments); //$IDENTITY-COMPARISON$
 			}
-			
+			final int hash(TypeBinding b) {
+				if(b instanceof WildcardBinding || b instanceof TypeVariableBinding) {
+					return System.identityHashCode(b);
+				}
+				return b.hashCode();
+			}
 			public int hashCode() {
-				int hashCode = this.type.hashCode() + 13 * (this.enclosingType != null ? this.enclosingType.hashCode() : 0);
+				final int prime=31;
+				int hashCode = 1 + hash(this.type) + (this.enclosingType != null ? hash(this.enclosingType) : 0);
 				for (int i = 0, length = this.arguments == null ? 0 : this.arguments.length; i < length; i++) {
-					hashCode += (i + 1) * this.arguments[i].hashCode();
+					hashCode = hashCode * prime + hash(this.arguments[i]);
 				}
 				return hashCode;
 			}
 		}
 		
-		HashMap<ParameterizedTypeBinding, ParameterizedTypeBinding []> hashedParameterizedTypes = new HashMap<ParameterizedTypeBinding, ParameterizedTypeBinding[]>(256);
+		HashMap<PTBKey, ParameterizedTypeBinding []> hashedParameterizedTypes = new HashMap<>(256);
 
 		ParameterizedTypeBinding get(ReferenceBinding genericType, TypeBinding[] typeArguments, ReferenceBinding enclosingType, AnnotationBinding[] annotations) {
 			
@@ -97,7 +137,7 @@
 			}
 			ReferenceBinding unannotatedEnclosingType = enclosingType == null ? null : (ReferenceBinding) getUnannotatedType(enclosingType);
 			
-			ParameterizedTypeBinding typeParameterization = new InternalParameterizedTypeBinding(unannotatedGenericType, unannotatedTypeArguments, unannotatedEnclosingType, TypeSystem.this.environment);
+			PTBKey key = new PTBKey(unannotatedGenericType, unannotatedTypeArguments, unannotatedEnclosingType, null);
 			ReferenceBinding genericTypeToMatch = unannotatedGenericType, enclosingTypeToMatch = unannotatedEnclosingType;
 			TypeBinding [] typeArgumentsToMatch = unannotatedTypeArguments;
 			if (TypeSystem.this instanceof AnnotatableTypeSystem) {
@@ -105,7 +145,7 @@
 				enclosingTypeToMatch = enclosingType;
 				typeArgumentsToMatch = typeArguments;
 			}
-			ParameterizedTypeBinding [] parameterizedTypeBindings = this.hashedParameterizedTypes.get(typeParameterization);
+			ParameterizedTypeBinding [] parameterizedTypeBindings = this.hashedParameterizedTypes.get(key);
 			for (int i = 0, length = parameterizedTypeBindings == null ? 0 : parameterizedTypeBindings.length; i < length; i++) {
 				ParameterizedTypeBinding parameterizedType = parameterizedTypeBindings[i];
 				if (parameterizedType.actualType() != genericTypeToMatch) { //$IDENTITY-COMPARISON$
@@ -130,9 +170,9 @@
 			}
 			ReferenceBinding unannotatedEnclosingType = enclosingType == null ? null : (ReferenceBinding) getUnannotatedType(enclosingType);
 			
-			ParameterizedTypeBinding typeParameterization = new InternalParameterizedTypeBinding(unannotatedGenericType, unannotatedTypeArguments, unannotatedEnclosingType, TypeSystem.this.environment);
+			PTBKey key = new PTBKey(unannotatedGenericType, unannotatedTypeArguments, unannotatedEnclosingType, TypeSystem.this.environment);
 			
-			ParameterizedTypeBinding [] parameterizedTypeBindings = this.hashedParameterizedTypes.get(typeParameterization);
+			ParameterizedTypeBinding [] parameterizedTypeBindings = this.hashedParameterizedTypes.get(key);
 			int slot;
 			if (parameterizedTypeBindings == null) {
 				slot = 0;
@@ -142,7 +182,7 @@
 				System.arraycopy(parameterizedTypeBindings, 0, parameterizedTypeBindings = new ParameterizedTypeBinding[slot + 1], 0, slot);
 			}
 			parameterizedTypeBindings[slot] = parameterizedType;
-			this.hashedParameterizedTypes.put(typeParameterization, parameterizedTypeBindings);
+			this.hashedParameterizedTypes.put(key, parameterizedTypeBindings);
 		}
 	}	
 	
@@ -342,7 +382,9 @@
 		}
 		TypeBinding unannotatedBound = bound == null ? null : getUnannotatedType(bound);
 
-		TypeBinding[] derivedTypes = this.types[unannotatedGenericType.id];  // by construction, cachedInfo != null now.
+		boolean useDerivedTypesOfBound = unannotatedBound instanceof TypeVariableBinding || unannotatedBound instanceof ParameterizedTypeBinding;
+		TypeBinding[] derivedTypes = this.types[useDerivedTypesOfBound ? unannotatedBound.id :unannotatedGenericType.id];  // by construction, cachedInfo != null now.
+
 		int i, length = derivedTypes.length;
 		for (i = 0; i < length; i++) {
 			TypeBinding derivedType = derivedTypes[i];
@@ -358,7 +400,7 @@
 		
 		if (i == length) {
 			System.arraycopy(derivedTypes, 0, derivedTypes = new TypeBinding[length * 2], 0, length);
-			this.types[unannotatedGenericType.id] = derivedTypes;
+			this.types[useDerivedTypesOfBound ? unannotatedBound.id :unannotatedGenericType.id] = derivedTypes;
 		}
 		TypeBinding wildcard = derivedTypes[i] = new WildcardBinding(unannotatedGenericType, rank, unannotatedBound, unannotatedOtherBounds, boundKind, this.environment);