blob: 28728925edadc255a5e991a7d6c4e4e099d0fbb2 [file] [log] [blame]
/**********************************************************************
* This file is part of "Object Teams Development Tooling"-Software
*
* Copyright 2004, 2009 Fraunhofer Gesellschaft, Munich, Germany,
* for its Fraunhofer Institute for Computer Architecture and Software
* Technology (FIRST), Berlin, Germany and Technical University Berlin,
* Germany.
*
* 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
* $Id: TeamAnchor.java 23417 2010-02-03 20:13:55Z stephan $
*
* Please visit http://www.eclipse.org/objectteams for updates and contact.
*
* Contributors:
* Fraunhofer FIRST - Initial API and implementation
* Technical University Berlin - Initial API and implementation
**********************************************************************/
package org.eclipse.objectteams.otdt.internal.core.compiler.lookup;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.core.compiler.IProblem;
import org.eclipse.jdt.internal.compiler.ast.AllocationExpression;
import org.eclipse.jdt.internal.compiler.ast.Argument;
import org.eclipse.jdt.internal.compiler.ast.CastExpression;
import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.FieldReference;
import org.eclipse.jdt.internal.compiler.ast.NameReference;
import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference;
import org.eclipse.jdt.internal.compiler.ast.Statement;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.impl.Constant;
import org.eclipse.jdt.internal.compiler.lookup.Binding;
import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment;
import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.Scope;
import org.eclipse.jdt.internal.compiler.lookup.TagBits;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.VariableBinding;
import org.eclipse.jdt.internal.compiler.problem.AbortCompilation;
import org.eclipse.objectteams.otdt.core.compiler.IOTConstants;
import org.eclipse.objectteams.otdt.internal.core.compiler.ast.TypeAnchorReference;
import org.eclipse.objectteams.otdt.internal.core.compiler.control.Config;
import org.eclipse.objectteams.otdt.internal.core.compiler.control.Config.NotConfiguredException;
import org.eclipse.objectteams.otdt.internal.core.compiler.model.FieldModel;
import org.eclipse.objectteams.otdt.internal.core.compiler.model.RoleModel;
import org.eclipse.objectteams.otdt.internal.core.compiler.model.TeamModel;
import org.eclipse.objectteams.otdt.internal.core.compiler.util.TypeAnalyzer;
/**
* NEW for OTDT.
*
* This class implements what is needed to use a variable as a team anchor
* for an externalized role.
*
* Note: team anchors should only be accessed via the interface ITeamAnchor!
* There are only two exceptions:
* + implementers of "TeamAnchor getClone()"
* + access to the static method maybeImproveAnchor(..).
*
* Rationale: There is a potential conflict if a team is also a role:
* + For type checking the interface part should be used throughout
* + For accessing roles of the nested team, the class part is needed.
* For reasons of this conflict, accessing the type of a team anchor is
* fully encapsulated in this class. The methods of this class will do
* the selection between ifc and class part if needed.
*
* Secondly, this class implements a registry for role types that are known
* to be anchored to the given variable.
*
* @author stephan
*/
public abstract class TeamAnchor extends Binding implements ITeamAnchor {
// pulled up from VariableBinding:
public TypeBinding type;
public boolean isTeam() {
return this.type.isTeam();
}
/** Is this binding possibly a team anchor? */
public boolean couldBeTeamAnchor() {
return isValidBinding() && isFinal() && this.type.isTeam();
}
// ====== managing bestname paths ======
/**
* Each variable has a reference to it's bestNamePath denoting a path
* by which its value can be reached. The first element may be any
* VariableBinging, the others are FieldBindings.
*
* This path is either the variable itself,
* or the head of a chain of paths initialized from one another.
* All variables in a path must be final, the last one must have a team-type,
* in order to be used as a type anchor.
*
* Synthetic variabe bindings can be used as anchors for multi-element paths
* evaluated from QualifiedNameReferences.
*
* Note: typing the whole array to ITeamAnchor[] is over-cautious, but when
* working with bestname paths, no other properties than those available
* via ITeamAnchor are needed.
*/
protected ITeamAnchor[] bestNamePath = new ITeamAnchor[]{this};
public ITeamAnchor[] getBestNamePath() {
return getBestNamePath(true);
}
public ITeamAnchor[] getBestNamePath(boolean needResolve) {
if (needResolve)
resolveInitIfNeeded(); // may set bestNamePath
return flattenBestNamePath(this.bestNamePath, 0);
}
/**
* Does this anchor's path start with a field binding (rather than a local/arg)?
*/
public boolean pathIsAbsolute() {
return ((TeamAnchor)this.bestNamePath[0]).kind() == Binding.FIELD;
}
public boolean isValidAnchor() {
if (!isValidBinding())
return false;
if (!this.type.isValidBinding())
return false;
if (this.type.leafComponentType().isBaseType())
return false;
return true;
}
private ITeamAnchor[] flattenBestNamePath(ITeamAnchor[] tree, int start) {
if (tree == null || start >= tree.length)
return new ITeamAnchor[0];
TypeBinding firstType = ((TeamAnchor)this.bestNamePath[start]).leafType();
ITeamAnchor[] prefix;
if (RoleTypeBinding.isRoleWithExplicitAnchor(firstType)) {
prefix = ((IRoleTypeBinding)firstType).getAnchorBestName();
} else {
prefix = new ITeamAnchor[] { this.bestNamePath[start] };
}
if (start == tree.length-1)
return prefix;
ITeamAnchor[] tail = flattenBestNamePath(tree, start+1);
if (tail.length == 0)
return prefix;
int len1 = prefix.length;
int len2 = tail.length;
ITeamAnchor[] result = new ITeamAnchor[len1+len2];
System.arraycopy(prefix, 0, result, 0, len1);
System.arraycopy(tail, 0, result, len1, len2);
return result;
}
public char[][] tokens()
{
char[][] tokens = new char[this.bestNamePath.length][];
for (int i = 0; i < this.bestNamePath.length; i++) {
tokens[i] = this.bestNamePath[i].internalName();
}
return tokens;
}
/**
* Check whether the given expression determines a proper bestName for this binding.
* @param rhs expression being assigned to this variable (Expression or Argument)
*/
public void setBestNameFromStat(Statement rhs)
{
if ( isFinal()
&& (this.type instanceof ReferenceBinding))
{
ITeamAnchor[] path = getBestNameFromStat(rhs);
if (path != null) {
ITeamAnchor lastBinding = path[path.length-1];
if ( (this instanceof FieldBinding)
&& (lastBinding instanceof LocalVariableBinding))
{
LocalVariableBinding localVar = ((LocalVariableBinding)lastBinding);
Scope scope = localVar.declaringScope;
if (scope.referenceContext() instanceof ConstructorDeclaration) {
// don't record the initial field assignment within a constructor,
// but reverse this: pretend the local was initialized from the field,
// to make both equivalent within this ctor.
if (lastBinding.isFinal()) {
if ( (localVar.tagBits & TagBits.IsArgument) != 0
&& !localVar.pathIsAbsolute())
{
// localVar is really an argument not anchored to a field
lastBinding.shareBestName(this);
}
}
}
return; // all others: ignore
}
this.bestNamePath = path;
}
}
}
public void shareBestName(ITeamAnchor other) {
this.bestNamePath = other.getBestNamePath();
}
public static ITeamAnchor[] getBestNameFromStat(Statement stat) {
if (stat instanceof Argument) {
return ((Argument)stat).binding.bestNamePath;
}
Expression expr = (Expression)stat;
if (RoleTypeBinding.isRoleWithExplicitAnchor(expr.resolvedType)) {
return ((IRoleTypeBinding)expr.resolvedType).getAnchorBestName();
}
if (expr instanceof QualifiedNameReference) {
QualifiedNameReference qRef = (QualifiedNameReference)expr;
if (qRef.binding instanceof ITeamAnchor) {
// collect all bindings from this qualified reference:
ITeamAnchor first = (ITeamAnchor)qRef.binding;
if (!first.isFinal())
return null;
int resultLength = qRef.otherBindings == null ? 0 : qRef.otherBindings.length;
ITeamAnchor[] result = new ITeamAnchor[resultLength + 1];
result[0] = first;
for (int i = 0; i < resultLength; i++) {
result[i+1] = qRef.otherBindings[i];
if (!result[i+1].isFinal())
return null;
}
return result;
}
} else if (expr instanceof NameReference) {
// a name representing a variable? => already has a bestNamePath:
Binding bind = ((NameReference)expr).binding;
if ( bind instanceof VariableBinding
&& ((VariableBinding)bind).isFinal()) // see 1.6.5-otjld-*-2
return ((VariableBinding)bind).bestNamePath;
} else if (expr instanceof FieldReference) {
// a field reference => append one element to the receiver's bestNamePath:
FieldReference fieldRef = (FieldReference)expr;
if (fieldRef.binding.isFinal())
{
ITeamAnchor[] prefix = getBestNameFromStat(fieldRef.receiver);
if (prefix == null)
prefix = new VariableBinding[0];
ITeamAnchor[] tail = fieldRef.binding.bestNamePath;
ITeamAnchor[] path = new ITeamAnchor[prefix.length+tail.length];
System.arraycopy(prefix, 0, path, 0, prefix.length);
System.arraycopy(tail, 0, path, prefix.length, tail.length);
return path;
}
} /*else if (expr instanceof MessageSend) {
MessageSend send = (MessageSend)expr;
ITeamAnchor[] prefix = getBestNameFromStat(send.receiver);
return prefix;
} */
// Note(SH): getting best name from message send with non-wrapped type
// would require flow analysis to compute unique best name of the methods
// return value. Simply using the receiver as attempted above is incorrect.
return null;
}
/**
* Assuming that reference is resolved construct the team anchor deduced from it.
* @param expression
* @return an existing or newly constructed anchor or null
*/
public static ITeamAnchor getTeamAnchor(Expression expression) {
Binding bind = null;
if (expression instanceof NameReference) {
bind = ((NameReference)expression).binding;
if (expression instanceof QualifiedNameReference) {
FieldBinding[] otherFields = ((QualifiedNameReference)expression).otherBindings;
if (otherFields != null && otherFields.length > 0) {
int len = otherFields.length;
ITeamAnchor anchor = otherFields[len-1];
for (int i = len-2; i >= 0; i--)
anchor = anchor.setPathPrefix(otherFields[i]);
if (bind instanceof ITeamAnchor)
return anchor.setPathPrefix((ITeamAnchor)bind);
}
}
} else if (expression instanceof TypeAnchorReference) {
bind = (Binding) ((TypeAnchorReference)expression).getResolvedAnchor();
}
if (bind != null && (bind instanceof TeamAnchor))
return (TeamAnchor)bind;
TypeBinding type = expression.resolvedType;
if (type != null && type.isRoleType())
return ((IRoleTypeBinding)type).getAnchor();
else
return null;
}
/**
* Do two variables provably denote the same instance?
* Shown by shallow-equal bestNamePaths.
*/
public boolean hasSameBestNameAs(ITeamAnchor other) {
if (other == this) return true;
ITeamAnchor[] otherBestName = other.getBestNamePath();
return hasSameBestNameAs(otherBestName, other);
}
/** Variant if path of other is directly known. */
public boolean hasSameBestNameAs(ITeamAnchor[] otherBestName, ITeamAnchor other) {
ITeamAnchor[] thisBestName = getBestNamePath();
// perhaps one anchor directly refers to the other:
if (thisBestName.length == 1 && thisBestName[0] == other)
return true;
if (otherBestName.length == 1 && otherBestName[0] == this)
return true;
// general case, compare paths:
if (thisBestName.length == otherBestName.length)
{
for (int i = 0; i < thisBestName.length; i++) {
if (!isSameVariable(thisBestName[i], otherBestName[i])) {
return false;
}
}
return true;
}
return false;
}
private boolean isSameVariable(ITeamAnchor one, ITeamAnchor two) {
if (one == two) return true;
if (one instanceof TThisBinding && two instanceof TThisBinding)
return true;
// thanks to 1.4(c) tthis bindings from the same context
// can never refer to different roles of the same name.
// check if one is a fake replica of the other:
if (one instanceof FieldBinding && two instanceof FieldBinding)
{
FieldBinding f1= (FieldBinding)one;
FieldBinding f2= (FieldBinding)two;
if (!CharOperation.equals(f1.name, f2.name))
return false;
return TypeBinding.equalsEquals(FieldModel.getActualDeclaringClass(f1), FieldModel.getActualDeclaringClass(f2));
}
return false;
}
/** Hook method to be overridden in FieldBinding. */
protected void resolveInitIfNeeded() {/*nothing*/}
/**
* API for generating team anchors into byte-code attributes.
*
* Get this anchor's "best name", ie., the 'minimal' path.
* If first component is a field prepend the fully qualified name of its declaring class.
* (This FQN uses '$' to separate inner classes, because this way LookupEnvironment.askForType()
* can directly find the type).
* @return a flat ('.'-seperated) representation of this variable's best name.
*/
public char[] getBestName() {
int len = this.bestNamePath.length;
int prefixLen = 0;
char[][] tokens;
if (this.bestNamePath[0] instanceof FieldBinding) {
tokens = new char[len+1][];
tokens[0] = CharOperation.concatWith(
((FieldBinding)this.bestNamePath[0]).declaringClass.compoundName,
'.');
prefixLen++;
} else {
tokens = new char[len][];
}
for(int i = 0; i < len; i++) {
tokens[i+prefixLen] = this.bestNamePath[i].internalName();
}
return CharOperation.concatWith(tokens, '.');
}
/**
* Answer an anchor that is suitable for externalizing a given role type.
* The anchor can either be this anchor itself, or, if the anchor
* has a role type it might be that role type's anchor, etc.
* If roleType already has a significant anchor, and this anchor is TThis
* use the more specific anchor from roleType.
*/
public ITeamAnchor asAnchorFor (ReferenceBinding roleType) {
if (! (this.type instanceof ReferenceBinding))
return null;
ReferenceBinding leafType = leafType();
// TODO(SH): is it correct to compare leafType and roleType?
// shouldn't we look at roleType._teamAnchor[0] instead??
if (TeamModel.isTeamContainingRole(leafType, roleType)) {
if ( this instanceof TThisBinding
&& RoleTypeBinding.isRoleWithExplicitAnchor(roleType))
{
return ((IRoleTypeBinding)roleType).getAnchor();
}
return this;
}
if (leafType instanceof IRoleTypeBinding)
return ((IRoleTypeBinding)leafType).getAnchor().asAnchorFor(roleType);
return null;
}
public ITeamAnchor retrieveAnchorFromAnchorRoleTypeFor(ReferenceBinding roleType) {
// TODO (SH) copied and not yet validated!
// in contrast to the above, this method will never return 'this'
ReferenceBinding leafType = leafType();
if (this.type != null && leafType != null && leafType.isRoleType())
{
ITeamAnchor anchor = ((IRoleTypeBinding)leafType).getAnchor();
if (anchor.isTeamContainingRole(roleType))
{
return anchor;
} else {
return anchor.asAnchorFor(roleType);
}
}
return null;
}
/**
* Cloning is needed to create synthetic bindings used as type anchor
* evaluated from QualifiedNameReference.
*/
protected abstract TeamAnchor getClone();
/**
* Create a VariableBinding with a bestNamePath constructed from
* the bestNamePath of `prefix' plus this as last element.
*/
public ITeamAnchor setPathPrefix (ITeamAnchor prefix) {
TeamAnchor result = getClone();
ITeamAnchor[] prefixBestName = prefix.getBestNamePath();
ITeamAnchor[] thisBestName = getBestNamePath();
int len1 = prefixBestName.length;
int len2 = thisBestName.length;
result.bestNamePath = new VariableBinding[len1 + len2];
System.arraycopy(prefixBestName, 0, result.bestNamePath, 0, len1);
System.arraycopy(thisBestName, 0, result.bestNamePath, len1, len2);
return result;
}
public ITeamAnchor replaceFirst(ITeamAnchor anchor) {
TeamAnchor result = getClone();
result.bestNamePath[0] = anchor;
return result;
}
public boolean isPrefixLegal(ReferenceBinding site, ITeamAnchor prefix) {
ITeamAnchor firstField = this.bestNamePath[0];
if (firstField instanceof FieldBinding) {
ReferenceBinding declaringClass = ((FieldBinding)firstField).declaringClass;
if (maySkipAnchor(site, prefix, declaringClass))
return false;
ReferenceBinding currentType = (ReferenceBinding)((TeamAnchor)prefix).type.leafComponentType();
while (currentType != null) {
if (currentType.isCompatibleWith(declaringClass))
return true;
currentType = currentType.enclosingType();
}
}
return false;
}
/**
* If an anchor simply leads us to a field which is also directly accessible in the current context,
* the anchor may be skipped in constructing a path.
* @param site
* @param prefix
* @param declaringClass
* @return the result ;-)
*/
private boolean maySkipAnchor(ReferenceBinding site, ITeamAnchor prefix, ReferenceBinding declaringClass) {
if (((TeamAnchor)prefix).hasRoleTypeWithRelevantAnchor())
return false;
ReferenceBinding currentType = site;
while (currentType != null) {
if (currentType.isCompatibleWith(declaringClass))
return true;
currentType = currentType.enclosingType();
}
return false;
}
/**
* If an anchor has in its best name a role type with explicit anchor,
* this component is needed and will be found in flattenBestNamePath().
*/
private boolean hasRoleTypeWithRelevantAnchor() {
for (int i = 0; i < this.bestNamePath.length; i++) {
ReferenceBinding currentLeafType = ((TeamAnchor)this.bestNamePath[i]).leafType();
if (RoleTypeBinding.isRoleWithExplicitAnchor(currentLeafType))
return true;
}
return false;
}
// ====== encapsulate all access to type:
public boolean hasValidReferenceType() {
return this.type != null && (this.type instanceof ReferenceBinding) && this.type.isValidBinding();
}
public boolean hasSameTypeAs(ITeamAnchor other) {
return TypeBinding.equalsEquals(leafType(), ((TeamAnchor)other).leafType());
}
public TeamModel getTeamModelOfType() {
return leafType().getTeamModel();
}
public boolean isTypeCompatibleWith(ReferenceBinding other) {
if (! (this.type instanceof ReferenceBinding))
return false;
if (this.type.isRole())
return (((ReferenceBinding)this.type).getRealClass().isCompatibleWith(other));
return this.type.isCompatibleWith(other);
}
public boolean isTypeCompatibleWithTypeOf(ITeamAnchor other) {
ReferenceBinding otherAnchorType = (ReferenceBinding)((TeamAnchor)other).type;
if ( otherAnchorType.isRole()
&& !otherAnchorType.isInterface())
{
// for compatibility check don't use a role-class-part
otherAnchorType = otherAnchorType.roleModel.getInterfacePartBinding();
}
return this.type.isCompatibleWith(otherAnchorType);
}
public void setStaticallyKnownTeam(RoleTypeBinding rtb) {
ReferenceBinding teamBinding = leafType();
if (teamBinding.isSynthInterface()) {
// never use a synth interface as the team of a RTB
ReferenceBinding outerTeam = teamBinding.enclosingType();
char[] teamName = CharOperation.concat(
IOTConstants.OT_DELIM_NAME,
teamBinding.internalName());
teamBinding = outerTeam.getMemberType(teamName);
}
rtb._staticallyKnownTeam = teamBinding;
}
public boolean isTeamContainingRole(ReferenceBinding roleType) {
return TeamModel.isTeamContainingRole(leafType(), roleType);
}
/**
* Get a field from this anchor's type.
*/
public FieldBinding getFieldOfType(char[] token, boolean isStatic, boolean allowOuter) {
return TypeAnalyzer.findField(
leafType(),
token,
isStatic,
allowOuter);
}
public ReferenceBinding getMemberTypeOfType(char[] name) {
// safe if type is a class-part
ReferenceBinding roleType = (leafType()).getMemberType(name);
if (roleType == null)
return null;
return (RoleTypeBinding)getRoleTypeBinding(roleType, 0);
}
public RoleModel getStrengthenedRole (ReferenceBinding role) {
if (TypeBinding.notEquals(role.roleModel.getTeamModel().getBinding(), leafType()))
return (leafType()).getMemberType(role.internalName()).roleModel;
return role.roleModel;
}
/**
* Try to interpret existingAnchor relative to receiver,
* composing a new anchor path from receiver and existingAnchor.
* @param site type where resolving takes places,
* an implicit this of this type can always be assumed as an implicit anchor.
* FIXME(SH): make sure we always have that instance (static scopes??)
* @param existingAnchor
* @param receiver
*
* @return a team anchor or null
*/
public static ITeamAnchor maybeImproveAnchor(ReferenceBinding site, ITeamAnchor existingAnchor, Expression receiver)
{
TeamAnchor receiverVar = null;
ITeamAnchor fallback = null;
if (receiver instanceof CastExpression)
receiver = ((CastExpression)receiver).expression;
if (receiver instanceof NameReference) {
Binding bind = ((NameReference)receiver).binding;
if (bind instanceof VariableBinding)
receiverVar = (VariableBinding)bind;
} else if (receiver instanceof AllocationExpression) {
// need to record that this anchor is unique (not compatible to any other anchor).
fallback = receiverVar = new FieldBinding(("'non-final anchor "+receiver.toString()+'\'').toCharArray(), //$NON-NLS-1$
receiver.resolvedType, // type
ClassFileConstants.AccFinal,
site, // declaring class
Constant.NotAConstant)
{
@Override public int problemId() { return IProblem.AnchorNotFinal; }
};
} else {
TypeBinding receiverType = receiver.resolvedType;
if ( receiverType != null
&& RoleTypeBinding.isRoleWithExplicitAnchor(receiverType))
{
receiverVar = (TeamAnchor)((IRoleTypeBinding)receiverType).getAnchor();
}
}
if (receiverVar != null) {
ITeamAnchor[] prefix = existingAnchor.getBestNamePath();
ITeamAnchor previous = null;
if (prefix != null && prefix.length > 0)
previous = prefix[prefix.length-1];
if (previous instanceof FieldBinding) {
if (DependentTypeBinding.isDependentTypeOf(receiverVar.type, previous))
{
DependentTypeBinding depReceiver = (DependentTypeBinding)receiverVar.type;
return existingAnchor.replaceFirst(depReceiver._teamAnchor);
}
FieldBinding previousField = (FieldBinding)previous;
ReferenceBinding currentType = null;
boolean firstImplicitlyReachable = false;
if (receiverVar instanceof FieldBinding)
currentType = ((FieldBinding)receiverVar).declaringClass;
else if (receiverVar instanceof LocalVariableBinding)
currentType = ((LocalVariableBinding)receiverVar).declaringScope.enclosingSourceType();
while(currentType != null) {
if (currentType.isCompatibleWith(previousField.declaringClass)) {
firstImplicitlyReachable = true;
break;
// reachable without navigating receiverVar
}
currentType = currentType.enclosingType();
}
currentType = receiverVar.leafType();
while(currentType != null) {
if (currentType.isCompatibleWith(previousField.declaringClass)) {
if (firstImplicitlyReachable) {
if (fallback != null)
return fallback; // if we detected non-finalness above, now is the time to report this back
} else {
return existingAnchor.setPathPrefix(receiverVar);
}
}
currentType = currentType.enclosingType();
fallback = null; // don't use any more after we have traveled out
}
if (firstImplicitlyReachable)
return existingAnchor; // didn't improve but improvement wasn't necessary
}
return null;
}
// did not improve
return null;
}
public TypeBinding getResolvedType() {
if (leafType() != null && leafType().isRole()) {
ReferenceBinding roleIfc = leafType().roleModel.getInterfacePartBinding();
if (this.bestNamePath != null && this.bestNamePath.length > 1) {
int len = this.bestNamePath.length;
ITeamAnchor previousAnchor = this.bestNamePath[len-2];
if (previousAnchor.isTeam()) {
for (int i=len-3; i>=0; i--)
previousAnchor = previousAnchor.setPathPrefix(this.bestNamePath[i]);
TypeBinding[] typeArguments = this.type.isParameterizedType() ? ((ParameterizedTypeBinding)this.type).arguments : null;
return previousAnchor.getDependentTypeBinding(roleIfc, -1, typeArguments, this.type.dimensions());
}
}
return roleIfc;
}
return this.type;
}
// internal helper: when used as an anchor an arraybinding is stripped to its leaf:
private ReferenceBinding leafType() {
if (this.type != null)
return (ReferenceBinding)this.type.leafComponentType();
return null;
}
/** Answer the declaring class of the first path element, if it
* is either a field or tthis.
*/
public ReferenceBinding getFirstDeclaringClass() {
if (this.bestNamePath[0] instanceof FieldBinding)
return ((FieldBinding)this.bestNamePath[0]).declaringClass;
if (this.bestNamePath[0] instanceof TThisBinding)
return ((TThisBinding)this.bestNamePath[0]).declaringClass;
return null;
}
// ====== manage role types anchored to this variable ======
/**
* Retrieve or create and record a role type depending on this variable as its anchor.
* Use this factory method instead of new RoleTypeBinding()!
*
* PRE: type checking done.
*
* @param roleBinding the pure ReferenceBinding of the role
* @param dimensions if > 0 we request an array of roles.
* @return either RoleTypeBinding or ArrayBinding.
*/
public TypeBinding getRoleTypeBinding(
ReferenceBinding roleBinding,
int dimensions)
{
TypeBinding[] typeArguments = roleBinding.isParameterizedType() ? ((ParameterizedTypeBinding)roleBinding).arguments : null;
return getRoleTypeBinding(roleBinding, typeArguments, dimensions);
}
public TypeBinding getRoleTypeBinding(
ReferenceBinding roleBinding,
TypeBinding[] arguments,
int dimensions)
{
if (kind() == Binding.LOCAL)
((LocalVariableBinding)this).useFlag = LocalVariableBinding.USED;
if (RoleTypeBinding.isRoleWithExplicitAnchor(roleBinding)) {
// ITeamAnchor combinedAnchor = combineAnchors(this, ((RoleTypeBinding)roleBinding)._teamAnchor);
// if (combinedAnchor == null) {
// ITeamAnchor anchor = ((RoleTypeBinding)roleBinding)._teamAnchor;
// if (anchor.isPrefixLegal(roleBinding, this)) {
// //combinedAnchor = anchor.setPathPrefix(this);
// throw new InternalCompilerError("HERE!");
// } else {
// FIXME(SH): who calls us with an RTB ?? is this strategy OK?
// saw a call from FieldAccessSpec.resolveFeature() (improving anchor)
return getRoleTypeBinding(roleBinding.getRealType(), arguments, dimensions);
// }
// }
// return combinedAnchor.getRoleTypeBinding(roleBinding.getRealType(), dimensions);
}
ReferenceBinding roleEnclosing = roleBinding.enclosingType();
if ( roleEnclosing != null
&& TypeBinding.notEquals(roleEnclosing.erasure(), leafType().getRealClass())) // i.e.: teams differ
{
//assert TeamModel.areCompatibleEnclosings(this.leafType(), roleEnclosing);
// team of roleBinding is less specific than this anchor => requesting a weakened type
ReferenceBinding strengthenedRole =
(ReferenceBinding)TeamModel.strengthenRoleType(leafType(), roleBinding);
if (TypeBinding.notEquals(strengthenedRole, roleBinding)) {// avoid infinite recursion if strengthening made no difference
DependentTypeBinding strongRoleType = (DependentTypeBinding)getRoleTypeBinding(strengthenedRole, arguments, 0);
if (strongRoleType != null)
return WeakenedTypeBinding.makeWeakenedTypeBinding(strongRoleType, roleBinding, dimensions);
}
if (dimensions == roleBinding.dimensions() && roleBinding instanceof DependentTypeBinding)
return roleBinding;
}
int paramPosition = -1;
if (roleBinding instanceof DependentTypeBinding)
paramPosition = ((DependentTypeBinding)roleBinding)._valueParamPosition;
if (roleBinding instanceof RoleTypeBinding)
if (!this.isTeamContainingRole(roleBinding)) // need to switch to outer anchor?
return this.asAnchorFor(roleBinding).getDependentTypeBinding(roleBinding, paramPosition, arguments, dimensions, ((RoleTypeBinding)roleBinding).environment);
if (roleBinding instanceof WeakenedTypeBinding) {
if (((WeakenedTypeBinding)roleBinding)._teamAnchor == this)
return roleBinding;
}
if (roleBinding instanceof ParameterizedTypeBinding)
return getDependentTypeBinding(roleBinding, paramPosition, arguments, dimensions, ((ParameterizedTypeBinding)roleBinding).environment);
return getDependentTypeBinding(roleBinding, paramPosition, arguments, dimensions);
}
/**
* Retrieve or create and record a role type depending on this variable as its anchor.
* Use this factory method instead of new RoleTypeBinding()!
*
* PRE: type checking done.
*
* @param typeBinding the pure ReferenceBinding of the role
* @param dimensions if > 0 we request an array of roles.
* @return either RoleTypeBinding or ArrayBinding.
*/
public TypeBinding getDependentTypeBinding(
ReferenceBinding typeBinding,
int paramPosition,
TypeBinding[] arguments,
int dimensions)
{
try {
return getDependentTypeBinding(typeBinding, paramPosition, arguments, dimensions, Config.getLookupEnvironment());
} catch (NotConfiguredException e) {
throw new AbortCompilation(false, e);
}
}
public TypeBinding getDependentTypeBinding(
ReferenceBinding typeBinding,
int paramPosition,
TypeBinding[] arguments,
int dimensions,
LookupEnvironment env)
{
DependentTypeBinding dependentTypeBinding =
(DependentTypeBinding)env.createParameterizedType(typeBinding, arguments, this, paramPosition, typeBinding.enclosingType(), Binding.NO_ANNOTATIONS);
return (dimensions > 0)
? dependentTypeBinding.getArrayType(dimensions)
: dependentTypeBinding;
}
public TypeBinding resolveRoleType(char[] roleName, int dims) {
if (!this.type.isTeam())
return null;
ReferenceBinding roleType = ((ReferenceBinding)this.type).getMemberType(roleName);
if (roleType == null || !roleType.isValidBinding())
return roleType; // FIXME(SH): is it correct to return a ProblemBinding? Check clients!
return getRoleTypeBinding(roleType, dims);
}
}