| /********************************************************************** |
| * This file is part of "Object Teams Development Tooling"-Software |
| * |
| * Copyright 2003, 2006 Fraunhofer Gesellschaft, Munich, Germany, |
| * for its Fraunhofer Institute for Computer Architecture and Software |
| * Technology (FIRST), Berlin, Germany and Technical University Berlin, |
| * Germany. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * $Id: TypeAnalyzer.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.util; |
| |
| |
| import org.eclipse.jdt.annotation.Nullable; |
| import org.eclipse.jdt.core.compiler.CharOperation; |
| import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; |
| import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; |
| import org.eclipse.jdt.internal.compiler.ast.Expression; |
| import org.eclipse.jdt.internal.compiler.ast.ExpressionContext; |
| import org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference; |
| import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference; |
| import org.eclipse.jdt.internal.compiler.ast.SingleNameReference; |
| import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference; |
| import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; |
| import org.eclipse.jdt.internal.compiler.ast.TypeReference; |
| import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.BaseTypeBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.Binding; |
| import org.eclipse.jdt.internal.compiler.lookup.FieldBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.InferenceContext18; |
| import org.eclipse.jdt.internal.compiler.lookup.InvocationSite; |
| import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment; |
| import org.eclipse.jdt.internal.compiler.lookup.MemberTypeBinding; |
| 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.ProblemFieldBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.ProblemReasons; |
| import org.eclipse.jdt.internal.compiler.lookup.ProblemReferenceBinding; |
| 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.TypeBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; |
| import org.eclipse.jdt.internal.compiler.lookup.TypeIds; |
| import org.eclipse.jdt.internal.compiler.lookup.UnresolvedReferenceBinding; |
| import org.eclipse.objectteams.otdt.core.compiler.IOTConstants; |
| import org.eclipse.objectteams.otdt.core.exceptions.InternalCompilerError; |
| 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.Dependencies; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.DependentTypeBinding; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.ITeamAnchor; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.RoleTypeBinding; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.model.TeamModel; |
| |
| /** |
| * Utility class for analyzing, and generating types. |
| * Handles bindings, names and references. |
| * |
| * @author stephan <i>stephan@cs.tu-berlin.de</i> |
| * @version $Id: TypeAnalyzer.java 23416 2010-02-03 19:59:31Z stephan $ |
| */ |
| public class TypeAnalyzer { |
| |
| public static final int EXACT_MATCH_ONLY = 1; |
| public static final int NEEDING_ADJUSTMENT_ONLY = 2; |
| public static final int ANY_MATCH = 3; |
| |
| |
| /** |
| * Given a method in a super Team, check wether the method declaration |
| * from the sub Team is "the same method", which means, that role type |
| * adjustments are taken into consideration. |
| * These kinds of comparison are supported: |
| * <dt> |
| * <dt>EXACT_MATCH_ONLY |
| * <dd> these mathods are the same even in plain Java |
| * <dt>NEEDING_ADJUSTMENT_ONLY |
| * <dd> only answer "true" if any parameter needs weakening |
| * <dt>ANY_MATCH |
| * <dd> answer "true" if either kind of match is encountered. |
| * </dt> |
| * @param superTeam |
| * @param superMeth |
| * @param subTeam |
| * @param subMeth |
| * @param matchKind one of the above constants. |
| * @return the answer |
| */ |
| public static boolean isEqualMethodSignature( |
| ReferenceBinding superTeam, |
| MethodBinding superMeth, |
| ReferenceBinding subTeam, |
| MethodBinding subMeth, |
| int matchKind) |
| { |
| |
| if(subMeth.parameters.length != superMeth.parameters.length) |
| return false; |
| TypeBinding[] params = subMeth.parameters; |
| boolean needAdjustment = false; |
| for (int i = 0; i < params.length; i++) { |
| TypeBinding param = params[i]; |
| TypeBinding superTypeBind = superMeth.parameters[i]; |
| if (TypeBinding.equalsEquals(param, superTypeBind)) continue; |
| if (areTypesMatchable(param, subTeam, superTypeBind, superTeam, matchKind)) |
| { |
| needAdjustment = true; |
| } else { |
| return false; |
| } |
| |
| } |
| if (matchKind == NEEDING_ADJUSTMENT_ONLY) |
| return needAdjustment; |
| return true; |
| } |
| |
| /** |
| * Answer, whether two reference bindings are views of the same role |
| * possibly seen from different teams. |
| */ |
| public static boolean isSameRole(ReferenceBinding r1, ReferenceBinding r2) |
| { |
| if (TypeBinding.equalsEquals(r1, r2)) |
| return true; |
| if (r1 == null || r2 == null) |
| return false; |
| if (! (r1.isRole() && r2.isRole())) |
| return false; |
| ReferenceBinding t1 = r1.enclosingType(); |
| ReferenceBinding t2 = r2.enclosingType(); |
| if (TypeBinding.equalsEquals(t1, t2) || t1 == null || t2 == null) |
| return false; // not two different teams: no other chance.. |
| if (! (t1.isCompatibleWith(t2) || t2.isCompatibleWith(t1))) |
| return false; // not sub/super teams. |
| return CharOperation.equals(r1.internalName(), r2.internalName()); |
| } |
| |
| /** does role refine the extends clause of its tsuper role? */ |
| public static boolean refinesExtends (ReferenceBinding role, ReferenceBinding tsuperRole) |
| { |
| return !isSameRole( |
| role.superclass(), |
| tsuperRole.superclass()); |
| } |
| /** |
| * Answer, whether two type are the same, when regarding a class and an |
| * interface part of a role as identical. |
| */ |
| public static boolean isSameType(ReferenceBinding site, TypeBinding t1, TypeBinding t2) |
| { |
| if (TypeBinding.equalsEquals(t1, t2)) return true; |
| if (t1 == null || t2 == null) |
| return false; |
| if (t1.isArrayType()) { |
| if ( !t2.isArrayType() |
| || (t1.dimensions() != t2.dimensions())) |
| return false; |
| t1 = t1.leafComponentType(); |
| t2 = t2.leafComponentType(); |
| } |
| else |
| if (t2.isArrayType()) |
| { |
| return false; |
| } |
| |
| if (t1.isBaseType()) |
| return TypeBinding.equalsEquals(t1, t2); |
| if (t2.isBaseType()) |
| return false; |
| ReferenceBinding r1 = (ReferenceBinding)t1; |
| ReferenceBinding r2 = (ReferenceBinding)t2; |
| if (r1.isDirectRole() && r2.isDirectRole()) { |
| r2 = (ReferenceBinding)TeamModel.strengthenRoleType(site, r2); |
| return TypeBinding.equalsEquals(r1.roleModel.getInterfacePartBinding(), |
| r2.roleModel.getInterfacePartBinding()); |
| } |
| return false; |
| } |
| /** |
| * Are type compatible possibly performing role type adjustment? |
| * @param currentType |
| * @param subTeam |
| * @param tsuperType |
| * @param superTeam |
| * @param matchKind |
| * @return the answer |
| */ |
| public static boolean areTypesMatchable( |
| TypeBinding currentType, |
| ReferenceBinding subTeam, |
| TypeBinding tsuperType, |
| ReferenceBinding superTeam, |
| int matchKind) |
| { |
| // be very defensive: |
| if (currentType instanceof ProblemReferenceBinding) { |
| TypeBinding closestMatch = ((ProblemReferenceBinding)currentType).closestMatch(); |
| if (closestMatch == null) |
| return CharOperation.equals(currentType.sourceName(), tsuperType.sourceName()); |
| currentType = closestMatch; |
| } |
| if (tsuperType instanceof ProblemReferenceBinding) { |
| TypeBinding closestMatch = ((ProblemReferenceBinding)tsuperType).closestMatch(); |
| if (closestMatch == null) |
| return CharOperation.equals(currentType.sourceName(), tsuperType.sourceName()); |
| tsuperType = closestMatch; |
| } |
| |
| // if it's array-types, compare dimension and extract leafComponentType |
| if ((currentType instanceof ArrayBinding) || (tsuperType instanceof ArrayBinding)) |
| { |
| if (!(tsuperType instanceof ArrayBinding) || |
| !(currentType instanceof ArrayBinding)) |
| return false; |
| ArrayBinding currentArray = (ArrayBinding)currentType; |
| ArrayBinding superArray = (ArrayBinding)tsuperType; |
| if (currentArray.dimensions() != superArray.dimensions()) |
| return false; |
| currentType = currentArray.leafComponentType(); |
| tsuperType = superArray.leafComponentType(); |
| } |
| |
| // guaranteed to be scalar now |
| if (currentType instanceof ReferenceBinding) |
| { |
| if (currentType instanceof DependentTypeBinding) |
| currentType = ((DependentTypeBinding)currentType).getRealType(); |
| if (! (tsuperType instanceof ReferenceBinding)) |
| return false; |
| if (tsuperType instanceof DependentTypeBinding) |
| tsuperType = ((DependentTypeBinding)tsuperType).getRealType(); |
| if (currentType.isParameterizedType() || tsuperType.isParameterizedType()) { |
| // at least one type parameterized: get erasure(s) |
| if (currentType.isParameterizedType()) { |
| if (tsuperType.isParameterizedType()) { |
| // both parameterized: check parameters: |
| ParameterizedTypeBinding currentParameterized = ((ParameterizedTypeBinding)currentType); |
| ParameterizedTypeBinding tsuperParameterized = ((ParameterizedTypeBinding)tsuperType); |
| if (!CharOperation.equals(currentParameterized.genericTypeSignature, |
| tsuperParameterized.genericTypeSignature)) |
| return false; |
| } else if (!tsuperType.isRawType()) { |
| return false; // mismatch generic vs. non-generic |
| } |
| |
| } else if (!currentType.isRawType()) { |
| return false; // mismatch non-generic vs. generic |
| } |
| currentType = currentType.erasure(); |
| tsuperType = tsuperType.erasure(); |
| } |
| if (currentType.isTypeVariable() && tsuperType.isTypeVariable()) |
| return TypeBinding.equalsEquals(currentType, tsuperType); |
| |
| char[][] tname1 = compoundNameOfReferenceType((ReferenceBinding)tsuperType, true, true); |
| char[][] tname2 = compoundNameOfReferenceType((ReferenceBinding)currentType, true, true); |
| if (CharOperation.equals(tname1, tname2)) { |
| if (TypeBinding.notEquals(tsuperType, currentType) && tsuperType.isValidBinding()) // don't complain about different missing types |
| throw new InternalCompilerError("different bindings for the same type??"+currentType+':'+tsuperType); //$NON-NLS-1$ |
| return true; |
| } |
| if (matchKind == EXACT_MATCH_ONLY) |
| { |
| return false; |
| } else { |
| tname1 = splitTypeNameRelativeToTeam((ReferenceBinding)tsuperType, superTeam); |
| tname2 = splitTypeNameRelativeToTeam((ReferenceBinding)currentType, subTeam); |
| if (!CharOperation.equals(tname1, tname2)) |
| return false; |
| } |
| } else if (currentType instanceof BaseTypeBinding) { |
| if (TypeBinding.notEquals(currentType, tsuperType)) |
| return false; |
| } else { |
| throw new InternalCompilerError("matching of unexpected type kind: "+currentType); //$NON-NLS-1$ |
| } |
| return true; |
| } |
| |
| |
| /** |
| * Extract a compound type name relativ to a given Team. |
| * This means, if tb is a (transitiv) member type of team, |
| * cut off the Team and return only remaining part. |
| * If tb is not a (transitiv) member of team, return the |
| * full compound name of outer/inner classes but not containing |
| * the package. |
| * |
| * For features inherited from an indirect super team allow |
| * the team prefix to relate to that super team. |
| * |
| * @param tb the type to represent |
| * @param teamBinding the Team |
| * @return non-null array of at least one component |
| */ |
| public static char[][] splitTypeNameRelativeToTeam(ReferenceBinding tb, TypeBinding teamBinding) { |
| char [][] qname = new char[][] {tb.internalName()}; |
| // move out until we find the source team: |
| while (tb instanceof MemberTypeBinding) { |
| tb = ((MemberTypeBinding)tb).enclosingType; |
| if (tb.isTeam()) { |
| if (TypeBinding.equalsEquals(tb, teamBinding)) |
| return qname; |
| else if (teamBinding.isCompatibleWith(tb)) |
| return qname; |
| } else { |
| // prepend one component to qname: |
| char[][] qn2 = new char[qname.length+1][]; |
| System.arraycopy(qname, 0, qn2, 1, qname.length); |
| qn2[0] = tb.internalName(); |
| qname = qn2; |
| } |
| } |
| |
| return qname; |
| } |
| |
| /** |
| * Split the qualified name of a member type into a compound name. |
| * (the field compoundName merges Outer$Inner into one element |
| * of the compound, which is not what we want here). |
| * @param tb |
| * @param includePackage should the package name(s) be included? |
| * @param createTeamAnchor should a possible team anchor be included in the compound name? |
| * TODO(SH) saying yes here causes as to create old syntax AST. |
| * @return non-null array of at least one component |
| */ |
| public static char[][] compoundNameOfReferenceType( |
| ReferenceBinding tb, |
| boolean includePackage, |
| boolean createTeamAnchor) |
| { |
| if (tb instanceof ProblemReferenceBinding) { |
| ReferenceBinding closestMatch = (ReferenceBinding) tb.closestMatch(); |
| if (closestMatch != null) |
| tb = closestMatch; |
| } |
| if (!tb.isValidBinding()) { // no further processing possible |
| if (includePackage) |
| return tb.compoundName; |
| int l = tb.compoundName.length; |
| return new char[][]{tb.compoundName[l-1]}; |
| } |
| if (tb instanceof UnresolvedReferenceBinding) { |
| LookupEnvironment env = Config.getLookupEnvironment(); |
| if (env == null) |
| throw new InternalCompilerError("No lookup environment configured"); //$NON-NLS-1$ |
| tb = ((UnresolvedReferenceBinding)tb).resolve(env, false); |
| } |
| if ( createTeamAnchor |
| && tb instanceof DependentTypeBinding |
| && ((DependentTypeBinding)tb).hasExplicitAnchor()) |
| { |
| DependentTypeBinding roleTypeBinding = (DependentTypeBinding)tb; |
| |
| // for role types the prefix is a variable not a type: |
| ITeamAnchor[] path = roleTypeBinding._teamAnchor.getBestNamePath(); |
| |
| // If anchor is a field, prepend a team anchor with "Outer.this" |
| // for the type containing the anchor field. |
| char[] declaringClass = null; |
| int prefixLen = 0; |
| if (roleTypeBinding._teamAnchor instanceof FieldBinding) { |
| declaringClass = ((FieldBinding)(roleTypeBinding)._teamAnchor).declaringClass.internalName(); |
| prefixLen = 2; |
| } |
| char[][] names = new char[path.length+1+prefixLen][]; |
| if (declaringClass != null) { |
| // do prepend |
| names[0] = declaringClass; |
| names[1] = "this".toCharArray(); //$NON-NLS-1$ |
| } |
| for (int i = 0; i < path.length; i++) { |
| names[i+prefixLen] = path[i].internalName(); |
| } |
| names[path.length+prefixLen] = tb.internalName(); |
| |
| return names; |
| } |
| char[][] packName = includePackage && (tb.getPackage() != null) ? |
| tb.getPackage().compoundName : |
| new char[0][]; |
| char[][] outerName = (tb.enclosingType() != null) ? |
| compoundNameOfReferenceType(tb.enclosingType(), false, createTeamAnchor) : |
| new char[0][]; |
| char[][] result = new char[packName.length+outerName.length+1][]; |
| System.arraycopy(packName, 0, result, 0, packName.length); |
| System.arraycopy(outerName, 0, result, packName.length, outerName.length); |
| result[result.length-1] = tb.internalName(); |
| return result; |
| } |
| |
| /** |
| * Try to interpret a type name as a local type of a role. |
| * Take the constantPoolName and chop off the team name which prefixes this name. |
| * |
| * @param teamBinding |
| * @param compoundName |
| * @return non-null char-array |
| */ |
| public static char[] constantPoolNameRelativeToTeam(ReferenceBinding teamBinding, char[]compoundName) { |
| char[] teamName = teamBinding.constantPoolName(); |
| if (!CharOperation.prefixEquals(teamName, compoundName)) |
| return compoundName; |
| assert (compoundName[teamName.length] == '$'); |
| return CharOperation.subarray(compoundName, teamName.length+1, -1); |
| } |
| |
| /** |
| * Compare two types trying to interpret type names as local types of roles, where |
| * the constantPoolName must be investigated but the team name prefix chopped off. |
| * |
| * @param teamBinding enclosing team of the first type |
| * @param role the first type itself |
| * @param typeName relative name of the second type (ie., team prefix is already copped off). |
| * @return the answer |
| */ |
| public static boolean equalRoleLocal(ReferenceBinding teamBinding, ReferenceBinding role, char[] typeName) |
| { |
| // TODO (SH) during CopyInheritanc constantPoolName is still null. |
| // but do we need to map names of local types in accessor signatures?? |
| if (role.constantPoolName() == null) |
| return false; |
| char[] relativeName = constantPoolNameRelativeToTeam(teamBinding, role.constantPoolName()); |
| return CharOperation.equals(typeName, relativeName); |
| } |
| /** |
| * Given a reference (read from source code) and a binding (read from |
| * a resolved super-role) create a weakened type reference, i.e., if |
| * the type is a role-type, create a new reference from the binding. |
| * Precondition: both types are identical except for implicit inheritance. |
| * @param origRef This reference may or may not need to be changed. |
| * In any case use its source positions. |
| * @param binding this specifies the type we need to refer to. |
| * @return either origRef or a fresh reference |
| */ |
| public static TypeReference weakenTypeReferenceFromBinding( |
| MethodScope scope, |
| TypeReference origRef, |
| TypeBinding origBinding, |
| TypeBinding binding) |
| { |
| if (!( (binding instanceof ReferenceBinding) |
| || (binding instanceof ArrayBinding))) |
| return origRef; |
| binding = binding.erasure(); |
| origBinding = origBinding.erasure(); |
| if (binding instanceof RoleTypeBinding) |
| if (TypeBinding.equalsEquals(origBinding, ((RoleTypeBinding)binding).getRealType())) |
| return origRef; |
| if (TypeBinding.equalsEquals(origBinding, binding)) |
| return origRef; |
| AstGenerator gen = new AstGenerator(origRef.sourceStart, origRef.sourceEnd); |
| return gen.typeReference(binding); |
| } |
| |
| /** |
| * Find a method in a super role |
| * @param subTeam the Team containing subMethod |
| * @param subMethod a method to match |
| * @param superRole where to look |
| * @param superTeam enclosing Team of superRole |
| * @return a method or null |
| */ |
| public static MethodBinding findMethodInSuperRole( |
| ReferenceBinding subTeam, MethodBinding subMethod, |
| ReferenceBinding superRole, ReferenceBinding superTeam, |
| int matchKind) |
| { |
| MethodBinding[] superMethods =superRole.methods(); |
| for (int i=0; i<superMethods.length; i++) |
| { |
| if (isEqualMethodSignature(superTeam, superMethods[i], subTeam, subMethod, matchKind)) |
| return superMethods[i]; |
| } |
| return null; |
| } |
| |
| public static boolean isOrgObjectteamsTeam(ReferenceBinding type) { |
| if (type == null) |
| return false; |
| return CharOperation.equals(type.compoundName, IOTConstants.ORG_OBJECTTEAMS_TEAM); |
| } |
| |
| public static boolean isOrgObjectteamsTeam(CompilationUnitDeclaration unit) { |
| if ( unit != null |
| && unit.currentPackage != null |
| && unit.types != null |
| && unit.types.length > 0) { |
| return CharOperation.equals(IOTConstants.ORG_OBJECTTEAMS, unit.currentPackage.tokens) |
| && CharOperation.equals(IOTConstants.TEAM, unit.types[0].name); |
| } |
| |
| return false; |
| } |
| |
| public static boolean isVariableRef (Expression e) { |
| return (e.bits & Binding.VARIABLE) != 0; |
| } |
| |
| /** |
| * Find a fied in a type searching superclasses and enclosing classes. |
| * Note, that this method indeed finds private fields even via a super-class of type. |
| * |
| * @param type where to look |
| * @param token name of the field to look for |
| * @param isStaticScope is the field reference within a static scope? |
| * @param allowOuter is it legal to find the field in an enclosing type? |
| * @param requiredState when navigating to supertypes, is a certain state required for those? |
| * (this does not apply to `type' itself and its enclosing). |
| * @return a field or null |
| */ |
| public static FieldBinding findField( |
| ReferenceBinding type, |
| char[] token, |
| boolean isStaticScope, |
| boolean allowOuter, |
| int requiredState) |
| { |
| while (type != null) { // loop inner->outer |
| ReferenceBinding currentType = type; |
| while (currentType != null) { // loop sub->super |
| FieldBinding foundVar = currentType.getField(token, /*resolve*/true); |
| if (foundVar != null) { |
| if (isStaticScope && ! foundVar.isStatic()) |
| return new ProblemFieldBinding(foundVar, currentType, token, ProblemReasons.NonStaticReferenceInStaticContext); |
| return foundVar; |
| } |
| currentType = currentType.superclass(); |
| if (currentType != null && requiredState != -1) |
| Dependencies.ensureBindingState(currentType, requiredState); |
| } |
| if (!allowOuter) |
| return null; |
| isStaticScope = type.isStatic(); |
| type = type.enclosingType(); |
| } |
| return null; |
| } |
| |
| public static FieldBinding findField(ReferenceBinding type, |
| char[] token, |
| boolean isStaticScope, |
| boolean allowOuter) |
| { |
| return findField(type, token, isStaticScope, allowOuter, -1); |
| } |
| /** |
| * Find a method in type or one of its super types. |
| * @param scope where the method shall be invoked from |
| * @param type where to search |
| * @param selector name of the method |
| * @param params params |
| */ |
| public static MethodBinding findMethod( |
| Scope scope, |
| ReferenceBinding type, |
| char[] selector, |
| TypeBinding[] params) |
| { |
| return findMethod(scope, type, selector, params, /*decapsulationAllowed*/false, null); |
| } |
| /** |
| * Find a method in type or one of its super types. |
| * @param scope where the method shall be invoked from |
| * @param type where to search |
| * @param selector name of the method |
| * @param params params |
| * @param decapsulationAllowed whether or not invisible methods should be found, too |
| * @param site invocation site if available |
| */ |
| public static @Nullable MethodBinding findMethod( |
| final Scope scope, |
| ReferenceBinding type, |
| char[] selector, |
| TypeBinding[] params, |
| boolean decapsulationAllowed, |
| @Nullable InvocationSite site) |
| { |
| if (type == null || scope == null) |
| return null; |
| if (site == null) { |
| final Expression[] fakeArguments = new Expression[params.length]; |
| for (int i = 0; i < params.length; i++) { |
| fakeArguments[i] = new SingleNameReference(("fakeArg"+i).toCharArray(), 0L); //$NON-NLS-1$ |
| fakeArguments[i].resolvedType = params[i]; |
| } |
| site = new InvocationSite() { |
| @Override public int sourceStart() { return 0; } |
| @Override public int sourceEnd() { return 0; } |
| @Override public void setFieldIndex(int depth) { /* nop */ } |
| @Override public void setDepth(int depth) { /* nop */ } |
| |
| @Override public void setActualReceiverType(ReferenceBinding receiverType) { /* nop */ } |
| @Override public boolean receiverIsImplicitThis() { return false; } |
| @Override public boolean isTypeAccess() { return false; } |
| @Override public boolean isSuperAccess() { return false; } |
| @Override public boolean isQualifiedSuper() { return false; } |
| @Override public boolean checkingPotentialCompatibility() { return false; } |
| @Override public void acceptPotentiallyCompatibleMethods(MethodBinding[] methods) { /* nop */ } |
| |
| @Override |
| public TypeBinding invocationTargetType() { |
| return scope.getJavaLangObject(); |
| } |
| |
| @Override |
| public ExpressionContext getExpressionContext() { |
| return ExpressionContext.ASSIGNMENT_CONTEXT; |
| } |
| |
| @Override |
| public TypeBinding[] genericTypeArguments() { |
| return null; |
| } |
| |
| @Override |
| public InferenceContext18 freshInferenceContext(Scope someScope) { |
| return new InferenceContext18(someScope, fakeArguments, this, null); |
| } |
| }; |
| } |
| return scope.getMethod(type, selector, params, site); |
| } |
| |
| /** Find a method with identical parameters after erasing. */ |
| public static MethodBinding findCompatibleMethod(ReferenceBinding site, MethodBinding template) |
| { |
| methods: |
| for (MethodBinding existingMethod : site.getMethods(template.selector)) |
| { |
| if (existingMethod.parameters.length != template.parameters.length) |
| continue; |
| for (int j = 0; j < template.parameters.length; j++) { |
| if (TypeBinding.notEquals(existingMethod.parameters[j].erasure(), template.parameters[j].erasure())) |
| continue methods; |
| } |
| return existingMethod; |
| } |
| return null; |
| } |
| /** |
| * Look for the method declaration matching the given selector and length of argument list. |
| * @param typeDeclaration |
| * @param selector |
| * @param nArgs |
| * @return a method or null |
| */ |
| public static AbstractMethodDeclaration findMethodDecl( |
| TypeDeclaration typeDeclaration, |
| char[] selector, |
| int nArgs) |
| { |
| if (typeDeclaration.methods == null) |
| return null; |
| AbstractMethodDeclaration[] methods = typeDeclaration.methods; |
| for (int i=0; i<methods.length; i++) { |
| if (CharOperation.equals(methods[i].selector, selector)) |
| { |
| if (methods[i].arguments == null) { |
| if (nArgs == 0) |
| return methods[i]; |
| } else if (methods[i].arguments.length == nArgs) { |
| return methods[i]; |
| } |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Find a method just by its selector. |
| * Looks up the superclass/interfaces hierarchy. |
| * |
| * @param typeBinding |
| * @param selector |
| * @return first matching method |
| */ |
| public static MethodBinding getMethod(ReferenceBinding typeBinding, char[] selector) { |
| if (typeBinding == null) |
| return null; |
| MethodBinding[] foundMethods = typeBinding.getMethods(selector); |
| if (foundMethods != Binding.NO_METHODS) |
| return foundMethods[0]; |
| MethodBinding foundMethod = getMethod(typeBinding.superclass(), selector); |
| if (foundMethod != null) |
| return foundMethod; |
| ReferenceBinding[] superInterfaces = typeBinding.superInterfaces(); |
| for (int i = 0; i < superInterfaces.length; i++) { |
| foundMethod = getMethod(superInterfaces[i], selector); |
| if (foundMethod != null) |
| return foundMethod; |
| } |
| return null; |
| } |
| |
| /** |
| * When comparing two role types, allow the teams to be super/sub teams |
| * in either direction. |
| * |
| * TODO (SH): check: |
| * (1) does this method hide relevant incompatibilities? |
| * (2) do we need to apply this method in other places, too? |
| * |
| * @param one |
| * @param two |
| * @return the answer |
| */ |
| public static boolean areRoleTypesEqual( |
| RoleTypeBinding one, |
| RoleTypeBinding two) |
| { |
| if (!CharOperation.equals(one.sourceName(), two.sourceName())) |
| return false; |
| ReferenceBinding teamOne = one._staticallyKnownTeam; |
| ReferenceBinding teamTwo = two._staticallyKnownTeam; |
| if (teamOne.isRole() && teamTwo.isRole()) { |
| ReferenceBinding outerOne = teamOne.enclosingType(); |
| ReferenceBinding outerTwo = teamTwo.enclosingType(); |
| if (TypeBinding.notEquals(outerOne, outerTwo)) { |
| // raise nested teams to same level. |
| if (outerOne.isCompatibleWith(outerTwo)) { |
| teamTwo = (ReferenceBinding)TeamModel.strengthenRoleType(outerOne, teamTwo); |
| } else if (outerTwo.isCompatibleWith(outerOne)) { |
| teamOne = (ReferenceBinding)TeamModel.strengthenRoleType(outerTwo, teamOne); |
| } |
| } |
| // in order to compare two team-as-role types take their interface parts: |
| teamOne = teamOne.roleModel.getInterfacePartBinding(); |
| teamTwo = teamTwo.roleModel.getInterfacePartBinding(); |
| } |
| if (teamOne.isCompatibleWith(teamTwo)) |
| return true; |
| if (teamTwo.isCompatibleWith(teamOne)) |
| return true; |
| return false; |
| } |
| /* |
| * This is the structure of confined types as seen by the compiler: |
| * |
| * predefined: |
| * IConfined .superclass = null |
| * Team.IConfined .superclass = null, superinterface = IConfined |
| * Team.Confined .superclass = null |
| * Team.__OT__Confined.superclass = null, superinterface = Team.Confined |
| * |
| * client code: |
| * TX.IConfined .superclass = OTC, superinterface = Team.IConfined |
| * TX.Confined .superclass = OTC, superinterface = Team.Confined |
| * TX.__OT__Confined .superclass = OTC, superinterface = TX.Confined |
| * TX.R .superclass = OTC, superinterface = TX.IMyIFC |
| * TX.__OT__R .superclass = OTC, superinterface = TX.R |
| * (OTX = __OT__Confined, resolving to TX.__OT__Confined except for itself) |
| * |
| * Class-files have to differ from this view: |
| * For JVM-compatibility all superinterfaces must store Object as their superclass. |
| * These interfaces are marked using OTClassFlags attribute. |
| * |
| */ |
| |
| public static boolean isTopConfined(ReferenceBinding type) { |
| char[][] compoundName= type.compoundName; |
| if ( compoundName.length == 3 |
| && ( CharOperation.equals(compoundName, IOTConstants.ORG_OBJECTTEAMS_ICONFINED) |
| || CharOperation.equals(compoundName, IOTConstants.ORG_OBJECTTEAMS_ITEAM_ICONFINED) |
| || CharOperation.equals(compoundName, IOTConstants.ORG_OBJECTTEAMS_TEAM_CONFINED) |
| || CharOperation.equals(compoundName, IOTConstants.ORG_OBJECTTEAMS_TEAM_OTCONFINED))) |
| { |
| return true; |
| } |
| char[] name= type.internalName(); |
| return type.isRole() |
| && ( CharOperation.equals(name, IOTConstants.ICONFINED) |
| || CharOperation.equals(name, IOTConstants.OTCONFINED) |
| || CharOperation.equals(name, IOTConstants.CONFINED)); |
| } |
| |
| public static boolean isConfined(TypeBinding type) { |
| if (type == null || !type.isRole()) return false; |
| ReferenceBinding currentType= (ReferenceBinding)type; |
| while (currentType != null) { |
| if (currentType.id == TypeIds.T_JavaLangObject) |
| return false; |
| currentType = currentType.superclass(); |
| } |
| return true; |
| } |
| public static boolean isPredefinedRole(ReferenceBinding type) { |
| char[] name = type.internalName(); |
| if (CharOperation.equals(name, IOTConstants.ROFI_CACHE)) |
| return true; |
| if (!type.isRole()) |
| return false; |
| return CharOperation.equals(name, IOTConstants.ICONFINED) |
| || CharOperation.equals(name, IOTConstants.OTCONFINED) |
| || CharOperation.equals(name, IOTConstants.CONFINED) |
| || CharOperation.equals(name, IOTConstants.ILOWERABLE) |
| || CharOperation.equals(name, IOTConstants.IBOUNDBASE) |
| || CharOperation.equals(name, IOTConstants.IBOUNDBASE2); |
| } |
| |
| public static boolean extendsOTConfined(TypeDeclaration type) { |
| if (type.superclass instanceof QualifiedTypeReference) |
| return CharOperation.equals(((QualifiedTypeReference)type.superclass).tokens, |
| IOTConstants.ORG_OBJECTTEAMS_TEAM_OTCONFINED); |
| if (type.superclass instanceof SingleTypeReference) |
| return CharOperation.equals(((SingleTypeReference)type.superclass).token, |
| IOTConstants.OTCONFINED); |
| return false; |
| } |
| |
| /** |
| * When asking for a field should synthetic fields be searched? |
| * @param scope site of usage, if this is a generated methods, searching synthetics is OK |
| * @param receiverType class containing the field |
| * @param fieldName |
| * @return the answer |
| */ |
| public static boolean isSearchingForSyntheticField(MethodScope scope, TypeBinding receiverType, char[] fieldName) { |
| return CharOperation.prefixEquals(TypeConstants.SYNTHETIC_ENCLOSING_INSTANCE_PREFIX, fieldName) |
| && scope != null |
| && ((AbstractMethodDeclaration)scope.referenceContext).isGenerated |
| && receiverType instanceof ReferenceBinding |
| && ((ReferenceBinding)receiverType).isRole(); |
| } |
| |
| public static boolean isSourceTypeWithErrors (ReferenceBinding type) { |
| if (type instanceof SourceTypeBinding) { |
| SourceTypeBinding sourceType = (SourceTypeBinding)type; |
| if (sourceType.scope != null) |
| return sourceType.scope.referenceContext.hasErrors(); |
| } |
| return false; |
| } |
| |
| // _OT$base.Types need stripping: |
| public static char[] stripTypeName(char[] typeName) { |
| if (CharOperation.prefixEquals(IOTConstants._OT_BASE, typeName)) |
| typeName = CharOperation.subarray(typeName, IOTConstants.OT_DOLLAR_LEN, -1); |
| return typeName; |
| } |
| |
| public static boolean sameOrContained(ReferenceBinding binding, ReferenceBinding targetEnclosingType) |
| { |
| while (binding != null) { |
| if (TypeBinding.equalsEquals(binding, targetEnclosingType)) |
| return true; |
| binding = binding.enclosingType(); |
| } |
| return false; |
| } |
| |
| /** Does the unit contain any role class with a base class reference that could potentially represent an anchored type? */ |
| public static boolean containsAnchoredBaseclass(CompilationUnitDeclaration parsedUnit) { |
| if (parsedUnit.types == null) return false; |
| for (TypeDeclaration type : parsedUnit.types) |
| if (containsAnchoredBaseclass(type)) |
| return true; |
| return false; |
| } |
| |
| private static boolean containsAnchoredBaseclass(TypeDeclaration type) { |
| TypeReference baseRef = type.baseclass; |
| if (baseRef instanceof ParameterizedSingleTypeReference) { |
| TypeReference[] typeArguments = ((ParameterizedSingleTypeReference)baseRef).typeArguments; |
| return typeArguments != null && typeArguments.length > 0 && typeArguments[0] instanceof TypeAnchorReference; |
| } else if (baseRef instanceof QualifiedTypeReference) { |
| return true; |
| } |
| TypeDeclaration[] members = type.memberTypes; |
| if (members != null) |
| for (TypeDeclaration member : members) |
| if (containsAnchoredBaseclass(member)) |
| return true; |
| return false; |
| } |
| } |