/******************************************************************************* * Copyright (c) 2000, 2009 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.jdt.internal.compiler.lookup; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.compiler.ast.Wildcard; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; public class CaptureBinding extends TypeVariableBinding { public TypeBinding lowerBound; public WildcardBinding wildcard; public int captureID; /* information to compute unique binding key */ public ReferenceBinding sourceType; public int position; public CaptureBinding(WildcardBinding wildcard, ReferenceBinding sourceType, int position, int captureID) { super(TypeConstants.WILDCARD_CAPTURE_NAME_PREFIX, null, 0, wildcard.environment); this.wildcard= wildcard; this.modifiers= ClassFileConstants.AccPublic | ExtraCompilerModifiers.AccGenericSignature; // treat capture as public this.fPackage= wildcard.fPackage; this.sourceType= sourceType; this.position= position; this.captureID= captureID; } /* * sourceTypeKey ! wildcardKey position semi-colon * p.X { capture of ? } --> !*123; (Lp/X; in declaring type except if leaf) * p.X { capture of ? extends p.Y } --> !+Lp/Y;123; (Lp/X; in declaring type except if leaf) */ public char[] computeUniqueKey(boolean isLeaf) { StringBuffer buffer= new StringBuffer(); if (isLeaf) { buffer.append(this.sourceType.computeUniqueKey(false/*not a leaf*/)); buffer.append('&'); } buffer.append(TypeConstants.WILDCARD_CAPTURE); buffer.append(this.wildcard.computeUniqueKey(false/*not a leaf*/)); buffer.append(this.position); buffer.append(';'); int length= buffer.length(); char[] uniqueKey= new char[length]; buffer.getChars(0, length, uniqueKey, 0); return uniqueKey; } public String debugName() { if (this.wildcard != null) { StringBuffer buffer= new StringBuffer(10); buffer .append(TypeConstants.WILDCARD_CAPTURE_NAME_PREFIX) .append(this.captureID) .append(TypeConstants.WILDCARD_CAPTURE_NAME_SUFFIX) .append(this.wildcard.debugName()); return buffer.toString(); } return super.debugName(); } public char[] genericTypeSignature() { if (this.genericTypeSignature == null) { this.genericTypeSignature= CharOperation.concat(TypeConstants.WILDCARD_CAPTURE, this.wildcard.genericTypeSignature()); } return this.genericTypeSignature; } /** * Initialize capture bounds using substituted supertypes e.g. given X<U, V extends X<U, V>>, * capture(X<E,?>) = X<E,capture>, where capture extends X<E,capture> */ public void initializeBounds(Scope scope, ParameterizedTypeBinding capturedParameterizedType) { TypeVariableBinding wildcardVariable= this.wildcard.typeVariable(); if (wildcardVariable == null) { // error resilience when capturing Zork<?> // no substitution for wildcard bound (only formal bounds from type variables are to be substituted: 104082) TypeBinding originalWildcardBound= this.wildcard.bound; switch (this.wildcard.boundKind) { case Wildcard.EXTENDS: // still need to capture bound supertype as well so as not to expose wildcards to the outside (111208) TypeBinding capturedWildcardBound= originalWildcardBound.capture(scope, this.position); if (originalWildcardBound.isInterface()) { this.superclass= scope.getJavaLangObject(); this.superInterfaces= new ReferenceBinding[] { (ReferenceBinding)capturedWildcardBound }; } else { // the wildcard bound should be a subtype of variable superclass // it may occur that the bound is less specific, then consider glb (202404) if (capturedWildcardBound.isArrayType() || capturedWildcardBound == this) { this.superclass= scope.getJavaLangObject(); } else { this.superclass= (ReferenceBinding)capturedWildcardBound; } this.superInterfaces= Binding.NO_SUPERINTERFACES; } this.firstBound= capturedWildcardBound; if ((capturedWildcardBound.tagBits & TagBits.HasTypeVariable) == 0) this.tagBits&= ~TagBits.HasTypeVariable; break; case Wildcard.UNBOUND: this.superclass= scope.getJavaLangObject(); this.superInterfaces= Binding.NO_SUPERINTERFACES; this.tagBits&= ~TagBits.HasTypeVariable; break; case Wildcard.SUPER: this.superclass= scope.getJavaLangObject(); this.superInterfaces= Binding.NO_SUPERINTERFACES; this.lowerBound= this.wildcard.bound; if ((originalWildcardBound.tagBits & TagBits.HasTypeVariable) == 0) this.tagBits&= ~TagBits.HasTypeVariable; break; } return; } ReferenceBinding originalVariableSuperclass= wildcardVariable.superclass; ReferenceBinding substitutedVariableSuperclass= (ReferenceBinding)Scope.substitute(capturedParameterizedType, originalVariableSuperclass); // prevent cyclic capture: given X<T>, capture(X<? extends T> could yield a circular type if (substitutedVariableSuperclass == this) substitutedVariableSuperclass= originalVariableSuperclass; ReferenceBinding[] originalVariableInterfaces= wildcardVariable.superInterfaces(); ReferenceBinding[] substitutedVariableInterfaces= Scope.substitute(capturedParameterizedType, originalVariableInterfaces); if (substitutedVariableInterfaces != originalVariableInterfaces) { // prevent cyclic capture: given X<T>, capture(X<? extends T> could yield a circular type for (int i= 0, length= substitutedVariableInterfaces.length; i < length; i++) { if (substitutedVariableInterfaces[i] == this) substitutedVariableInterfaces[i]= originalVariableInterfaces[i]; } } // no substitution for wildcard bound (only formal bounds from type variables are to be substituted: 104082) TypeBinding originalWildcardBound= this.wildcard.bound; switch (this.wildcard.boundKind) { case Wildcard.EXTENDS: // still need to capture bound supertype as well so as not to expose wildcards to the outside (111208) TypeBinding capturedWildcardBound= originalWildcardBound.capture(scope, this.position); if (originalWildcardBound.isInterface()) { this.superclass= substitutedVariableSuperclass; // merge wildcard bound into variable superinterfaces using glb if (substitutedVariableInterfaces == Binding.NO_SUPERINTERFACES) { this.superInterfaces= new ReferenceBinding[] { (ReferenceBinding)capturedWildcardBound }; } else { int length= substitutedVariableInterfaces.length; System.arraycopy(substitutedVariableInterfaces, 0, substitutedVariableInterfaces= new ReferenceBinding[length + 1], 1, length); substitutedVariableInterfaces[0]= (ReferenceBinding)capturedWildcardBound; this.superInterfaces= Scope.greaterLowerBound(substitutedVariableInterfaces); } } else { // the wildcard bound should be a subtype of variable superclass // it may occur that the bound is less specific, then consider glb (202404) if (capturedWildcardBound.isArrayType() || capturedWildcardBound == this) { this.superclass= substitutedVariableSuperclass; } else { this.superclass= (ReferenceBinding)capturedWildcardBound; if (this.superclass.isSuperclassOf(substitutedVariableSuperclass)) { this.superclass= substitutedVariableSuperclass; } } this.superInterfaces= substitutedVariableInterfaces; } this.firstBound= capturedWildcardBound; if ((capturedWildcardBound.tagBits & TagBits.HasTypeVariable) == 0) this.tagBits&= ~TagBits.HasTypeVariable; break; case Wildcard.UNBOUND: this.superclass= substitutedVariableSuperclass; this.superInterfaces= substitutedVariableInterfaces; this.tagBits&= ~TagBits.HasTypeVariable; break; case Wildcard.SUPER: this.superclass= substitutedVariableSuperclass; if (wildcardVariable.firstBound == substitutedVariableSuperclass || originalWildcardBound == substitutedVariableSuperclass) { this.firstBound= substitutedVariableSuperclass; } this.superInterfaces= substitutedVariableInterfaces; this.lowerBound= originalWildcardBound; if ((originalWildcardBound.tagBits & TagBits.HasTypeVariable) == 0) this.tagBits&= ~TagBits.HasTypeVariable; break; } } /** * @see org.eclipse.jdt.internal.compiler.lookup.TypeBinding#isCapture() */ public boolean isCapture() { return true; } /** * @see TypeBinding#isEquivalentTo(TypeBinding) */ public boolean isEquivalentTo(TypeBinding otherType) { if (this == otherType) return true; if (otherType == null) return false; // capture of ? extends X[] if (this.firstBound != null && this.firstBound.isArrayType()) { if (this.firstBound.isCompatibleWith(otherType)) return true; } switch (otherType.kind()) { case Binding.WILDCARD_TYPE: case Binding.INTERSECTION_TYPE: return ((WildcardBinding)otherType).boundCheck(this); } return false; } public char[] readableName() { if (this.wildcard != null) { StringBuffer buffer= new StringBuffer(10); buffer .append(TypeConstants.WILDCARD_CAPTURE_NAME_PREFIX) .append(this.captureID) .append(TypeConstants.WILDCARD_CAPTURE_NAME_SUFFIX) .append(this.wildcard.readableName()); int length= buffer.length(); char[] name= new char[length]; buffer.getChars(0, length, name, 0); return name; } return super.readableName(); } public char[] shortReadableName() { if (this.wildcard != null) { StringBuffer buffer= new StringBuffer(10); buffer .append(TypeConstants.WILDCARD_CAPTURE_NAME_PREFIX) .append(this.captureID) .append(TypeConstants.WILDCARD_CAPTURE_NAME_SUFFIX) .append(this.wildcard.shortReadableName()); int length= buffer.length(); char[] name= new char[length]; buffer.getChars(0, length, name, 0); return name; } return super.shortReadableName(); } public String toString() { if (this.wildcard != null) { StringBuffer buffer= new StringBuffer(10); buffer .append(TypeConstants.WILDCARD_CAPTURE_NAME_PREFIX) .append(this.captureID) .append(TypeConstants.WILDCARD_CAPTURE_NAME_SUFFIX) .append(this.wildcard); return buffer.toString(); } return super.toString(); } }