| /********************************************************************** |
| * This file is part of "Object Teams Development Tooling"-Software |
| * |
| * Copyright 2003, 2010 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: RoleTypeBinding.java 23416 2010-02-03 19:59:31Z 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.HashMap; |
| |
| import org.eclipse.jdt.core.compiler.CharOperation; |
| import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; |
| import org.eclipse.jdt.internal.compiler.lookup.Binding; |
| import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope; |
| import org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers; |
| 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.MethodBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.MethodScope; |
| import org.eclipse.jdt.internal.compiler.lookup.PackageBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.ParameterizedFieldBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.ProblemReasons; |
| import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.Scope; |
| import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding; |
| 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.core.compiler.OTNameUtils; |
| import org.eclipse.objectteams.otdt.core.exceptions.InternalCompilerError; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.control.Config; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.control.Dependencies; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.control.ITranslationStates; |
| 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.statemachine.transformer.RoleSplitter; |
| |
| |
| /** |
| * NEW for OTDT. |
| * |
| * The type of a role is wrapped by a RoleTypeBinding. |
| * This class takes care of anchoring each role type to an enclosing Team instance. |
| * If the type anchor (a VariableBinding) is typed to a sub-type of |
| * the Team containing the original role type, |
| * we use the corresponding role from the sub-Team. |
| * |
| * What: Signature weakening |
| * Why: Creation of a RTB might pass a role type from a super team together with an anchor |
| * from a sub-team due to signature weakening. |
| * How: Effecting the byte code generation: |
| * + Store weak type in _declaredRoleType as used for constantPoolName() |
| * also TypeAnalyzer.getMethodReturnTypeReference() uses _declaredRoleType() [should others, too?] |
| * + roleModel, _teamModel are kept consistent with _declaredRoleType, |
| * roleModel is used by TeamAnchor registerRoleType -> tell those types apart |
| * that will generate different byte code! |
| * For use by type checking: |
| * + Stronger type derived from team anchor is stored in _staticallyKnownRole{Class,Type} |
| * |
| * What: Role types anchored to a method parameter |
| * Why: These require special substitution strategy, since argument names may not be available |
| * when processing message sends, so positions must be used instead. |
| * How: Store _argumentPosition of team anchor. |
| * Persist this position in AnchorListAttribute |
| * Map anchor in AnchorMapping (several locations) |
| * Setting _argumentPosition happens in |
| * + RoleTypeCreator.getParameterType() (case of source type) |
| * + AnchorListAttribute.getWrappedType() (case of binary type) |
| * |
| * What: Break circularity of org.objectteams.Team$__OT__Confined |
| * Why: Copies of __OT__Confined are the only classes, whose superclass-link |
| * actually uses the tsuper role from org.objectteams.Team. |
| * (so only this predefined class has a null-superclass). |
| * Function superclass() would loop for ever on these types, because |
| * using a TThis-anchor it would remap the super class Team.__OT__Confined |
| * to its copy in the current team => class and its superclass are the same! |
| * How: For confined classes preserve the actual enclosing type rather then |
| * deducing that from the team anchor (RoleTypeBinding(ITeamAnchor, ReferenceBinding)) |
| * |
| * REGISTRIES: |
| * ----------- |
| * What: Instantiated types (with different team anchors) |
| * How: Register these in _instantiatedTypes(), managed by |
| * + maybeInstantiate() |
| * - registerAnchor() (for use by our constructors only) |
| * |
| * What: Arrays (with different dimensions) |
| * Where: _arrayBindings stores all known array types derived from a given RTB |
| * This field is encapsulated by getArrayType(dims) |
| * Also: maybeInstantiate(teamAnchor, dimensions), ITeamAnchor.getRoleTypeBinding(type, dims) |
| * |
| * FORWARDING: |
| * ----------- |
| * What: Forward API-functions to either _staticallyKnownRoleType or ..Class |
| * How: Type: methods and constant fields |
| * Class: fields, constructors, memberTypes |
| * |
| * TYPE CHECKING: |
| * -------------- |
| * What: Main entry is "isCompatibleWith()" |
| * This function has a side effect in possible calling |
| * Config.setCastRequired(), Config.setLoweringRequired() |
| * |
| * FIELD ACCESS REPLACING: |
| * ----------------------- |
| * What: When accessing a role field, the role expression has to be wrapped with a cast. |
| * Why: Role expressions are typed to the interface, fields however reside in the class. |
| * Where: checkReplaceFieldAccess() |
| * |
| * |
| * TODO(SH): This class manifests a current compiler limitation: an outer scope cannot |
| * access a field of a contained role. This is so difficult, because: |
| * - cast to role class locks to a concrete class, breaks implicit inheritance. |
| * - cast method is dynamically bound does not return role _class_ |
| * - the need for access methods which could make this would sometimes be |
| * detected far too late. |
| * |
| * @author stephan |
| */ |
| public class RoleTypeBinding extends DependentTypeBinding |
| implements IRoleTypeBinding, IOTConstants, ProblemReasons |
| { |
| // used to signal that no valid anchor was found: |
| public static final VariableBinding NoAnchor = new LocalVariableBinding( |
| "<no valid anchor>".toCharArray(), TypeBinding.NULL, ClassFileConstants.AccFinal, false); //$NON-NLS-1$ |
| |
| |
| public ReferenceBinding _declaredRoleType; // as passed to the constructor FIXME(SH): fade out |
| private ReferenceBinding _staticallyKnownRoleType; // the interface FIXME(SH): unify with DTB._type |
| private ReferenceBinding _staticallyKnownRoleClass; // the class if we have one |
| public ReferenceBinding _staticallyKnownTeam; // the static type of the team anchor |
| private ReferenceBinding _superClass; // caches the result of superclass() |
| |
| |
| // ============= CREATION AND INSTANCE REGISTRY: =================== |
| public RoleTypeBinding(ReferenceBinding genericType, TypeBinding[] typeArguments, ITeamAnchor teamAnchor, ReferenceBinding enclosingType, LookupEnvironment lookupEnvironment) |
| { |
| super(genericType.getRealType(), typeArguments, teamAnchor, enclosingType, lookupEnvironment); |
| initialize(genericType, teamAnchor); |
| } |
| |
| private void initialize(ReferenceBinding roleType, ITeamAnchor teamAnchor) |
| { |
| // FIXME(SH): is it OK to strip ParameterizedFields? |
| if (teamAnchor instanceof ParameterizedFieldBinding) |
| teamAnchor = ((ParameterizedFieldBinding)teamAnchor).original(); |
| |
| initializeDependentType(teamAnchor, -1); // role type bindings have no explicit value parameters |
| |
| // infer argument position. |
| ITeamAnchor firstAnchorSegment = teamAnchor.getBestNamePath()[0]; |
| if ( firstAnchorSegment instanceof LocalVariableBinding |
| && (((LocalVariableBinding)firstAnchorSegment).tagBits & TagBits.IsArgument) != 0) |
| { |
| LocalVariableBinding argumentBinding = (LocalVariableBinding)firstAnchorSegment; |
| this._argumentPosition = argumentBinding.resolvedPosition; |
| final MethodScope methodScope = argumentBinding.declaringScope.methodScope(); |
| if (methodScope != null) |
| // if scope is a callout, role method may not yet be resolved, defer: |
| this._declaringMethod = new IMethodProvider() { |
| private MethodBinding binding; |
| @Override |
| public MethodBinding getMethod() { |
| if (this.binding == null) |
| this.binding = methodScope.referenceMethodBinding(); |
| return this.binding; |
| } |
| }; |
| } |
| |
| // compute the team: |
| if (CharOperation.equals(roleType.compoundName, IOTConstants.ORG_OBJECTTEAMS_TEAM_OTCONFINED)) |
| // the following is needed in order to break the circularity |
| // of roles extending the predefined Team.__OT__Confined (see class comment) |
| this._staticallyKnownTeam = roleType.enclosingType(); |
| else if (teamAnchor == NoAnchor) |
| this._staticallyKnownTeam = roleType.enclosingType(); |
| else |
| teamAnchor.setStaticallyKnownTeam(this); |
| |
| assert(this._staticallyKnownTeam.isTeam()); |
| |
| // compute role class and role interface (by name manipulation): |
| if (RoleSplitter.isClassPartName(roleType.sourceName)) { |
| this._staticallyKnownRoleClass = roleType.getRealClass(); |
| char[] typeName = RoleSplitter.getInterfacePartName(roleType.sourceName); |
| this._staticallyKnownRoleType = this._staticallyKnownTeam.getMemberType(typeName); |
| } else { |
| this._staticallyKnownRoleType = roleType.getRealType(); |
| char[] className = CharOperation.concat( |
| OT_DELIM_NAME, |
| this._staticallyKnownRoleType.sourceName); |
| this._staticallyKnownRoleClass = this._staticallyKnownTeam.getMemberType(className); |
| this._staticallyKnownRoleClass = transferTypeArguments(this._staticallyKnownRoleClass); |
| } |
| this._staticallyKnownRoleType = transferTypeArguments(this._staticallyKnownRoleType); |
| |
| this._declaredRoleType = this._staticallyKnownRoleType; |
| // keep these consistent with _declaredRoleType: |
| this.roleModel = this._declaredRoleType.roleModel; |
| this._teamModel = this._declaredRoleType.getTeamModel(); |
| |
| assert TypeBinding.equalsEquals(this._staticallyKnownTeam.getRealClass().original(), roleType.enclosingType().original()): "weakening not using WeakenedTypeBinding"; //$NON-NLS-1$ |
| // some adjustments after all fields are known: |
| if (TypeBinding.notEquals(this._staticallyKnownTeam, roleType.enclosingType())) |
| this._staticallyKnownRoleType = this._staticallyKnownTeam.getMemberType(roleType.sourceName); |
| if (this._staticallyKnownRoleClass != null) |
| this.modifiers = this._staticallyKnownRoleClass.modifiers; |
| // after we might have overwritten the modifiers restore some bits in the vein of ParameterizedTypeBinding: |
| if (this.arguments != null) { |
| this.modifiers |= ExtraCompilerModifiers.AccGenericSignature; |
| } else if (this.enclosingType() != null) { |
| this.modifiers |= (this.enclosingType().modifiers & ExtraCompilerModifiers.AccGenericSignature); |
| this.tagBits |= this.enclosingType().tagBits & (TagBits.HasTypeVariable | TagBits.HasMissingType); |
| } |
| |
| // record as known role type at teamAnchor and in our own cache |
| registerAnchor(); |
| } |
| |
| @Override |
| public TypeBinding clone(TypeBinding outerType) { |
| RoleTypeBinding clone = new RoleTypeBinding(this.type, typeArguments(), this._teamAnchor, (ReferenceBinding) outerType, this.environment); |
| return clone; |
| } |
| |
| // hook of maybeInstantiate |
| @Override |
| TypeBinding forAnchor(ITeamAnchor anchor, int dimensions) { |
| return anchor.getRoleTypeBinding(this._staticallyKnownRoleType, dimensions); |
| } |
| |
| // cache field for use only by the following method |
| HashMap<ReferenceBinding, DependentTypeBinding> weakenedTypes; |
| @Override |
| public ReferenceBinding weakenFrom(ReferenceBinding other) { |
| if (other instanceof RoleTypeBinding) { |
| if (this.weakenedTypes == null) |
| this.weakenedTypes = new HashMap<ReferenceBinding, DependentTypeBinding>(); |
| ReferenceBinding knownWeakened = this.weakenedTypes.get(other); |
| if (knownWeakened != null) |
| return knownWeakened; |
| RoleTypeBinding otherRTB = (RoleTypeBinding)other; |
| DependentTypeBinding newWeakened = new WeakenedTypeBinding(this, otherRTB._declaredRoleType, this.environment); |
| this.weakenedTypes.put(other, newWeakened); |
| return newWeakened; |
| } |
| return other; |
| } |
| |
| /** Get a RoleTypeBinding from given type, either by casting or by unwrapping a weakened type. */ |
| public static RoleTypeBinding getRoleTypeBinding(TypeBinding binding) { |
| if (binding instanceof WeakenedTypeBinding) |
| return getRoleTypeBinding(((WeakenedTypeBinding)binding).type); |
| return (RoleTypeBinding)binding; |
| } |
| |
| // Are two given types basically the same type (not considering |
| // role wrapping or different class/ifc view)? |
| public static boolean type_eq(ReferenceBinding left, ReferenceBinding right) { |
| // null-checks allways first: |
| if (left == null) { |
| if (right == null) return true; |
| else return false; |
| } |
| if (right == null) return false; |
| |
| if (TypeBinding.equalsEquals(left.erasure(), right.erasure())) |
| return true; |
| |
| if (left instanceof RoleTypeBinding) { |
| RoleTypeBinding leftRole = (RoleTypeBinding)left; |
| if (right instanceof RoleTypeBinding) |
| return TypeBinding.equalsEquals(left.getRealType(), ((RoleTypeBinding)right).getRealType()); |
| if (TypeBinding.equalsEquals(leftRole.getRealType(), right)) |
| return true; |
| if (TypeBinding.equalsEquals(leftRole.getRealClass(), right)) |
| return true; |
| } else if (right instanceof RoleTypeBinding) { |
| RoleTypeBinding rightRole = (RoleTypeBinding)right; |
| if (TypeBinding.equalsEquals(rightRole.getRealType(), left)) |
| return true; |
| if (TypeBinding.equalsEquals(rightRole.getRealClass(), left)) |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Stricter comparison: almost as "==", allow different but equivalent type anchors. |
| * Similar to isEquivalentTo, but symmetric. |
| * |
| * @param left |
| * @param right |
| * @return whether or not left and right are regarded as equal as defined above. |
| */ |
| public static boolean eq(TypeBinding left, TypeBinding right) { |
| if (TypeBinding.equalsEquals(left, right)) return true; |
| DependentTypeBinding leftDep= null; |
| DependentTypeBinding rightDep= null; |
| // nesting occurs for WeakenedTypeBinding(RoleTypeBinding), e.g. |
| while (left instanceof DependentTypeBinding) { |
| leftDep= (DependentTypeBinding)left; |
| left= leftDep.type; |
| } |
| while (right instanceof DependentTypeBinding) { |
| rightDep= (DependentTypeBinding)right; |
| right= rightDep.type; |
| } |
| return leftDep != null && rightDep != null |
| && leftDep._teamAnchor.hasSameBestNameAs(rightDep._teamAnchor) |
| && CharOperation.equals(left.internalName(), right.internalName()); |
| } |
| |
| @Override |
| public boolean isRoleType() { |
| return true; |
| } |
| |
| @Override |
| public boolean isPlainDependentType() { |
| return false; |
| } |
| |
| public static boolean isRoleTypeOrArrayOfRole(TypeBinding binding) { |
| if ((binding.tagBits & TagBits.IsBaseType) != 0) |
| return false; |
| // if (binding.isParameterizedType()) { |
| // ParameterizedTypeBinding parameterizedType = (ParameterizedTypeBinding)binding; |
| // if (isRoleTypeOrArrayOfRole(parameterizedType.actualType())) |
| // return true; |
| // for (TypeBinding typeArg : parameterizedType.arguments) |
| // if (isRoleTypeOrArrayOfRole(typeArg)) return true; |
| // return false; |
| // } |
| TypeBinding leafType = binding.leafComponentType(); |
| return leafType != null && leafType.isRoleType(); |
| } |
| |
| /** |
| * Is param a role type with explicit anchor? |
| * |
| * @param type any type binding. |
| * @return the answer |
| */ |
| public static boolean isRoleWithExplicitAnchor(TypeBinding type) { |
| if (type == null) |
| return false; |
| if (!type.isRoleType()) |
| return false; |
| return ((DependentTypeBinding)type).hasExplicitAnchor(); |
| } |
| |
| /** |
| * Is param a role type with no explicit anchor? |
| * |
| * @param type any type binding. |
| * @return the answer |
| */ |
| public static boolean isRoleWithoutExplicitAnchor(TypeBinding type) { |
| if (type == null) |
| return false; |
| if (!type.isRoleType()) |
| return false; |
| return !((DependentTypeBinding)type).hasExplicitAnchor(); |
| } |
| |
| @Override |
| public DependentTypeBinding asPlainDependentType() { |
| return null; |
| } |
| |
| /** |
| * Answer whether the method has a parameter of a type of a role such that |
| * <ul> |
| * <li>the role is a role of the type declaring the method, and</li> |
| * <li>the parameter type is used by a simple reference, i.e., not explicitly anchored.</li> |
| * </ul> |
| * @param method |
| * @return the answer |
| */ |
| public static boolean hasNonExternalizedRoleParameter(MethodBinding method) { |
| ReferenceBinding declaringClass = method.declaringClass; |
| TypeBinding[] parameters = method.parameters; |
| for (int j = 0; j < parameters.length; j++) { |
| TypeBinding leafType = parameters[j].leafComponentType(); |
| if ( isRoleWithoutExplicitAnchor(leafType) |
| && TypeBinding.equalsEquals(((ReferenceBinding)leafType).enclosingType(), declaringClass)) |
| return true; |
| } |
| return false; |
| } |
| |
| // ============= Begin Instance Methods =============== |
| @Override |
| public ITeamAnchor[] getAnchorBestName() { |
| return this._teamAnchor.getBestNamePath(); |
| } |
| /** |
| * For field lookup we give the real role class "__OT__Role". |
| */ |
| @Override |
| public ReferenceBinding getRealClass() |
| { |
| if (this._staticallyKnownRoleClass != null) |
| return this._staticallyKnownRoleClass; |
| return this._staticallyKnownRoleType.getRealClass(); |
| } |
| |
| |
| /** |
| * For type comparison we give the real role type "Role". |
| */ |
| @Override |
| public ReferenceBinding getRealType() |
| { |
| return this._staticallyKnownRoleType; |
| } |
| |
| /** Answer the type that will be used in the class file: */ |
| @Override |
| public TypeBinding erasure() { |
| return this.type.erasure(); |
| } |
| |
| @Override |
| public ReferenceBinding genericType() { |
| // check if any regular genericity is involved: |
| if (this.arguments == null) { |
| if (this.type instanceof ParameterizedTypeBinding) |
| return ((ParameterizedTypeBinding) this.type).genericType(); |
| return this; // no regular type parameters/arguments involved |
| } |
| return super.genericType(); |
| } |
| |
| /** |
| * Is type `other' a sibling role of current? |
| */ |
| public boolean isSiblingRole(SourceTypeBinding other) { |
| return TypeBinding.equalsEquals(enclosingType(), other.enclosingType()); |
| } |
| |
| /** |
| * Would 'role' be the same type as 'this' if it had 'anchor'? |
| * |
| * @param role |
| * @param anchor |
| */ |
| public boolean isSameType(RoleTypeBinding role, ITeamAnchor anchor) { |
| return |
| TypeBinding.equalsEquals(role._staticallyKnownRoleType, this._staticallyKnownRoleType) |
| && TypeBinding.equalsEquals(role._staticallyKnownTeam, this._staticallyKnownTeam) |
| && this._teamAnchor.hasSameBestNameAs(anchor); |
| } |
| |
| |
| // ============== forward most queries to the _staticallyKnownRoleType. ==================== |
| |
| // --------- Find fields in the class: |
| @Override |
| public FieldBinding[] availableFields() { |
| if (this._staticallyKnownRoleClass == null) |
| return Binding.NO_FIELDS; |
| return this._staticallyKnownRoleClass.availableFields(); |
| } |
| @Override |
| public int fieldCount() { |
| if (this._staticallyKnownRoleClass == null) |
| return 0; |
| return this._staticallyKnownRoleClass.fieldCount(); |
| } |
| @Override |
| public FieldBinding[] fields() { |
| if (this._staticallyKnownRoleClass == null) |
| return Binding.NO_FIELDS; |
| return this._staticallyKnownRoleClass.fields(); |
| } |
| @Override |
| public FieldBinding getField(char[] fieldName, boolean needResolve) { |
| // normal case first: |
| FieldBinding result = null; |
| if (this._staticallyKnownRoleClass != null) { |
| result = this._staticallyKnownRoleClass.getField(fieldName, needResolve); |
| if (result != null) |
| return result; |
| } |
| // static final fields are found in the interface part: |
| return this._staticallyKnownRoleType.getField(fieldName, needResolve); |
| } |
| |
| // ------------- Find methods in the interface: |
| @Override |
| public MethodBinding[] getMethods(char[] selector) { |
| if (this.methods == null && !isParameterizedType()) |
| return this.type.getMethods(selector); |
| return super.getMethods(selector); |
| } |
| |
| @Override |
| public MethodBinding[] availableMethods() { |
| return this._staticallyKnownRoleType.availableMethods(); |
| } |
| /* well, constructors are actually in the class: */ |
| @Override |
| public MethodBinding getExactConstructor(TypeBinding[] argumentTypes) { |
| if (this._staticallyKnownRoleClass == null) { |
| // try self-healing: |
| this._staticallyKnownRoleClass = this.roleModel.getClassPartBinding(); |
| if (this._staticallyKnownRoleClass == null) |
| throw new InternalCompilerError("Searching for constructor in pure interface role " //$NON-NLS-1$ |
| +new String(this._staticallyKnownRoleType.sourceName())); |
| } |
| return this._staticallyKnownRoleClass.getExactConstructor(argumentTypes); |
| } |
| @Override |
| public MethodBinding getExactMethod(char[] selector, TypeBinding[] argumentTypes, CompilationUnitScope refScope) { |
| return this._staticallyKnownRoleType.getExactMethod(selector, argumentTypes, refScope); |
| } |
| |
| /* Implement OT-specific addition to ReferenceBinding. */ |
| @Override |
| public void addMethod(MethodBinding method) { |
| if (this._staticallyKnownRoleClass != null) { |
| this._staticallyKnownRoleClass.addMethod(method); |
| } |
| MethodBinding ifcPart = new MethodBinding(method, this._staticallyKnownRoleType); |
| ifcPart.modifiers |= ClassFileConstants.AccAbstract; |
| this._staticallyKnownRoleType.addMethod(ifcPart); |
| } |
| |
| |
| @Override |
| public boolean canBeInstantiated() { |
| return this._staticallyKnownRoleType.canBeInstantiated(); |
| } |
| |
| |
| // ===== can't redefine finals! ===== |
| // public final boolean canBeSeenBy(PackageBinding invocationPackage) { |
| // public final boolean canBeSeenBy(ReferenceBinding receiverType, SourceTypeBinding invocationType) { |
| // public final boolean canBeSeenBy(Scope scope) { |
| // public final int depth() { |
| // public final boolean isViewedAsDeprecated() { |
| // public final ReferenceBinding enclosingTypeAt(int relativeDepth) { |
| |
| // these simply operate on this.modifiers: |
| // public final int getAccessFlags() { |
| // public final boolean isAbstract() { |
| // public final boolean isAnonymousType() { |
| // public final boolean isBinaryBinding() { |
| |
| // public final boolean isTeam() { |
| // public final boolean isRole() { |
| // public final boolean isDefault() { |
| // public final boolean isDeprecated() { |
| // public final boolean isFinal() { |
| // public final boolean isInterface() { |
| // public final boolean isLocalType() { |
| // public final boolean isMemberType() { |
| // public final boolean isNestedType() { |
| // public final boolean isPrivate() { |
| // public final boolean isPrivateUsed() { |
| // public final boolean isProtected() { |
| // public final boolean isPublic() { |
| // public final boolean isStatic() { |
| // public final boolean isStrictfp() { |
| |
| @Override |
| public TeamModel getTeamModel() { |
| if (this._teamModel != null) |
| return this._teamModel; |
| if (this._staticallyKnownRoleClass != null) |
| this._teamModel = this._staticallyKnownRoleClass.getTeamModel(); |
| if (this._teamModel != null) |
| return this._teamModel; |
| this._teamModel = this._staticallyKnownRoleType.getTeamModel(); |
| return this._teamModel; |
| |
| } |
| @Override |
| public final boolean isClass() { |
| return this._staticallyKnownRoleClass != null; |
| } |
| @Override |
| public boolean isRole() { |
| return true; |
| } |
| @Override |
| public boolean isSourceRole() { |
| return true; |
| } |
| @Override |
| public boolean isDirectRole() { |
| return true; |
| } |
| |
| @Override |
| public void computeId() { |
| this._staticallyKnownRoleType.computeId(); |
| } |
| |
| @Override |
| public char[] getFileName() { |
| return this._staticallyKnownRoleType.getFileName(); |
| } |
| |
| @Override |
| public ReferenceBinding[] memberTypes() { |
| return this._staticallyKnownRoleType.memberTypes(); |
| } |
| @Override |
| public ReferenceBinding getMemberType(char[] typeName) { |
| ReferenceBinding result = this._staticallyKnownRoleType.getMemberType(typeName); |
| if (result != null) |
| return result; |
| if (this._staticallyKnownRoleClass == null) |
| return null; |
| return this._staticallyKnownRoleClass.getMemberType(typeName); |
| } |
| |
| @Override |
| public SyntheticArgumentBinding[] valueParamSynthArgs() { |
| if (this._staticallyKnownRoleClass != null) |
| return this._staticallyKnownRoleClass.valueParamSynthArgs(); |
| return NO_SYNTH_ARGUMENTS; |
| } |
| |
| @Override |
| public PackageBinding getPackage() { |
| return this._staticallyKnownRoleType.getPackage(); |
| } |
| |
| @Override |
| public ReferenceBinding superclass() { |
| // 0. a stored superclass: |
| if (this._superClass != null) |
| return this._superClass; |
| |
| // 1. a direct super-role is found as the superclass of this role's class: |
| if (this._staticallyKnownRoleClass != null) { |
| ReferenceBinding superClass = this._staticallyKnownRoleClass.superclass(); |
| if ((superClass != null) && (superClass.isDirectRole())) |
| { |
| // 1.a: a confined type "as-is": don't instantiate/strengthen |
| if ( OTNameUtils.isPredefinedConfined(superClass.compoundName) |
| || CharOperation.equals(IOTConstants.OTCONFINED, superClass.sourceName)) |
| return this._superClass = superClass; |
| |
| // 1.b: instantiate superclass to the current team anchor: |
| superClass = (ReferenceBinding)this._teamAnchor.getRoleTypeBinding( |
| superClass.roleModel.getInterfacePartBinding(), |
| 0); // dimensions=0 => the above cast is safe. |
| |
| // 1.c: strengthen weakened type: |
| if (superClass instanceof WeakenedTypeBinding) { |
| // TODO(SH): comment: how come this is possible? why is depth relevant? |
| if (superClass.depth() >= depth()) { |
| return null; // FIXME(SH): find a witness or delete this branch |
| } else { |
| // using "extends" accross team border: |
| superClass = ((WeakenedTypeBinding)superClass).type; |
| } |
| } |
| return this._superClass = superClass; |
| } |
| } |
| |
| // 2. non-role superclass as the superclass of the role's interfaces: |
| if (this._staticallyKnownRoleType != null) |
| return this._superClass = this._staticallyKnownRoleType.superclass(); |
| |
| return null; |
| } |
| |
| @Override |
| public boolean isHierarchyConnected() { |
| return this.type.isHierarchyConnected(); |
| } |
| |
| @Override |
| public ReferenceBinding[] syntheticEnclosingInstanceTypes() { |
| return this._staticallyKnownRoleType.syntheticEnclosingInstanceTypes(); |
| } |
| @Override |
| public SyntheticArgumentBinding[] syntheticOuterLocalVariables() { |
| return this._staticallyKnownRoleType.syntheticOuterLocalVariables(); |
| } |
| |
| |
| /* To check whether a role type implements a given role interface, |
| * we need to take our role CLASS and the other side's INTERFACE, |
| * extracting this from a RoleTypeBinding if needed. |
| */ |
| @Override |
| public boolean implementsInterface(ReferenceBinding anInterface, boolean searchHierarchy) { |
| if (anInterface instanceof RoleTypeBinding) |
| { |
| // TODO (SH): check Team equivalence |
| anInterface = ((RoleTypeBinding)anInterface)._staticallyKnownRoleType; |
| } |
| return this._staticallyKnownRoleType.implementsInterface(anInterface, searchHierarchy); |
| } |
| @Override |
| public boolean implementsMethod(MethodBinding method) { |
| return ((AbstractOTReferenceBinding)this._staticallyKnownRoleType).implementsMethod(method); |
| } |
| |
| /* |
| * For superclass test, take the role CLASSES on both sides. |
| */ |
| @Override |
| public boolean isSuperclassOf(ReferenceBinding otherType) { |
| if (otherType instanceof RoleTypeBinding) |
| { |
| // TODO (SH): check Team equivalence? |
| otherType = ((RoleTypeBinding)otherType)._staticallyKnownRoleType; |
| } |
| return this._staticallyKnownRoleType.isSuperclassOf(otherType); |
| } |
| |
| /* mainly forward: */ |
| @Override |
| public TypeBinding findSuperTypeOriginatingFrom(TypeBinding otherType) { |
| if (otherType instanceof ReferenceBinding) { |
| ReferenceBinding otherRef = (ReferenceBinding)otherType; |
| // interface part first: |
| ReferenceBinding ifcBinding = this.transferTypeArguments(this._staticallyKnownRoleType); |
| TypeBinding superRef = ifcBinding.findSuperTypeOriginatingFrom(otherRef.getRealType()); |
| // for regular superclass also search class part: |
| if (superRef == null && this._staticallyKnownRoleClass != null) { |
| ReferenceBinding classBinding = this.transferTypeArguments(this._staticallyKnownRoleClass); |
| superRef = classBinding.findSuperTypeOriginatingFrom(otherRef.getRealClass()); |
| if (superRef != null && (Config.getCastRequired() == null)) { |
| // compatible via role class only with a cast, |
| // use the actually expected type to mark this. |
| Config.setCastRequired((ReferenceBinding)otherType); |
| } |
| } |
| if (superRef != null) { |
| if (superRef.isRole()) |
| return this._teamAnchor.getRoleTypeBinding((ReferenceBinding) superRef, 0); |
| else |
| return superRef; |
| } |
| } |
| return null; |
| } |
| |
| @Override |
| public ReferenceBinding findSuperTypeOriginatingFrom(int wellKnownOriginalID, boolean originalIsClass) { |
| // interface part first: |
| ReferenceBinding ifcBinding = this.transferTypeArguments(this._staticallyKnownRoleType); |
| ReferenceBinding superRef = ifcBinding.findSuperTypeOriginatingFrom(wellKnownOriginalID, originalIsClass); |
| // for regular superclass also search class part: |
| if (superRef == null && this._staticallyKnownRoleClass != null) { |
| ReferenceBinding classBinding = this.transferTypeArguments(this._staticallyKnownRoleClass); |
| superRef = classBinding.findSuperTypeOriginatingFrom(wellKnownOriginalID, originalIsClass); |
| } |
| if (superRef != null) { |
| if (superRef.isRole()) |
| return (ReferenceBinding) this._teamAnchor.getRoleTypeBinding(superRef, 0); |
| else |
| return superRef; |
| } |
| return null; |
| } |
| |
| /** Forward to either part: */ |
| @Override |
| public boolean isProvablyDistinct(TypeBinding otherType) { |
| if (this._staticallyKnownRoleType != null && !this._staticallyKnownRoleType.isProvablyDistinct(otherType)) |
| return false; |
| if (this._staticallyKnownRoleClass != null && !this._staticallyKnownRoleClass.isProvablyDistinct(otherType)) |
| return false; |
| return true; |
| } |
| |
| @Override |
| public void setIsBoundBase(ReferenceBinding roleType) { |
| if (this._staticallyKnownRoleClass != null) |
| this._staticallyKnownRoleClass.setIsBoundBase(roleType); |
| if (this._staticallyKnownRoleType != null) |
| this._staticallyKnownRoleType.setIsBoundBase(roleType); |
| } |
| /** |
| * MAIN ENTRY FOR TYPE CHECKING. |
| * |
| * Answer true if the receiver type can be assigned to the argument type (right). |
| * Compare team-anchor and role-type. |
| * Note, that the type of _teamAnchor and _staticallyKnownTeam may differ. |
| * The former is relevant for type-checking. the latter serves mainly for code generation |
| * and for determining overriding. |
| */ |
| @Override |
| public boolean isCompatibleWith(TypeBinding right, /*@Nullable*/ Scope captureScope) { |
| if (TypeBinding.equalsEquals(right, this)) |
| return true; |
| if (!(right instanceof ReferenceBinding)) |
| return false; |
| if (right.isRawType() && this.erasure().isCompatibleWith(right, captureScope)) |
| return true; |
| |
| ReferenceBinding referenceBinding = (ReferenceBinding) right; |
| if (referenceBinding.isRoleType()) |
| { |
| RoleTypeBinding rightRole = getRoleTypeBinding(referenceBinding); |
| ReferenceBinding rightTeam = referenceBinding.enclosingType(); |
| |
| // compare teams: |
| if (!this._teamAnchor.hasSameBestNameAs(rightRole._teamAnchor)) |
| { // different anchors, not both tthis: not compatible! |
| return isCompatibleViaLowering(rightRole); |
| } |
| |
| // compensate weakened signature: |
| if (TypeBinding.notEquals(rightRole._staticallyKnownTeam, this._staticallyKnownTeam)) { |
| try { |
| if (TeamModel.areTypesCompatible( |
| rightTeam, |
| this._staticallyKnownTeam)) |
| { |
| ReferenceBinding leftStrengthened = this._teamAnchor.getMemberTypeOfType(internalName()); |
| if (TypeBinding.notEquals(leftStrengthened, this)) |
| return leftStrengthened.isCompatibleWith(right, captureScope); |
| } |
| else if (TeamModel.areTypesCompatible( |
| this._staticallyKnownTeam, |
| rightTeam)) |
| { |
| rightRole = (RoleTypeBinding)this._teamAnchor.getMemberTypeOfType(rightRole.internalName()); |
| |
| } else { |
| return false; |
| } |
| } finally { |
| Config.setCastRequired(null); // reset |
| } |
| } |
| |
| // check the role types: |
| if (this._staticallyKnownRoleType. |
| isCompatibleWith(rightRole._staticallyKnownRoleType, captureScope)) |
| return true; |
| } |
| if ( referenceBinding.isInterface() |
| && implementsInterface(referenceBinding, true)) |
| return true; |
| |
| if ( this._staticallyKnownRoleClass == null |
| && this._staticallyKnownRoleType.isCompatibleWith(referenceBinding, false, captureScope)) |
| { |
| checkAmbiguousObjectLower(referenceBinding); |
| return true; // this case is wittnessed by: "this=RoleIfc", right="Object"; other examples? |
| } |
| |
| // do we need the class part instead of the interface part? |
| if ( (this._staticallyKnownRoleClass != null) |
| && this._staticallyKnownRoleClass.isStrictlyCompatibleWith(referenceBinding, captureScope) |
| && !TeamModel.isTeamContainingRole(this._staticallyKnownTeam, referenceBinding)) |
| { |
| // Cast from a role to its non-role superclass |
| // (Interfaces do not reflect this compatibility, thus we need to help here). |
| Config.setCastRequired(referenceBinding); |
| checkAmbiguousObjectLower(referenceBinding); |
| return true; |
| } |
| |
| // after everything else has failed try lowering: |
| return isCompatibleViaLowering(referenceBinding); |
| } |
| |
| // check if upcast to java.lang.Object bypasses a potentially desired lowering: |
| private void checkAmbiguousObjectLower(ReferenceBinding otherType) { |
| if ( otherType.id == TypeIds.T_JavaLangObject |
| && baseclass() != null) |
| Config.setLoweringPossible(true); |
| } |
| |
| @Override |
| public boolean isCompatibleViaLowering(ReferenceBinding otherType) { |
| // <B base R>? |
| if (otherType instanceof TypeVariableBinding) { |
| ReferenceBinding otherRole = ((TypeVariableBinding)otherType).roletype; |
| if (this.isCompatibleWith(otherRole)) { |
| Config.setLoweringRequired(true); |
| return true; |
| } |
| } |
| ReferenceBinding baseType = null; |
| // needed for base-class lookup: |
| Dependencies.ensureRoleState(this.roleModel, ITranslationStates.STATE_LENV_DONE_FIELDS_AND_METHODS); |
| |
| RoleModel strengthened = this._teamAnchor.getStrengthenedRole(this); |
| if ((strengthened.getInterfacePartBinding().modifiers & AccSynthIfc) == ClassFileConstants.AccInterface) |
| baseType = strengthened.getInterfacePartBinding().baseclass(); |
| else |
| { |
| if(strengthened.getClassPartBinding() != null) |
| { |
| baseType = strengthened.getClassPartBinding().baseclass(); |
| } |
| } |
| if ( (baseType != null) |
| && baseType.isCompatibleWith(otherType)) |
| { |
| Config.setLoweringRequired(true); |
| return true; |
| } |
| return false; |
| } |
| |
| public boolean isStrictlyCompatibleWith(TypeBinding right) { |
| return isCompatibleWith(right); |
| } |
| |
| // see DependentTypeBinding.isEquivalentTo() |
| @Override |
| public boolean isEquivalentTo(TypeBinding otherType) { |
| if (! (otherType instanceof RoleTypeBinding)) |
| return this._staticallyKnownRoleType.isEquivalentTo(otherType); // don't use DTB._type |
| RoleTypeBinding otherDep = getRoleTypeBinding(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()); |
| } |
| |
| // =========== VARIOUS NAMES: ============= |
| |
| @Override |
| public char[] qualifiedSourceName() { |
| return this._staticallyKnownRoleType.qualifiedSourceName(); |
| } |
| |
| @Override |
| public char[] optimalName() /*var.RoleType or java.lang.Object */ |
| { |
| if (this._teamAnchor instanceof TThisBinding) |
| return super.readableName(); |
| |
| char[] anchorName = new char[0]; |
| |
| ITeamAnchor[] bestNamePath = this._teamAnchor.getBestNamePath(); |
| for (int i = 0; i < bestNamePath.length; i++) { |
| char[] readableName = bestNamePath[i].readableName(); |
| if (CharOperation.equals(readableName, IOTConstants._OT_BASE)) |
| readableName = "base".toCharArray(); //$NON-NLS-1$ |
| anchorName = CharOperation.concatWith( |
| new char[][]{anchorName, readableName}, |
| '.'); |
| } |
| |
| return CharOperation.concat( |
| anchorName, |
| strippedName(this._staticallyKnownRoleType), |
| '.'); |
| } |
| |
| private char[] strippedName(ReferenceBinding role) { |
| if (OTNameUtils.isTSuperMarkerInterface(role.sourceName)) |
| { |
| return "<tsuper-mark>".toCharArray(); //$NON-NLS-1$ |
| } |
| else if (RoleSplitter.isClassPartName(role.sourceName)) |
| { |
| return RoleSplitter.getInterfacePartName(role.sourceName); |
| } |
| return role.sourceName; |
| } |
| |
| // don't override: need original version in classfile: |
| // public char[] signature() /* Ljava/lang/Object; */ { |
| @Override |
| public char[] sourceName() { |
| return this._staticallyKnownRoleType.sourceName(); |
| } |
| |
| // Note: this uses the original name (respects signature weakening): |
| @Override |
| public char[] constantPoolName() /* java/lang/Object */ { |
| return this._declaredRoleType.constantPoolName(); |
| } |
| @Override |
| public char[] attributeName() { |
| if (this._staticallyKnownRoleClass != null) |
| return this._staticallyKnownRoleClass.attributeName(); |
| return super.attributeName(); |
| } |
| @Override |
| public String toString() { |
| StringBuilder sb = new StringBuilder(); |
| sb.append(annotatedDebugName()); |
| sb.append('<').append('@'); |
| ITeamAnchor[] bestNamePath = this._teamAnchor.getBestNamePath(false); |
| for (int i = 0; i < bestNamePath.length; i++) { |
| if (i>0) |
| sb.append('.'); |
| sb.append(bestNamePath[i].readableName()); |
| } |
| if (!(this._teamAnchor instanceof TThisBinding)) |
| sb.append('[').append(this._staticallyKnownTeam.sourceName()).append(']'); |
| sb.append('>'); |
| return sb.toString(); |
| } |
| } |