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