/******************************************************************************* * 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(); } }