| /******************************************************************************* |
| * Copyright (c) 2013, 2019 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| * Stephan Herrmann - Contribution for |
| * Bug 434602 - Possible error with inferred null annotations leading to contradictory null annotations |
| * Bug 456497 - [1.8][null] during inference nullness from target type is lost against weaker hint from applicability analysis |
| * Bug 456487 - [1.8][null] @Nullable type variant of @NonNull-constrained type parameter causes grief |
| * Till Brychcy - Contribution for |
| * Bug 473713 - [1.8][null] Type mismatch: cannot convert from @NonNull A1 to @NonNull A1 |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.compiler.lookup; |
| |
| import java.util.HashMap; |
| |
| import org.eclipse.jdt.core.compiler.CharOperation; |
| import org.eclipse.jdt.internal.compiler.ast.ASTNode; |
| import org.eclipse.jdt.internal.compiler.util.SimpleLookupTable; |
| import org.eclipse.jdt.internal.compiler.util.Util; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.DependentTypeBinding; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.ITeamAnchor; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.RoleTypeBinding; |
| |
| /* TypeSystem: An abstraction responsible for keeping track of types that undergo "derivation" of some sort and the derived types produced thus. |
| Here we use the term derivation in the Pascal sense and not per object oriented parlance. |
| |
| As of Java SE8, a type can undergo derivation in a bunch of ways: |
| |
| - By being created arrays out of, |
| - By being parameterized, |
| - By being created raw forms of, |
| - By being the generic type which a wildcard type or an intersection type parameterizes, |
| - By being annotated. |
| |
| It is the responsibility of the TypeSystem to serve as the factory and ensure that unique types are created and maintained. Most of the |
| compiler depends on object identity given the derivation parameters are the same. E.g: If we dole out non-unique ParameterizedTypeBinding's |
| for two attempts to create List<String>, then one cannot be assigned to the other. |
| |
| Till Java SE7, we could manage to create a single binding for a type - not so with annotations coming into the picture. In order for |
| two uses of the same type to be annotated differently, the bindings for them need to be distinct and cannot be shared. If we start |
| doling out different bindings, then validating type identity and equivalence becomes an issue. |
| |
| What we do to solve the problem is produce different bindings when they need to be annotated differently, but stamp them with the |
| same id (TypeBinding#id). Thus types that fail == or != could quickly be ascertained to be mere annotation variants by comparing |
| the id field. |
| |
| This class is responsible for id stamping unique types. Only those types that are "derived from" in some form or participate in the |
| derivation in some form (by being type arguments say) get tracked and id'd here. A type which is not thus derived from in one form or |
| the other or participate in the derivation thus - we are completely oblivious to. |
| |
| TypeBinding.id computation: For primitive types and certain "well known" types, id assignment happens elsewhere. Here we start with an |
| id value that is suitably high and proceed monotonically upwards so we will not accidentally collide with the id space in use already. |
| id assignments happens in such a way that a naked type and its annotated variants - variously annotated - would all share the same id. |
| Example: @T1 Map<@T2 String, @T3 Object> and Map<@T4 String, @T5 Object> and @T6 Map<String, Object> and @T7 Map<String, @T8 Object> and |
| Map<String, @T9 Object> would all share the same id since the unadorned naked type in each case is the same: Map<String, Object>. None |
| of this would share the id with Map<String, String>. Briefly put, if you take a certain annotated type and strip it of all annotations |
| to come up with the naked type, that naked type and the annotated type would have the same id. Alternately, if you take a certain naked |
| type and arrive at the universe of all differently annotated types, they would all share the same id while their bindings could be different - |
| would be different unless they are identically annotated. |
| |
| Thus subsystems that are annotation agnostic could quickly ascertain binding equality by comparing the id field. |
| */ |
| public class TypeSystem { |
| |
| public final class HashedParameterizedTypes { |
| |
| 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; |
| //{ObjectTeams: announce dependent types as parameterizations, too: |
| ITeamAnchor teamAnchor; |
| int valueParamPosition = -1; |
| public PTBKey(ReferenceBinding type, TypeBinding[] arguments, ITeamAnchor teamAnchor, int valueParamPosition, |
| ReferenceBinding enclosingType, LookupEnvironment environment) { |
| /* orig: |
| public PTBKey(ReferenceBinding type, TypeBinding[] arguments, ReferenceBinding enclosingType, LookupEnvironment environment) { |
| */ |
| // :giro |
| this.teamAnchor = teamAnchor; |
| this.valueParamPosition = valueParamPosition; |
| // SH} |
| 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 = resolvedType.isStatic() ? enclosing : (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); |
| } |
| } |
| } |
| } |
| @Override |
| public boolean equals(Object other) { |
| PTBKey that = (PTBKey) other; // homogeneous container. |
| //{ObjectTeams: more checks: |
| int thatValueParamPos = that.valueParamPosition; |
| ITeamAnchor thatAnchor = that.teamAnchor; |
| if (this.teamAnchor != thatAnchor) { |
| if (this.teamAnchor == null || thatAnchor == null || !this.teamAnchor.hasSameBestNameAs(thatAnchor)) |
| return false; |
| } |
| if (this.valueParamPosition != thatValueParamPos) return false; |
| // SH} |
| 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 || b.getClass() == ParameterizedTypeBinding.class) { |
| return System.identityHashCode(b); |
| } |
| return b.hashCode(); |
| } |
| @Override |
| public int hashCode() { |
| final int prime=31; |
| int hashCode = 1 + hash(this.type); |
| for (int i = 0, length = this.arguments == null ? 0 : this.arguments.length; i < length; i++) { |
| hashCode = hashCode * prime + hash(this.arguments[i]); |
| } |
| //{ObjectTeams: more |
| if (this.teamAnchor != null) |
| hashCode += 3 * CharOperation.hashCode(this.teamAnchor.getBestName()); |
| // don't use getBaseNamePath() to avoid differences due to fake replica of FieldBindings |
| if (this.valueParamPosition != -1) hashCode += 5 * this.valueParamPosition; |
| // SH} |
| return hashCode; |
| } |
| } |
| |
| HashMap<PTBKey, ParameterizedTypeBinding []> hashedParameterizedTypes = new HashMap<>(256); |
| |
| //{ObjectTeams: more args: |
| /* orig: |
| ParameterizedTypeBinding get(ReferenceBinding genericType, TypeBinding[] typeArguments, ReferenceBinding enclosingType, AnnotationBinding[] annotations) { |
| :giro */ |
| ParameterizedTypeBinding get(ReferenceBinding genericType, TypeBinding[] typeArguments, ITeamAnchor anchor, int valueParamPosition, |
| ReferenceBinding enclosingType, AnnotationBinding[] annotations) { |
| // orig: |
| |
| 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); |
| |
| /* |
| PTBKey key = new PTBKey(unannotatedGenericType, unannotatedTypeArguments, unannotatedEnclosingType, null); |
| :giro */ |
| PTBKey key = new PTBKey(unannotatedGenericType, unannotatedTypeArguments, anchor, valueParamPosition, |
| unannotatedEnclosingType, null); |
| // SH} |
| ReferenceBinding genericTypeToMatch = unannotatedGenericType, enclosingTypeToMatch = unannotatedEnclosingType; |
| TypeBinding [] typeArgumentsToMatch = unannotatedTypeArguments; |
| if (TypeSystem.this instanceof AnnotatableTypeSystem) { |
| genericTypeToMatch = genericType; |
| enclosingTypeToMatch = enclosingType; |
| typeArgumentsToMatch = typeArguments; |
| } |
| 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$ |
| continue; |
| } |
| if (parameterizedType.enclosingType != enclosingTypeToMatch //$IDENTITY-COMPARISON$ |
| || !Util.effectivelyEqual(parameterizedType.typeArguments(), typeArgumentsToMatch)) |
| continue; |
| if (Util.effectivelyEqual(annotations, parameterizedType.getTypeAnnotations())) |
| return parameterizedType; |
| } |
| |
| return null; |
| } |
| |
| //{ObjectTeams: more args: |
| /* orig: |
| void put (ReferenceBinding genericType, TypeBinding[] typeArguments, ReferenceBinding enclosingType, ParameterizedTypeBinding parameterizedType) { |
| :giro */ |
| void put (ReferenceBinding genericType, TypeBinding[] typeArguments, ITeamAnchor anchor, int valueParamPosition, |
| ReferenceBinding enclosingType, ParameterizedTypeBinding parameterizedType) { |
| // orig: |
| 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); |
| |
| /* |
| PTBKey key = new PTBKey(unannotatedGenericType, unannotatedTypeArguments, unannotatedEnclosingType, TypeSystem.this.environment); |
| :giro */ |
| PTBKey key = new PTBKey(unannotatedGenericType, unannotatedTypeArguments, anchor, valueParamPosition, |
| unannotatedEnclosingType, TypeSystem.this.environment); |
| // SH} |
| |
| ParameterizedTypeBinding [] parameterizedTypeBindings = this.hashedParameterizedTypes.get(key); |
| 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(key, 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. |
| LookupEnvironment environment; |
| |
| public TypeSystem(LookupEnvironment environment) { |
| this.environment = 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. |
| public final TypeBinding getUnannotatedType(TypeBinding type) { |
| UnresolvedReferenceBinding urb = null; |
| if (type.isUnresolvedType()) { |
| urb = (UnresolvedReferenceBinding) type; |
| ReferenceBinding resolvedType = urb.resolvedType; |
| if (resolvedType != null) { |
| type = resolvedType; |
| } |
| } |
| try { |
| if (type.id == TypeIds.NoId) { |
| if (type.hasTypeAnnotations()) |
| throw new IllegalStateException(); |
| int typesLength = this.types.length; |
| if (this.typeid == typesLength) |
| System.arraycopy(this.types, 0, this.types = new TypeBinding[typesLength * 2][], 0, typesLength); |
| this.types[type.id = this.typeid++] = new TypeBinding[4]; |
| } else { |
| TypeBinding nakedType = this.types[type.id] == null ? null : this.types[type.id][0]; |
| if (type.hasTypeAnnotations() && nakedType == null) |
| throw new IllegalStateException(); |
| if (nakedType != null) |
| return nakedType; |
| this.types[type.id] = new TypeBinding[4]; // well known type, assigned id elsewhere. |
| } |
| } finally { |
| if (urb != null && urb.id == TypeIds.NoId) |
| urb.id = type.id; |
| } |
| |
| return this.types[type.id][0] = type; |
| } |
| |
| /** |
| * Forcefully register the given type as a derived type. |
| * If it itself is already registered as the key unannotated type of its family, |
| * create a clone to play that role from now on and swap types in the types cache. |
| */ |
| public void forceRegisterAsDerived(TypeBinding derived) { |
| int id = derived.id; |
| if (id != TypeIds.NoId && this.types[id] != null) { |
| TypeBinding unannotated = this.types[id][0]; |
| if (unannotated == derived) { //$IDENTITY-COMPARISON$ |
| // was previously registered as unannotated, replace by a fresh clone to remain unannotated: |
| this.types[id][0] = unannotated = derived.clone(null); |
| } |
| // proceed as normal: |
| cacheDerivedType(unannotated, derived); |
| } else { |
| throw new IllegalStateException("Type was not yet registered as expected: "+derived); //$NON-NLS-1$ |
| } |
| } |
| |
| // Given a type, return all its variously annotated versions. |
| public TypeBinding[] getAnnotatedTypes(TypeBinding type) { |
| return Binding.NO_TYPES; |
| } |
| |
| /* Note: parameters will not have type type annotations if lookup environment directly uses TypeSystem as its typeSystem. When ATS is used however |
| they may be annotated and we need to materialize the unannotated versions and work on them. |
| |
| See ArrayBinding.swapUnresolved for further special case handling if incoming leafType is a URB that would resolve to a raw type later. |
| */ |
| public ArrayBinding getArrayType(TypeBinding leafType, int dimensions) { |
| if (leafType instanceof ArrayBinding) { |
| dimensions += leafType.dimensions(); |
| leafType = leafType.leafComponentType(); |
| } |
| TypeBinding unannotatedLeafType = getUnannotatedType(leafType); |
| TypeBinding[] derivedTypes = this.types[unannotatedLeafType.id]; |
| int i, length = derivedTypes.length; |
| for (i = 0; i < length; i++) { |
| TypeBinding derivedType = derivedTypes[i]; |
| if (derivedType == null) |
| break; |
| if (!derivedType.isArrayType() || derivedType.hasTypeAnnotations()) |
| continue; |
| if (derivedType.leafComponentType() == unannotatedLeafType && derivedType.dimensions() == dimensions) //$IDENTITY-COMPARISON$ |
| return (ArrayBinding) derivedType; |
| } |
| if (i == length) { |
| System.arraycopy(derivedTypes, 0, derivedTypes = new TypeBinding[length * 2], 0, length); |
| this.types[unannotatedLeafType.id] = derivedTypes; |
| } |
| TypeBinding arrayType = derivedTypes[i] = new ArrayBinding(unannotatedLeafType, dimensions, this.environment); |
| 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 (ArrayBinding) (this.types[arrayType.id = this.typeid++][0] = arrayType); |
| } |
| |
| public ArrayBinding getArrayType(TypeBinding leafComponentType, int dimensions, AnnotationBinding[] annotations) { |
| return getArrayType(leafComponentType, dimensions); |
| } |
| |
| public ReferenceBinding getMemberType(ReferenceBinding memberType, ReferenceBinding enclosingType) { |
| return memberType; // nothing to do for plain vanilla type system, they are already hooked. |
| } |
| |
| /* Note: parameters will not have type type annotations if lookup environment directly uses TypeSystem. When AnnotatableTypeSystem is in use |
| they may and we need to materialize the unannotated versions and work on them. |
| */ |
| public ParameterizedTypeBinding getParameterizedType(ReferenceBinding genericType, TypeBinding[] typeArguments, ReferenceBinding enclosingType) { |
| //{ObjectTeams: more arguments for role types: |
| return getParameterizedType(genericType, typeArguments, null, -1, enclosingType); |
| } |
| public ParameterizedTypeBinding getParameterizedType(ReferenceBinding genericType, TypeBinding[] typeArguments, ITeamAnchor teamAnchor, int valueParamPosition, |
| ReferenceBinding enclosingType) { |
| if (teamAnchor == null && genericType instanceof DependentTypeBinding) |
| teamAnchor = ((DependentTypeBinding) genericType)._teamAnchor; |
| // SH} |
| |
| ReferenceBinding unannotatedGenericType = (ReferenceBinding) getUnannotatedType(genericType); |
| // getUnannotatedType may have replaced URB by resolvedType |
| if (enclosingType == null && genericType instanceof UnresolvedReferenceBinding |
| && !(unannotatedGenericType instanceof UnresolvedReferenceBinding)) { |
| enclosingType = unannotatedGenericType.enclosingType(); |
| } |
| 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); |
| |
| //{ObjectTeams: more arguments: |
| /* orig: |
| 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); |
| :giro */ |
| ParameterizedTypeBinding parameterizedType = this.parameterizedTypes.get(unannotatedGenericType, unannotatedTypeArguments, teamAnchor, valueParamPosition, |
| unannotatedEnclosingType, Binding.NO_ANNOTATIONS); |
| if (parameterizedType != null) |
| return parameterizedType; |
| |
| // dependent type? |
| if (teamAnchor == null) { |
| parameterizedType = new ParameterizedTypeBinding(unannotatedGenericType, unannotatedTypeArguments, unannotatedEnclosingType, this.environment); |
| } else { |
| if (unannotatedTypeArguments == null && unannotatedGenericType.isGenericType()) |
| unannotatedGenericType = (ReferenceBinding) this.environment.convertToRawType(unannotatedGenericType, false); // half-raw: teamAnchor but missing regular type args |
| if (genericType.isRole()) { |
| parameterizedType = new RoleTypeBinding(unannotatedGenericType, unannotatedTypeArguments, teamAnchor, unannotatedEnclosingType, this.environment); |
| } else { |
| parameterizedType = new DependentTypeBinding(unannotatedGenericType, unannotatedTypeArguments, teamAnchor, valueParamPosition, unannotatedEnclosingType, this.environment); |
| } |
| } |
| cacheDerivedType(unannotatedGenericType, parameterizedType); |
| // more arguments: |
| this.parameterizedTypes.put(genericType, typeArguments, teamAnchor, valueParamPosition, |
| enclosingType, parameterizedType); |
| // SH} |
| 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); |
| } |
| |
| //{ObjectTeams: more parameters: |
| /* orig: |
| public ParameterizedTypeBinding getParameterizedType(ReferenceBinding genericType, TypeBinding[] typeArguments, ReferenceBinding enclosingType, AnnotationBinding[] annotations) { |
| return getParameterizedType(genericType, typeArguments, enclosingType); |
| :giro */ |
| public ParameterizedTypeBinding getParameterizedType(ReferenceBinding genericType, TypeBinding[] typeArguments, ITeamAnchor teamAnchor, int valueParamPosition, |
| ReferenceBinding enclosingType, AnnotationBinding[] annotations) { |
| return getParameterizedType(genericType, typeArguments, teamAnchor, valueParamPosition, |
| enclosingType); |
| // SH} |
| } |
| |
| /* Note: Parameters will not have type type annotations if lookup environment directly uses TypeSystem. However when AnnotatableTypeSystem is in use, |
| they may and we need to materialize the unannotated versions and work on them. |
| */ |
| public RawTypeBinding getRawType(ReferenceBinding genericType, ReferenceBinding enclosingType) { |
| if (!genericType.hasEnclosingInstanceContext() && enclosingType != null) { |
| enclosingType = (ReferenceBinding) enclosingType.original(); |
| } |
| ReferenceBinding unannotatedGenericType = (ReferenceBinding) getUnannotatedType(genericType); |
| 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.isRawType() || derivedType.actualType() != unannotatedGenericType || derivedType.hasTypeAnnotations()) //$IDENTITY-COMPARISON$ |
| continue; |
| if (derivedType.enclosingType() == unannotatedEnclosingType) //$IDENTITY-COMPARISON$ |
| //{ObjectTeams: TODO: (how) can we handle half-raw dependent types? SH} |
| return (RawTypeBinding) derivedType; |
| } |
| |
| if (i == length) { |
| System.arraycopy(derivedTypes, 0, derivedTypes = new TypeBinding[length * 2], 0, length); |
| this.types[unannotatedGenericType.id] = derivedTypes; |
| } |
| |
| if(unannotatedGenericType.isStatic() && unannotatedEnclosingType != null) { |
| unannotatedEnclosingType=(ReferenceBinding) unannotatedEnclosingType.original(); |
| } |
| TypeBinding rawTytpe = derivedTypes[i] = new RawTypeBinding(unannotatedGenericType, unannotatedEnclosingType, this.environment); |
| 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 (RawTypeBinding) (this.types[rawTytpe.id = this.typeid++][0] = rawTytpe); |
| } |
| |
| public RawTypeBinding getRawType(ReferenceBinding genericType, ReferenceBinding enclosingType, AnnotationBinding[] annotations) { |
| return getRawType(genericType, enclosingType); |
| } |
| |
| /* Parameters will not have type type annotations if lookup environment directly uses TypeSystem. When AnnotatableTypeSystem is in use, |
| they may and we need to materialize the unannotated versions and work on them. |
| */ |
| public WildcardBinding getWildcard(ReferenceBinding genericType, int rank, TypeBinding bound, TypeBinding[] otherBounds, int boundKind) { |
| if (genericType == null) // pseudo wildcard denoting composite bounds for lub computation |
| genericType = ReferenceBinding.LUB_GENERIC; |
| |
| ReferenceBinding unannotatedGenericType = (ReferenceBinding) getUnannotatedType(genericType); |
| int otherBoundsLength = otherBounds == null ? 0: otherBounds.length; |
| TypeBinding [] unannotatedOtherBounds = otherBounds == null ? null : new TypeBinding[otherBoundsLength]; |
| for (int i = 0; i < otherBoundsLength; i++) { |
| unannotatedOtherBounds[i] = getUnannotatedType(otherBounds[i]); |
| } |
| TypeBinding unannotatedBound = bound == null ? null : getUnannotatedType(bound); |
| |
| boolean useDerivedTypesOfBound = unannotatedBound instanceof TypeVariableBinding || (unannotatedBound instanceof ParameterizedTypeBinding && !(unannotatedBound instanceof RawTypeBinding)); |
| 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]; |
| if (derivedType == null) |
| break; |
| if (!derivedType.isWildcard() || derivedType.actualType() != unannotatedGenericType || derivedType.hasTypeAnnotations()) //$IDENTITY-COMPARISON$ |
| continue; |
| if (derivedType.rank() != rank || derivedType.boundKind() != boundKind || derivedType.bound() != unannotatedBound) //$IDENTITY-COMPARISON$ |
| continue; |
| if (Util.effectivelyEqual(derivedType.additionalBounds(), unannotatedOtherBounds)) |
| return (WildcardBinding) derivedType; |
| } |
| |
| if (i == length) { |
| System.arraycopy(derivedTypes, 0, derivedTypes = new TypeBinding[length * 2], 0, length); |
| this.types[useDerivedTypesOfBound ? unannotatedBound.id :unannotatedGenericType.id] = derivedTypes; |
| } |
| TypeBinding wildcard = derivedTypes[i] = new WildcardBinding(unannotatedGenericType, rank, unannotatedBound, unannotatedOtherBounds, boundKind, this.environment); |
| |
| 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 (WildcardBinding) (this.types[wildcard.id = this.typeid++][0] = wildcard); |
| } |
| |
| // No need for an override in ATS, since interning is position specific and either the wildcard there is annotated or not. |
| public final CaptureBinding getCapturedWildcard(WildcardBinding wildcard, ReferenceBinding contextType, int start, int end, ASTNode cud, int id) { |
| |
| WildcardBinding unannotatedWildcard = (WildcardBinding) getUnannotatedType(wildcard); |
| TypeBinding[] derivedTypes = this.types[unannotatedWildcard.id]; // by construction, cachedInfo != null now. |
| int i, length = derivedTypes.length; |
| |
| /* Search backwards looking at recent captures, if we encounter a capture from a different compilation unit, this is a fresh uninterned capture. |
| While compiling one file, we may reach into another file to build structure, we should not compile method bodies there, so we expect to see |
| all captures from the same file together without being interleaved by captures from other files. |
| */ |
| int nullSlot = length; |
| for (i = length - 1; i >= -1; --i) { |
| if (i == -1) { |
| i = nullSlot; |
| break; |
| } |
| TypeBinding derivedType = derivedTypes[i]; |
| if (derivedType == null) { |
| nullSlot = i; |
| continue; |
| } |
| if (!derivedType.isCapture()) |
| continue; |
| CaptureBinding prior = (CaptureBinding) derivedType; |
| if (prior.cud != cud) { // Searching further to the left is futile, exit the loop. |
| i = nullSlot; |
| break; |
| } |
| if (prior.sourceType != contextType || prior.start != start || prior.end != end) //$IDENTITY-COMPARISON$ |
| continue; |
| return prior; |
| } |
| |
| if (i == length) { |
| System.arraycopy(derivedTypes, 0, derivedTypes = new TypeBinding[length * 2], 0, length); |
| this.types[unannotatedWildcard.id] = derivedTypes; |
| } |
| return (CaptureBinding) (derivedTypes[i] = new CaptureBinding(wildcard, contextType, start, end, cud, id)); |
| // the above constructor already registers the capture, don't repeat that here |
| } |
| |
| public WildcardBinding getWildcard(ReferenceBinding genericType, int rank, TypeBinding bound, TypeBinding[] otherBounds, int boundKind, AnnotationBinding[] annotations) { |
| return getWildcard(genericType, rank, bound, otherBounds, boundKind); |
| } |
| |
| public TypeBinding getAnnotatedType(TypeBinding type, AnnotationBinding[][] annotations) { |
| return type; // Nothing to do for plain vanilla type system. |
| } |
| |
| protected final TypeBinding /* @NonNull */ [] getDerivedTypes(TypeBinding keyType) { |
| keyType = getUnannotatedType(keyType); |
| return this.types[keyType.id]; |
| } |
| |
| private TypeBinding cacheDerivedType(TypeBinding keyType, TypeBinding derivedType) { |
| if (keyType == null || derivedType == null || keyType.id == TypeIds.NoId) |
| throw new IllegalStateException(); |
| |
| TypeBinding[] derivedTypes = this.types[keyType.id]; |
| // 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; |
| } |
| return derivedTypes[i] = derivedType; |
| } |
| |
| protected final TypeBinding cacheDerivedType(TypeBinding keyType, TypeBinding nakedType, TypeBinding derivedType) { |
| |
| /* Cache the derived type, tagging it as a derivative of both the key type and the naked type. |
| E.g: int @NonNull [] would be tagged as a derived type of both int and int []. This is not |
| needed for correctness, but for annotated object reuse. We provide two alternate ways to |
| annotate a type: |
| |
| Taking parameterized types as an example, a call to getParamaterizedType can be made with annotations |
| to create @NonNull List<@NonNull String> in one stroke. Or a parameterized type can be created first |
| and then annotated via getAnnotatedType. In the former case, the tables get looked up with List as |
| the key, in the latter with List<String> as the key. |
| |
| Binary vs source, substitutions, annotation re-attribution from SE7 locations etc trigger these |
| alternate code paths. Unless care is exercised, we will end up with duplicate objects (that share |
| the same TypeBinding.id => correctness is not an issue, but memory wastage is) |
| */ |
| cacheDerivedType(keyType, derivedType); |
| if (nakedType.id != keyType.id) { |
| cacheDerivedType(nakedType, derivedType); |
| } |
| return derivedType; |
| } |
| |
| /* Return a unique annotation binding for an annotation with either no or all default element-value pairs. |
| We may return a resolved annotation when requested for unresolved one, but not vice versa. |
| */ |
| public final AnnotationBinding getAnnotationType(ReferenceBinding annotationType, boolean requiredResolved) { |
| AnnotationBinding annotation = (AnnotationBinding) this.annotationTypes.get(annotationType); |
| if (annotation == null) { |
| if (requiredResolved) |
| annotation = new AnnotationBinding(annotationType, Binding.NO_ELEMENT_VALUE_PAIRS); |
| else |
| annotation = new UnresolvedAnnotationBinding(annotationType, Binding.NO_ELEMENT_VALUE_PAIRS, this.environment); |
| this.annotationTypes.put(annotationType, annotation); |
| } |
| if (requiredResolved) |
| annotation.resolve(); |
| return annotation; |
| } |
| |
| public boolean isAnnotatedTypeSystem() { |
| return false; |
| } |
| |
| public void cleanUp(int typeId) { |
| if (typeId != -1 && typeId < this.typeid && this.types != null) { |
| TypeBinding[] typesForId = this.types[typeId]; |
| if (typesForId != null) { |
| for (TypeBinding type : typesForId) |
| if (type instanceof SourceTypeBinding) |
| ((SourceTypeBinding) type).scope = null; |
| } |
| } |
| } |
| |
| public void reset() { |
| this.annotationTypes = new SimpleLookupTable(16); |
| this.typeid = TypeIds.T_LastWellKnownTypeId; |
| this.types = new TypeBinding[TypeIds.T_LastWellKnownTypeId * 2][]; |
| this.parameterizedTypes = new HashedParameterizedTypes(); |
| } |
| |
| public void updateCaches(UnresolvedReferenceBinding unresolvedType, ReferenceBinding resolvedType) { |
| final int unresolvedTypeId = unresolvedType.id; |
| if (resolvedType.id != TypeIds.NoId) { |
| unresolvedType.id = resolvedType.id; |
| } |
| if (unresolvedTypeId != TypeIds.NoId) { |
| // https://bugs.eclipse.org/bugs/show_bug.cgi?id=432977 |
| TypeBinding[] derivedTypes = this.types[unresolvedTypeId]; |
| for (int i = 0, length = derivedTypes == null ? 0 : derivedTypes.length; i < length; i++) { |
| if (derivedTypes[i] == null) |
| break; |
| if (derivedTypes[i] == unresolvedType) { //$IDENTITY-COMPARISON$ |
| if(resolvedType.id == TypeIds.NoId) |
| resolvedType.id = unresolvedTypeId; |
| derivedTypes[i] = resolvedType; |
| } |
| } |
| } |
| if (this.annotationTypes.get(unresolvedType) != null) { // update the key |
| Object[] keys = this.annotationTypes.keyTable; |
| for (int i = 0, l = keys.length; i < l; i++) { |
| if (keys[i] == unresolvedType) { |
| keys[i] = resolvedType; // hashCode is based on compoundName so this works. |
| break; |
| } |
| } |
| } |
| } |
| |
| public final TypeBinding getIntersectionType18(ReferenceBinding[] intersectingTypes) { |
| int intersectingTypesLength = intersectingTypes == null ? 0 : intersectingTypes.length; |
| if (intersectingTypesLength == 0) |
| return null; |
| TypeBinding keyType = intersectingTypes[0]; |
| if (keyType == null || intersectingTypesLength == 1) |
| return keyType; |
| |
| TypeBinding[] derivedTypes = getDerivedTypes(keyType); |
| int i, length = derivedTypes.length; |
| next: |
| for (i = 0; i < length; i++) { |
| TypeBinding derivedType = derivedTypes[i]; |
| if (derivedType == null) |
| break; |
| if (!derivedType.isIntersectionType18()) |
| continue; |
| ReferenceBinding [] priorIntersectingTypes = derivedType.getIntersectingTypes(); |
| if (priorIntersectingTypes.length != intersectingTypesLength) |
| continue; |
| for (int j = 0; j < intersectingTypesLength; j++) { |
| if (intersectingTypes[j] != priorIntersectingTypes[j]) //$IDENTITY-COMPARISON$ |
| continue next; |
| } |
| return derivedType; |
| } |
| return cacheDerivedType(keyType, new IntersectionTypeBinding18(intersectingTypes, this.environment)); |
| } |
| |
| /** |
| * If a TVB was created with a dummy declaring element and needs to be fixed now, |
| * make sure that this update affects all early clones, too. |
| */ |
| public void fixTypeVariableDeclaringElement(TypeVariableBinding var, Binding declaringElement) { |
| int id = var.id; |
| if (id < this.typeid && this.types[id] != null) { |
| for (TypeBinding t : this.types[id]) { |
| if (t instanceof TypeVariableBinding) |
| ((TypeVariableBinding)t).declaringElement = declaringElement; |
| } |
| } else { |
| var.declaringElement = declaringElement; |
| } |
| } |
| |
| //{ObjectTeams: compare role types: |
| boolean isRoleTypeMatch(ITeamAnchor teamAnchor, int valueParamPosition, TypeBinding cachedType) { |
| if (teamAnchor != null) { |
| if (!(cachedType instanceof DependentTypeBinding)) |
| return false; |
| if (!((DependentTypeBinding)cachedType)._teamAnchor.hasSameBestNameAs(teamAnchor)) |
| return false; |
| } |
| if ( valueParamPosition > -1 // position specified, requires dependent type |
| && !(cachedType instanceof DependentTypeBinding)) |
| return false; |
| if ( (cachedType instanceof DependentTypeBinding) // dependent type specified, positions must match |
| && ((DependentTypeBinding)cachedType)._valueParamPosition != valueParamPosition) |
| return false; |
| if (teamAnchor == null && cachedType.isRoleType()) // role type found though not requested? |
| return false; |
| return true; |
| } |
| //SH} |
| } |