| /********************************************************************** |
| * This file is part of "Object Teams Development Tooling"-Software |
| * |
| * Copyright 2006 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: DependentTypeBinding.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 java.util.WeakHashMap; |
| |
| import org.eclipse.jdt.core.compiler.CharOperation; |
| import org.eclipse.jdt.internal.compiler.ast.ASTNode; |
| import org.eclipse.jdt.internal.compiler.ast.Argument; |
| import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.Binding; |
| import org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers; |
| import org.eclipse.jdt.internal.compiler.lookup.FieldBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.InferenceContext; |
| import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment; |
| import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.MethodScope; |
| 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.SyntheticArgumentBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.TagBits; |
| import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.TypeIds; |
| import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.VariableBinding; |
| import org.eclipse.objectteams.otdt.core.compiler.IOTConstants; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.control.Config; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.model.TeamModel; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.statemachine.transformer.ReflectionGenerator; |
| |
| /** |
| * NEW for OTDT |
| * |
| * This class handles the dependent type mechanisms for role types and types with value parameters. |
| * |
| * For documentation of declaration of such types see TypeValueParameter. |
| * For documentation of application of such types see TypeAnchorReference. |
| * |
| * Current limitations: |
| * - Only one value parameter is supported, yet. |
| * |
| * Design note: since 1.3.0 this class is a subclass of ParameterizedTypeBinding in order to |
| * support combinations of value parameters and type parameters, specifically, generic role types. |
| * |
| * @author stephan |
| * @since 0.9.0. |
| */ |
| public class DependentTypeBinding extends ParameterizedTypeBinding { |
| |
| // TODO(SH): in order to support multiple anchors, turn the next fields into arrays!! |
| /** The Team to which this role type is anchored, given by a variable which must be final. */ |
| public ITeamAnchor _teamAnchor; |
| /** For externalized roles anchored to a method argument: index into the method argument list pointing to the team anchor. */ |
| public int _argumentPosition = -1; |
| public IMethodProvider _declaringMethod = null; // the method declaring the anchor argument |
| |
| /** A type/method with value parameters stores the field/argument representing that parameter here: */ |
| public VariableBinding _matchingVariable; |
| /** For value parameters: index into the type parameter list: */ |
| public int _valueParamPosition = -1; |
| |
| public static interface IMethodProvider { |
| public MethodBinding getMethod(); |
| } |
| |
| // REGISTRIES/CACHES: |
| // caching instantiations by anchor: |
| private WeakHashMap<ITeamAnchor, DependentTypeBinding> _instantiatedTypes = new WeakHashMap<ITeamAnchor, DependentTypeBinding>(); |
| // Each DependentTypeBinding stores unique bindings for any dimension of arrays of this type. |
| private ArrayBinding[] _arrayBindings = null; |
| |
| /** Public constructor for clients (FIXME: let only environment create us) */ |
| public DependentTypeBinding(ReferenceBinding type, TypeBinding[] typeArguments, ITeamAnchor teamAnchor, int paramPosition, ReferenceBinding enclosingType, LookupEnvironment lookupEnvironment) |
| { |
| super(type, typeArguments, teamAnchor, enclosingType, lookupEnvironment); |
| initializeFromType(type); |
| initializeDependentType(teamAnchor, paramPosition); |
| } |
| |
| /** For subclasses. Still need to call initializeDependentType afterwards. */ |
| DependentTypeBinding(ReferenceBinding type, TypeBinding[] arguments, ITeamAnchor teamAnchor, ReferenceBinding enclosingType, LookupEnvironment environment) |
| { |
| super(type, arguments, teamAnchor, enclosingType, environment); |
| initializeFromType(type); |
| } |
| private void initializeFromType(ReferenceBinding givenType) { |
| this.model = givenType.model; // shared model from type. |
| this.modifiers = givenType.modifiers; |
| this.tagBits = givenType.tagBits; |
| this.tagBits &= ~(TagBits.AreMethodsSorted|TagBits.AreMethodsComplete); // in case the generic type was already processed |
| |
| this.compoundName = givenType.compoundName; |
| this.sourceName = givenType.sourceName; |
| this.constantPoolName = givenType.constantPoolName; |
| this.callinCallouts = givenType.callinCallouts; |
| this.precedences = givenType.precedences; |
| this.fileName = givenType.fileName; |
| this.fPackage = givenType.fPackage; |
| this.teamPackage = givenType.teamPackage; |
| |
| if (givenType.isTypeVariable()) { // concrete type unknown ... |
| ITeamAnchor[] anchors = ((TypeVariableBinding)givenType).anchors; |
| if (anchors != null && anchors[0] instanceof LocalVariableBinding) { |
| final LocalVariableBinding anchor = (LocalVariableBinding) anchors[0]; |
| if ((anchor.tagBits & TagBits.IsArgument) != 0) { // ... but known to be anchored to an argument |
| this._argumentPosition = anchor.resolvedPosition; |
| this._declaringMethod = new IMethodProvider() { @Override |
| public MethodBinding getMethod() { |
| return ((MethodScope)anchor.declaringScope).referenceMethodBinding(); |
| }}; |
| } |
| } |
| } |
| // registerAnchor(); |
| } |
| |
| void initializeDependentType(ITeamAnchor anchor, int valueParamPosition) |
| { |
| // don't store baseclass, will be initialized by baseclass() |
| |
| this._valueParamPosition = valueParamPosition; |
| SyntheticArgumentBinding[] valParams = this.type.valueParamSynthArgs(); |
| if (valueParamPosition > -1) // subtypes don't set paramPosition/matchingField |
| if (valueParamPosition < valParams.length) // defensive |
| this._matchingVariable = valParams[valueParamPosition].matchingField; |
| |
| this._teamAnchor = anchor; |
| if (anchor instanceof LocalVariableBinding) |
| ((LocalVariableBinding)anchor).useFlag = LocalVariableBinding.USED; |
| else if (anchor instanceof FieldBinding) |
| ((FieldBinding)anchor).modifiers |= ExtraCompilerModifiers.AccLocallyUsed; // avoid unused-warning |
| } |
| |
| public void updateAnchor(Argument[] methodArguments) { |
| if (this._argumentPosition > -1) { |
| this._teamAnchor = methodArguments[this._argumentPosition].binding; |
| } |
| } |
| |
| public ITeamAnchor getAnchor() { return this._teamAnchor; } |
| |
| /* answer a (cached) array type whose elements are of the current RoleType. */ |
| // TODO(SH): should we replace this with Scope.createArray?? |
| public ArrayBinding getArrayType(int dims) { |
| if ((this._arrayBindings == null) || (this._arrayBindings.length < dims)) { |
| ArrayBinding[] oldArrays = this._arrayBindings; |
| this._arrayBindings = new ArrayBinding[dims]; |
| if (oldArrays != null) |
| System.arraycopy(oldArrays, 0, this._arrayBindings, 0, oldArrays.length); |
| } |
| if (this._arrayBindings[dims-1] == null) |
| this._arrayBindings[dims-1] = new ArrayBinding(this, dims, this.environment); |
| return this._arrayBindings[dims-1]; |
| } |
| |
| /** |
| * Register an anchor-RTB pair at both sides. |
| */ |
| protected void registerAnchor() { |
| this._instantiatedTypes.put(this._teamAnchor, this); |
| } |
| |
| /** Does this type have an anchor other than tthis? */ |
| public boolean hasExplicitAnchor() { |
| return this._teamAnchor != null && |
| this._teamAnchor.isValidBinding() && |
| !(this._teamAnchor instanceof TThisBinding); |
| } |
| |
| /** Answer whether this dependent type is anchored within a method's signature, |
| * and if a method is given, also check whether that's the one. |
| */ |
| public boolean hasAnchorWithinThisMethodsSignature(MethodBinding method) { |
| if (method != null) { |
| if (this._declaringMethod == null) |
| return false; |
| if (method != this._declaringMethod.getMethod()) |
| return false; |
| } |
| return this._argumentPosition > -1; |
| } |
| |
| /** |
| * Refine the anchor of this role type to be 'anchor'. |
| * Retrieve from cache whenever possible. |
| * Donot merge anchors. |
| * Donot downgrade explicit anchor to TThis. |
| * |
| * @param anchor |
| * @param dimensions |
| * @return a role type binding, possibly this. |
| */ |
| public TypeBinding maybeInstantiate(ITeamAnchor anchor, int dimensions) { |
| RoleTypeBinding cached = (RoleTypeBinding)this._instantiatedTypes.get(anchor); |
| if (cached != null) |
| return cached; |
| if ( anchor == null |
| || anchor == this._teamAnchor) |
| { |
| if (dimensions > 0) |
| return getArrayType(dimensions); |
| return this; |
| } |
| if (anchor instanceof TThisBinding) { |
| if ( !(this._teamAnchor instanceof TThisBinding) |
| || this._teamAnchor.isTypeCompatibleWithTypeOf(anchor)) |
| { |
| // don't downgrade: |
| if (dimensions > 0) |
| return getArrayType(dimensions); |
| return this; |
| } |
| } |
| |
| if (!anchor.isTypeCompatibleWithTypeOf(this._teamAnchor)) |
| return this; |
| // clone: |
| return forAnchor(anchor, dimensions); |
| } |
| |
| // hook within maybeInstantiate: |
| TypeBinding forAnchor(ITeamAnchor anchor, int dimensions) { |
| return anchor.getRoleTypeBinding(this, dimensions); // TODO(SH): use a shared type instead of this? |
| } |
| |
| @Override |
| public void collectSubstitutes(Scope scope, TypeBinding actualType, InferenceContext inferenceContext, int constraint) { |
| this.transferTypeArguments(this.type).collectSubstitutes(scope, actualType, inferenceContext, constraint); |
| } |
| |
| |
| // implement new method from Substitution: |
| @Override |
| public ITeamAnchor substituteAnchor(ITeamAnchor anchor, int rank) { |
| if (this._matchingVariable == anchor) |
| return getAnchor(); |
| return null; |
| } |
| |
| @Override |
| public ReferenceBinding transferTypeArguments(ReferenceBinding other) { |
| if (this.arguments == null) // although subclass of ParameterizedTypeBinding, we don't necessarily have arguments |
| return other; |
| return super.transferTypeArguments(other); |
| } |
| |
| public static boolean mayTakeValueParam(TypeBinding binding) { |
| if (binding.isRole()) |
| return true; |
| if (binding.isTypeVariable()) |
| return true; // could be anchored type variable |
| return binding.valueParamSynthArgs() != NO_SYNTH_ARGUMENTS; |
| } |
| |
| @Override |
| public boolean isParameterizedType() { |
| return this.arguments != null; // by inheritance we'd be parameterized, but if we don't have arguments, it's only value parameters. |
| } |
| |
| @Override |
| public DependentTypeBinding asPlainDependentType() { |
| return this; |
| } |
| |
| @Override |
| public boolean isPlainDependentType() { |
| return true; |
| } |
| |
| /** |
| * Is type a dependent type and declaredAnchor the field representing its type value parameter? |
| */ |
| public static boolean isDependentTypeOf(TypeBinding type, ITeamAnchor declaredAnchor) { |
| if (type == null || !type.isPlainDependentType()) |
| return false; |
| DependentTypeBinding depType = (DependentTypeBinding)type; |
| return depType._matchingVariable == declaredAnchor; |
| } |
| |
| public static boolean isDependentTypeVariable(TypeBinding binding) { |
| if (!(binding instanceof DependentTypeBinding)) |
| return false; |
| return ((DependentTypeBinding)binding).type.kind() == TYPE_PARAMETER; |
| } |
| |
| /** ignoring the type part, check if anchors are provably identical. */ |
| public boolean hasEquivalentAnchorTo(TypeBinding otherType) { |
| if (otherType instanceof DependentTypeBinding) { |
| DependentTypeBinding otherRoleType = (DependentTypeBinding)otherType; |
| if (this._teamAnchor.hasSameBestNameAs(otherRoleType._teamAnchor)) |
| return true; |
| } |
| return false; |
| } |
| |
| @Override |
| public boolean isEquivalentTo(TypeBinding otherType) { |
| if (! (otherType instanceof DependentTypeBinding)) |
| return this.type.isEquivalentTo(otherType); |
| DependentTypeBinding otherDep = (DependentTypeBinding)otherType; |
| // same best name implies that checking of simple names suffices (thanks to OTJLD 1.4(c)): |
| return otherDep._teamAnchor.hasSameBestNameAs(this._teamAnchor) |
| && CharOperation.equals(otherDep.internalName(), internalName()); |
| } |
| |
| @Override |
| public boolean isProvablyDistinct(TypeBinding otherType) { |
| if (otherType instanceof DependentTypeBinding) |
| otherType = ((DependentTypeBinding)otherType).getRealType(); |
| // for internal casting purposes type anchors can be ignored: |
| return getRealType().isProvablyDistinct(otherType); |
| } |
| |
| @Override |
| public int kind() { |
| return (this.arguments != null) ? Binding.PARAMETERIZED_TYPE : this.type.kind(); |
| } |
| |
| /** |
| * Re-check lowering ambiguity (OTJLD 2.2(f)) after type checking had detected this situation before. |
| * (see {@link Config#getLoweringPossible()}). |
| * |
| * @param otherType declared type to which a value of this type is being attached. |
| * @param location AST node for problem reporting |
| * @param scope scope for problem reporting |
| * @param resolvedMethod when resolving a message send this is the resolved method |
| */ |
| public void recheckAmbiguousLowering(TypeBinding otherType, ASTNode location, Scope scope, MethodBinding resolvedMethod) { |
| if (scope.isGeneratedScope()) return; |
| if ( otherType instanceof ReferenceBinding |
| && ((ReferenceBinding)otherType).id == TypeIds.T_JavaLangObject |
| && baseclass() != null) |
| { |
| if (resolvedMethod != null) { |
| if (resolvedMethod.declaringClass.isTeam()) { |
| // don't report against unregisterRole(Object), unregisterRole(Object,Class) |
| // -- we know arg should be the unlowered role! |
| if ( CharOperation.equals(resolvedMethod.selector, ReflectionGenerator.UNREGISTER_ROLE) |
| && ( resolvedMethod.parameters.length == 1 |
| || resolvedMethod.parameters.length == 2)) |
| return; |
| } |
| } |
| scope.problemReporter().ambiguousUpcastOrLowering(location, otherType, this); |
| } |
| } |
| |
| // ===>>> delegating methods: |
| @Override |
| public FieldBinding[] fields() { |
| return this.type.fields(); |
| } |
| @Override |
| public FieldBinding getField(char[] fieldName, boolean needResolve) { |
| return this.type.getField(fieldName, needResolve); |
| } |
| @Override |
| public MethodBinding getExactConstructor(TypeBinding[] argumentTypes) { |
| return this.type.getExactConstructor(argumentTypes); |
| } |
| @Override |
| public ReferenceBinding getMemberType(char[] typeName) { |
| return this.type.getMemberType(typeName); |
| } |
| @Override |
| public SyntheticArgumentBinding[] valueParamSynthArgs() { |
| return this.type.valueParamSynthArgs(); |
| } |
| |
| @Override |
| public TeamModel getTeamModel() { |
| if (this._teamModel == null && this.type != null) |
| this._teamModel = this.type.getTeamModel(); |
| return this._teamModel; |
| } |
| |
| @Override |
| public ReferenceBinding getRealType() { |
| return this.type.getRealType(); |
| } |
| |
| @Override |
| public ReferenceBinding getRealClass() { |
| return this.type.getRealClass(); |
| } |
| |
| @Override |
| public ReferenceBinding rawBaseclass() { |
| return this.type.rawBaseclass(); |
| } |
| |
| @Override |
| public TypeBinding original() { |
| return this.type.original(); |
| } |
| |
| @Override |
| public void setIsBoundBase(ReferenceBinding roleType) { |
| super.setIsBoundBase(roleType); |
| this.type.setIsBoundBase(roleType); |
| } |
| // delegating methods <<<=== |
| |
| @Override |
| @SuppressWarnings("nls") |
| public String toString() { |
| String anchorStr = ""; |
| ITeamAnchor[] bestNamePath = this._teamAnchor.getBestNamePath(false); |
| for (int i = 0; i < bestNamePath.length; i++) { |
| if (i>0) |
| anchorStr += "."; |
| anchorStr += new String(bestNamePath[i].readableName()); |
| } |
| return |
| new String(sourceName()) |
| + "<@" + anchorStr + ">"; |
| } |
| |
| @Override |
| public boolean appendReadableValueParameterNames(StringBuffer buf) /*@anchor[,...]*/ { |
| buf.append('@'); |
| buf.append(anchorName()); |
| return true; |
| } |
| |
| private char[] anchorName() { |
| char[] anchorName = new char[0]; |
| ITeamAnchor[] bestNamePath = this._teamAnchor.getBestNamePath(false); |
| for (int i = 0; i < bestNamePath.length; i++) { |
| char[] segment = bestNamePath[i].readableName(); |
| if (CharOperation.equals(segment, IOTConstants._OT_BASE)) |
| segment = "base".toCharArray(); //$NON-NLS-1$ |
| anchorName = CharOperation.concatWith( |
| new char[][]{anchorName, segment}, |
| '.'); |
| } |
| return anchorName; |
| } |
| } |