1 /******************************************************************************* 2 * Copyright (c) 2000, 2014 IBM Corporation and others. 3 * All rights reserved. This program and the accompanying materials 4 * are made available under the terms of the Eclipse Public License v1.0 5 * which accompanies this distribution, and is available at 6 * http://www.eclipse.org/legal/epl-v10.html 7 * 8 * Contributors: 9 * IBM Corporation - initial API and implementation 10 * Stephan Herrmann - Contribution for 11 * Bug 400874 - [1.8][compiler] Inference infrastructure should evolve to meet JLS8 18.x (Part G of JSR335 spec) 12 * Bug 429384 - [1.8][null] implement conformance rules for null-annotated lower / upper type bounds 13 *******************************************************************************/ 14 package org.eclipse.jdt.internal.compiler.lookup; 15 16 import org.eclipse.jdt.core.compiler.CharOperation; 17 import org.eclipse.jdt.internal.compiler.ast.Wildcard; 18 import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; 19 import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; 20 21 public class CaptureBinding extends TypeVariableBinding { 22 23 public TypeBinding lowerBound; 24 public WildcardBinding wildcard; 25 public int captureID; 26 27 /* information to compute unique binding key */ 28 public ReferenceBinding sourceType; 29 public int position; 30 CaptureBinding(WildcardBinding wildcard, ReferenceBinding sourceType, int position, int captureID)31 public CaptureBinding(WildcardBinding wildcard, ReferenceBinding sourceType, int position, int captureID) { 32 super(TypeConstants.WILDCARD_CAPTURE_NAME_PREFIX, null, 0, wildcard.environment); 33 this.wildcard = wildcard; 34 this.modifiers = ClassFileConstants.AccPublic | ExtraCompilerModifiers.AccGenericSignature; // treat capture as public 35 this.fPackage = wildcard.fPackage; 36 this.sourceType = sourceType; 37 this.position = position; 38 this.captureID = captureID; 39 this.tagBits |= TagBits.HasCapturedWildcard; 40 if (wildcard.hasTypeAnnotations()) { 41 setTypeAnnotations(wildcard.getTypeAnnotations(), wildcard.environment.globalOptions.isAnnotationBasedNullAnalysisEnabled); 42 if (wildcard.hasNullTypeAnnotations()) 43 this.tagBits |= TagBits.HasNullTypeAnnotation; 44 } 45 } 46 47 // for subclass CaptureBinding18 CaptureBinding(ReferenceBinding sourceType, char[] sourceName, int position, int captureID, LookupEnvironment environment)48 protected CaptureBinding(ReferenceBinding sourceType, char[] sourceName, int position, int captureID, LookupEnvironment environment) { 49 super(sourceName, null, 0, environment); 50 this.modifiers = ClassFileConstants.AccPublic | ExtraCompilerModifiers.AccGenericSignature; // treat capture as public 51 this.sourceType = sourceType; 52 this.position = position; 53 this.captureID = captureID; 54 } 55 CaptureBinding(CaptureBinding prototype)56 public CaptureBinding(CaptureBinding prototype) { 57 super(prototype); 58 this.wildcard = prototype.wildcard; 59 this.sourceType = prototype.sourceType; 60 this.position = prototype.position; 61 this.captureID = prototype.captureID; 62 this.lowerBound = prototype.lowerBound; 63 this.tagBits |= (prototype.tagBits & TagBits.HasCapturedWildcard); 64 } 65 66 // Captures may get cloned and annotated during type inference. clone(TypeBinding enclosingType)67 public TypeBinding clone(TypeBinding enclosingType) { 68 return new CaptureBinding(this); 69 } 70 71 /* 72 * sourceTypeKey ! wildcardKey position semi-colon 73 * p.X { capture of ? } --> !*123; (Lp/X; in declaring type except if leaf) 74 * p.X { capture of ? extends p.Y } --> !+Lp/Y;123; (Lp/X; in declaring type except if leaf) 75 */ computeUniqueKey(boolean isLeaf)76 public char[] computeUniqueKey(boolean isLeaf) { 77 StringBuffer buffer = new StringBuffer(); 78 if (isLeaf) { 79 buffer.append(this.sourceType.computeUniqueKey(false/*not a leaf*/)); 80 buffer.append('&'); 81 } 82 buffer.append(TypeConstants.WILDCARD_CAPTURE); 83 buffer.append(this.wildcard.computeUniqueKey(false/*not a leaf*/)); 84 buffer.append(this.position); 85 buffer.append(';'); 86 int length = buffer.length(); 87 char[] uniqueKey = new char[length]; 88 buffer.getChars(0, length, uniqueKey, 0); 89 return uniqueKey; 90 } 91 debugName()92 public String debugName() { 93 94 if (this.wildcard != null) { 95 StringBuffer buffer = new StringBuffer(10); 96 AnnotationBinding [] annotations = getTypeAnnotations(); 97 for (int i = 0, length = annotations == null ? 0 : annotations.length; i < length; i++) { 98 buffer.append(annotations[i]); 99 buffer.append(' '); 100 } 101 buffer 102 .append(TypeConstants.WILDCARD_CAPTURE_NAME_PREFIX) 103 .append(this.captureID) 104 .append(TypeConstants.WILDCARD_CAPTURE_NAME_SUFFIX) 105 .append(this.wildcard.debugName()); 106 return buffer.toString(); 107 } 108 return super.debugName(); 109 } 110 genericTypeSignature()111 public char[] genericTypeSignature() { 112 if (this.genericTypeSignature == null) { 113 this.genericTypeSignature = CharOperation.concat(TypeConstants.WILDCARD_CAPTURE, this.wildcard.genericTypeSignature()); 114 } 115 return this.genericTypeSignature; 116 } 117 118 /** 119 * Initialize capture bounds using substituted supertypes 120 * e.g. given X<U, V extends X<U, V>>, capture(X<E,?>) = X<E,capture>, where capture extends X<E,capture> 121 */ initializeBounds(Scope scope, ParameterizedTypeBinding capturedParameterizedType)122 public void initializeBounds(Scope scope, ParameterizedTypeBinding capturedParameterizedType) { 123 TypeVariableBinding wildcardVariable = this.wildcard.typeVariable(); 124 if (wildcardVariable == null) { 125 // error resilience when capturing Zork<?> 126 // no substitution for wildcard bound (only formal bounds from type variables are to be substituted: 104082) 127 TypeBinding originalWildcardBound = this.wildcard.bound; 128 switch (this.wildcard.boundKind) { 129 case Wildcard.EXTENDS : 130 // still need to capture bound supertype as well so as not to expose wildcards to the outside (111208) 131 TypeBinding capturedWildcardBound = originalWildcardBound.capture(scope, this.position); 132 if (originalWildcardBound.isInterface()) { 133 this.setSuperClass(scope.getJavaLangObject()); 134 this.setSuperInterfaces(new ReferenceBinding[] { (ReferenceBinding) capturedWildcardBound }); 135 } else { 136 // the wildcard bound should be a subtype of variable superclass 137 // it may occur that the bound is less specific, then consider glb (202404) 138 if (capturedWildcardBound.isArrayType() || TypeBinding.equalsEquals(capturedWildcardBound, this)) { 139 this.setSuperClass(scope.getJavaLangObject()); 140 } else { 141 this.setSuperClass((ReferenceBinding) capturedWildcardBound); 142 } 143 this.setSuperInterfaces(Binding.NO_SUPERINTERFACES); 144 } 145 this.setFirstBound(capturedWildcardBound); 146 if ((capturedWildcardBound.tagBits & TagBits.HasTypeVariable) == 0) 147 this.tagBits &= ~TagBits.HasTypeVariable; 148 break; 149 case Wildcard.UNBOUND : 150 this.setSuperClass(scope.getJavaLangObject()); 151 this.setSuperInterfaces(Binding.NO_SUPERINTERFACES); 152 this.tagBits &= ~TagBits.HasTypeVariable; 153 break; 154 case Wildcard.SUPER : 155 this.setSuperClass(scope.getJavaLangObject()); 156 this.setSuperInterfaces(Binding.NO_SUPERINTERFACES); 157 this.lowerBound = this.wildcard.bound; 158 if ((originalWildcardBound.tagBits & TagBits.HasTypeVariable) == 0) 159 this.tagBits &= ~TagBits.HasTypeVariable; 160 break; 161 } 162 return; 163 } 164 ReferenceBinding originalVariableSuperclass = wildcardVariable.superclass; 165 ReferenceBinding substitutedVariableSuperclass = (ReferenceBinding) Scope.substitute(capturedParameterizedType, originalVariableSuperclass); 166 // prevent cyclic capture: given X<T>, capture(X<? extends T> could yield a circular type 167 if (TypeBinding.equalsEquals(substitutedVariableSuperclass, this)) substitutedVariableSuperclass = originalVariableSuperclass; 168 169 ReferenceBinding[] originalVariableInterfaces = wildcardVariable.superInterfaces(); 170 ReferenceBinding[] substitutedVariableInterfaces = Scope.substitute(capturedParameterizedType, originalVariableInterfaces); 171 if (substitutedVariableInterfaces != originalVariableInterfaces) { 172 // prevent cyclic capture: given X<T>, capture(X<? extends T> could yield a circular type 173 for (int i = 0, length = substitutedVariableInterfaces.length; i < length; i++) { 174 if (TypeBinding.equalsEquals(substitutedVariableInterfaces[i], this)) substitutedVariableInterfaces[i] = originalVariableInterfaces[i]; 175 } 176 } 177 // no substitution for wildcard bound (only formal bounds from type variables are to be substituted: 104082) 178 TypeBinding originalWildcardBound = this.wildcard.bound; 179 180 switch (this.wildcard.boundKind) { 181 case Wildcard.EXTENDS : 182 // still need to capture bound supertype as well so as not to expose wildcards to the outside (111208) 183 TypeBinding capturedWildcardBound = originalWildcardBound.capture(scope, this.position); 184 if (originalWildcardBound.isInterface()) { 185 this.setSuperClass(substitutedVariableSuperclass); 186 // merge wildcard bound into variable superinterfaces using glb 187 if (substitutedVariableInterfaces == Binding.NO_SUPERINTERFACES) { 188 this.setSuperInterfaces(new ReferenceBinding[] { (ReferenceBinding) capturedWildcardBound }); 189 } else { 190 int length = substitutedVariableInterfaces.length; 191 System.arraycopy(substitutedVariableInterfaces, 0, substitutedVariableInterfaces = new ReferenceBinding[length+1], 1, length); 192 substitutedVariableInterfaces[0] = (ReferenceBinding) capturedWildcardBound; 193 this.setSuperInterfaces(Scope.greaterLowerBound(substitutedVariableInterfaces)); 194 } 195 } else { 196 // the wildcard bound should be a subtype of variable superclass 197 // it may occur that the bound is less specific, then consider glb (202404) 198 if (capturedWildcardBound.isArrayType() || TypeBinding.equalsEquals(capturedWildcardBound, this)) { 199 this.setSuperClass(substitutedVariableSuperclass); 200 } else { 201 this.setSuperClass((ReferenceBinding) capturedWildcardBound); 202 if (this.superclass.isSuperclassOf(substitutedVariableSuperclass)) { 203 this.setSuperClass(substitutedVariableSuperclass); 204 } 205 } 206 this.setSuperInterfaces(substitutedVariableInterfaces); 207 } 208 this.setFirstBound(capturedWildcardBound); 209 if ((capturedWildcardBound.tagBits & TagBits.HasTypeVariable) == 0) 210 this.tagBits &= ~TagBits.HasTypeVariable; 211 break; 212 case Wildcard.UNBOUND : 213 this.setSuperClass(substitutedVariableSuperclass); 214 this.setSuperInterfaces(substitutedVariableInterfaces); 215 this.tagBits &= ~TagBits.HasTypeVariable; 216 break; 217 case Wildcard.SUPER : 218 this.setSuperClass(substitutedVariableSuperclass); 219 if (TypeBinding.equalsEquals(wildcardVariable.firstBound, substitutedVariableSuperclass) || TypeBinding.equalsEquals(originalWildcardBound, substitutedVariableSuperclass)) { 220 this.setFirstBound(substitutedVariableSuperclass); 221 } 222 this.setSuperInterfaces(substitutedVariableInterfaces); 223 this.lowerBound = originalWildcardBound; 224 if ((originalWildcardBound.tagBits & TagBits.HasTypeVariable) == 0) 225 this.tagBits &= ~TagBits.HasTypeVariable; 226 break; 227 } 228 } 229 230 /** 231 * @see org.eclipse.jdt.internal.compiler.lookup.TypeBinding#isCapture() 232 */ isCapture()233 public boolean isCapture() { 234 return true; 235 } 236 237 /** 238 * @see TypeBinding#isEquivalentTo(TypeBinding) 239 */ isEquivalentTo(TypeBinding otherType)240 public boolean isEquivalentTo(TypeBinding otherType) { 241 if (equalsEquals(this, otherType)) return true; 242 if (otherType == null) return false; 243 // capture of ? extends X[] 244 if (this.firstBound != null && this.firstBound.isArrayType()) { 245 if (this.firstBound.isCompatibleWith(otherType)) 246 return true; 247 } 248 switch (otherType.kind()) { 249 case Binding.WILDCARD_TYPE : 250 case Binding.INTERSECTION_TYPE : 251 return ((WildcardBinding) otherType).boundCheck(this); 252 } 253 return false; 254 } 255 readableName()256 public char[] readableName() { 257 if (this.wildcard != null) { 258 StringBuffer buffer = new StringBuffer(10); 259 buffer 260 .append(TypeConstants.WILDCARD_CAPTURE_NAME_PREFIX) 261 .append(this.captureID) 262 .append(TypeConstants.WILDCARD_CAPTURE_NAME_SUFFIX) 263 .append(this.wildcard.readableName()); 264 int length = buffer.length(); 265 char[] name = new char[length]; 266 buffer.getChars(0, length, name, 0); 267 return name; 268 } 269 return super.readableName(); 270 } 271 shortReadableName()272 public char[] shortReadableName() { 273 if (this.wildcard != null) { 274 StringBuffer buffer = new StringBuffer(10); 275 buffer 276 .append(TypeConstants.WILDCARD_CAPTURE_NAME_PREFIX) 277 .append(this.captureID) 278 .append(TypeConstants.WILDCARD_CAPTURE_NAME_SUFFIX) 279 .append(this.wildcard.shortReadableName()); 280 int length = buffer.length(); 281 char[] name = new char[length]; 282 buffer.getChars(0, length, name, 0); 283 return name; 284 } 285 return super.shortReadableName(); 286 } 287 288 @Override nullAnnotatedReadableName(CompilerOptions options, boolean shortNames)289 public char[] nullAnnotatedReadableName(CompilerOptions options, boolean shortNames) { 290 StringBuffer nameBuffer = new StringBuffer(10); 291 appendNullAnnotation(nameBuffer, options); 292 nameBuffer.append(this.sourceName()); 293 if (!this.inRecursiveFunction) { // CaptureBinding18 can be recursive indeed 294 this.inRecursiveFunction = true; 295 try { 296 if (this.wildcard != null) { 297 nameBuffer.append("of "); //$NON-NLS-1$ 298 nameBuffer.append(this.wildcard.nullAnnotatedReadableName(options, shortNames)); 299 } else if (this.lowerBound != null) { 300 nameBuffer.append(" super "); //$NON-NLS-1$ 301 nameBuffer.append(this.lowerBound.nullAnnotatedReadableName(options, shortNames)); 302 } else if (this.firstBound != null) { 303 nameBuffer.append(" extends "); //$NON-NLS-1$ 304 nameBuffer.append(this.firstBound.nullAnnotatedReadableName(options, shortNames)); 305 TypeBinding[] otherUpperBounds = this.otherUpperBounds(); 306 if (otherUpperBounds != NO_TYPES) 307 nameBuffer.append(" & ..."); //$NON-NLS-1$ // only hint at more bounds, we currently don't evaluate null annotations on otherUpperBounds 308 } 309 } finally { 310 this.inRecursiveFunction = false; 311 } 312 } 313 int nameLength = nameBuffer.length(); 314 char[] readableName = new char[nameLength]; 315 nameBuffer.getChars(0, nameLength, readableName, 0); 316 return readableName; 317 } 318 319 @Override uncapture(Scope scope)320 public TypeBinding uncapture(Scope scope) { 321 return this.wildcard; 322 } 323 toString()324 public String toString() { 325 if (this.wildcard != null) { 326 StringBuffer buffer = new StringBuffer(10); 327 AnnotationBinding [] annotations = getTypeAnnotations(); 328 for (int i = 0, length = annotations == null ? 0 : annotations.length; i < length; i++) { 329 buffer.append(annotations[i]); 330 buffer.append(' '); 331 } 332 buffer 333 .append(TypeConstants.WILDCARD_CAPTURE_NAME_PREFIX) 334 .append(this.captureID) 335 .append(TypeConstants.WILDCARD_CAPTURE_NAME_SUFFIX) 336 .append(this.wildcard); 337 return buffer.toString(); 338 } 339 return super.toString(); 340 } 341 } 342