| /********************************************************************** |
| * This file is part of "Object Teams Development Tooling"-Software |
| * |
| * Copyright 2003, 2014 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 |
| * |
| * 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 |
| **********************************************************************/ |
| /** |
| * ObjectTeams Eclipse source extensions |
| * |
| * @author Markus Witte |
| * |
| * @date 29.09.2003 |
| */ |
| package org.eclipse.objectteams.otdt.internal.core.compiler.bytecode; |
| |
| |
| import java.util.Arrays; |
| |
| import org.eclipse.jdt.core.compiler.CharOperation; |
| import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; |
| import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.FieldBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; |
| import org.eclipse.objectteams.otdt.core.compiler.IOTConstants; |
| 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.Config.NotConfiguredException; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.RoleTypeBinding; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.mappings.CallinImplementorDyn; |
| 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.copyinheritance.CopyInheritance; |
| import org.eclipse.objectteams.otdt.internal.core.compiler.util.TypeAnalyzer; |
| |
| /** |
| * @author Markus Witte |
| * @version $Id: ConstantPoolObjectMapper.java 23416 2010-02-03 19:59:31Z stephan $ |
| */ |
| public class ConstantPoolObjectMapper implements ClassFileConstants{ |
| |
| private MethodBinding _srcMethod; //The source-Method which bytecode ist to copy |
| private MethodBinding _dstMethod; //The destination-Method for bytecode-copy will receive transformed bytecode from source-Method |
| |
| /** |
| * @param srcMethodBinding where bytes are copied from |
| * @param dstMethodBinding where bytes are copied to |
| */ |
| public ConstantPoolObjectMapper( |
| MethodBinding srcMethodBinding, |
| MethodBinding dstMethodBinding) |
| { |
| this._srcMethod = srcMethodBinding.original(); |
| this._dstMethod = dstMethodBinding.original(); |
| } |
| |
| /** |
| * map a src-ConstantPoolObject into an dst-ConstantPoolObject |
| * @param src_cpo |
| * @return destination object |
| */ |
| public ConstantPoolObject mapConstantPoolObject(ConstantPoolObject src_cpo) |
| { |
| return mapConstantPoolObject(src_cpo, false/*addMarkerArgAllowed*/); |
| } |
| /** |
| * map a src-ConstantPoolObject into an dst-ConstantPoolObject |
| * @param src_cpo |
| * @param addMarkerArgAllowed whether copying is allowed to add a marker arg |
| * @return destination object |
| */ |
| public ConstantPoolObject mapConstantPoolObject(ConstantPoolObject src_cpo, boolean addMarkerArgAllowed) |
| { |
| int type=src_cpo.getType(); |
| ReferenceBinding dstTeam = getTeam(this._dstMethod); |
| |
| // first investigate the declaring class if appropriate: |
| ReferenceBinding declaringClass = null; |
| switch (type) { |
| case MethodRefTag: |
| case InterfaceMethodRefTag: |
| declaringClass = src_cpo.getMethodRef().declaringClass; |
| break; |
| case FieldRefTag: |
| declaringClass = src_cpo.getFieldRef().declaringClass; |
| break; |
| } |
| if (RoleTypeBinding.isRoleWithExplicitAnchor(declaringClass)) |
| { |
| return src_cpo; // don't map features of externalized roles! |
| } |
| |
| // perform the mapping: |
| switch(type){ |
| case FieldRefTag: |
| return new ConstantPoolObject( |
| FieldRefTag, |
| mapField(this._srcMethod, src_cpo.getFieldRef().original(), dstTeam)) ; |
| case MethodRefTag: |
| return new ConstantPoolObject( |
| MethodRefTag, |
| mapMethod(this._srcMethod, src_cpo.getMethodRef().original(), this._dstMethod, dstTeam, addMarkerArgAllowed)); |
| case InterfaceMethodRefTag: |
| return new ConstantPoolObject( |
| InterfaceMethodRefTag, |
| mapMethod(this._srcMethod, src_cpo.getMethodRef().original(), this._dstMethod, dstTeam, addMarkerArgAllowed)); |
| case ClassTag: |
| return new ConstantPoolObject( |
| ClassTag, |
| mapClass(this._srcMethod, src_cpo.getClassObject(), dstTeam)); |
| } |
| //if no mapping is needed, return original ConstantPoolObject |
| return src_cpo; |
| } |
| |
| /** |
| * Map a type binding and convert it to a UTF8-Object. |
| * @param typeBinding |
| * @param useGenerics should type parameters be respected (else use erasure) |
| * @return ConstantPoolObject of type Utf8Tag |
| */ |
| public ConstantPoolObject mapTypeUtf8(TypeBinding typeBinding, boolean useGenerics) |
| { |
| //System.out.println("Sign of "+typeBinding+"="+new String(typeBinding.signature())); |
| char[] prefix = null; |
| if (typeBinding.isArrayType()) |
| { |
| // need to disassemble arrays, because we want to analyze the element type: |
| ArrayBinding array = (ArrayBinding)typeBinding; |
| prefix = new char[array.dimensions()]; |
| Arrays.fill(prefix, '['); |
| typeBinding = array.leafComponentType; |
| } |
| if (typeBinding.isClass() || typeBinding.isInterface()) |
| { |
| ConstantPoolObject clazzCPO = new ConstantPoolObject( |
| ClassTag, |
| mapClass(this._srcMethod, typeBinding, getTeam(this._dstMethod))); |
| typeBinding = clazzCPO.getClassObject(); |
| } |
| char[] signature = useGenerics ? typeBinding.genericTypeSignature() : typeBinding.signature(); |
| |
| if (prefix != null) |
| signature = CharOperation.concat(prefix, signature); |
| return new ConstantPoolObject(Utf8Tag, signature); |
| } |
| |
| /** |
| * this method realizes the logic of the mapping |
| * @return destination type |
| */ |
| private static TypeBinding mapClass(MethodBinding srcMethod, TypeBinding typeBinding, ReferenceBinding dstTeam) { |
| return mapClass(getTeam(srcMethod), typeBinding, dstTeam); |
| } |
| |
| public static TypeBinding mapClass(ReferenceBinding srcTeamBinding, TypeBinding typeBinding, ReferenceBinding dstTeam) |
| { |
| boolean isArrayBinding = typeBinding instanceof ArrayBinding; |
| int dimension = 0; |
| TypeBinding originalType = typeBinding; |
| if(isArrayBinding) |
| { |
| ArrayBinding formerType = (ArrayBinding) typeBinding; |
| typeBinding = formerType.leafComponentType(); |
| dimension = formerType.dimensions; |
| |
| if (typeBinding.isBaseType()) |
| return formerType; // no need to map array of basetype |
| } |
| ReferenceBinding refTypeBinding = (ReferenceBinding)typeBinding; |
| // if Binding points at Role-Field of Superteamclass, then mapping must be done |
| if(isMappableClass(refTypeBinding)) |
| { |
| refTypeBinding = (ReferenceBinding) refTypeBinding.erasure(); |
| ReferenceBinding refTeamBinding=getTeam(refTypeBinding); |
| if(refTeamBinding != null) |
| { |
| if(srcTeamBinding != null) |
| { |
| srcTeamBinding = (ReferenceBinding) srcTeamBinding.erasure(); |
| TypeBinding newBinding = null; |
| ReferenceBinding currentSrcTeam = srcTeamBinding; |
| ReferenceBinding currentDstTeam = dstTeam; |
| while ( currentSrcTeam != null |
| && currentDstTeam != null) { |
| if(TypeBinding.equalsEquals(refTeamBinding, currentSrcTeam)) { |
| // mapping the enclosing team which is nested in an outer team? |
| if (TypeBinding.equalsEquals(refTypeBinding, refTeamBinding) && refTypeBinding.isRole()) |
| newBinding = currentDstTeam; |
| else |
| newBinding = searchRoleClass(refTypeBinding, currentDstTeam); |
| break; |
| } |
| // try dependent refinement of base classes: |
| ReferenceBinding srcBase = currentSrcTeam.baseclass(); |
| if (srcBase != null && (srcBase.isTeam() || srcBase.isRole())) { |
| if (TypeBinding.equalsEquals(srcBase, refTypeBinding)) |
| newBinding = currentDstTeam.baseclass(); |
| else |
| newBinding = searchRoleClass(refTypeBinding, currentDstTeam.baseclass()); |
| if (newBinding != null) |
| break; |
| } |
| // the common team to start searching might be an enclosing: |
| if (!currentSrcTeam.isRole()) |
| break; |
| currentSrcTeam= currentSrcTeam.enclosingType(); |
| currentDstTeam= currentDstTeam.enclosingType(); |
| } |
| if (newBinding != null) { |
| if(isArrayBinding) |
| { |
| // have no scope so can't use Scope.createArray(), |
| // which otherwise should be used throughout. |
| try { |
| newBinding = new ArrayBinding(newBinding, dimension, Config.getLookupEnvironment()); |
| } catch (NotConfiguredException e) { |
| e.logWarning("Cannot create array binding"); //$NON-NLS-1$ |
| } |
| } |
| return newBinding; |
| } |
| } |
| } |
| } |
| return originalType; |
| } |
| |
| /** |
| * This method realizes the logic of the mapping for fields. |
| * @param srcMethod where to copy from |
| * @param refFieldBinding what to copy/remap |
| * @param dstTeam where to copy to |
| * @return destination field |
| */ |
| public static FieldBinding mapField(MethodBinding srcMethod, FieldBinding refFieldBinding, ReferenceBinding dstTeam) |
| { |
| // if Binding points at Role-Field of Superteamclass, then mapping must be done |
| if(dstTeam!=null){ |
| if(isMappableField(refFieldBinding)){ |
| if (refFieldBinding.isSynthetic()) { |
| RoleModel role = ((ReferenceBinding)mapClass(srcMethod, refFieldBinding.declaringClass, dstTeam)).roleModel; |
| if (role != null) { |
| FieldBinding dstField= role.mapSyntheticField(refFieldBinding); |
| if (dstField!= null) |
| return dstField; |
| } |
| } |
| ReferenceBinding refTeamBinding=getTeam(refFieldBinding); |
| if(refTeamBinding!=null){ |
| ReferenceBinding srcTeamBinding = getTeam(srcMethod); |
| if(srcTeamBinding!=null){ |
| if(TypeBinding.equalsEquals(refTeamBinding, srcTeamBinding)) { |
| FieldBinding newBinding=searchRoleField(refFieldBinding, dstTeam); |
| if ( newBinding != null |
| && TypeBinding.notEquals(newBinding.declaringClass, dstTeam) |
| && !TeamModel.isTeamContainingRole(dstTeam, newBinding.declaringClass)) |
| { |
| // field is declared neither in dstTeam nor one of its roles. |
| // find the class, that corresponds to the field's declaring class: |
| ReferenceBinding updatedClass = |
| (ReferenceBinding)mapClass(srcMethod, newBinding.declaringClass, dstTeam); |
| // update field binding to new declaring class? |
| if (TypeBinding.notEquals(newBinding.declaringClass, updatedClass)) |
| newBinding = new FieldBinding(newBinding, updatedClass); |
| } |
| return newBinding; |
| } |
| } |
| } |
| } |
| } |
| return refFieldBinding; |
| } |
| /** |
| * This method realizes the logic of the mapping for methods. |
| * @param srcMethod where to copy from |
| * @param refMethodBinding what to copy/remap |
| * @param dstTeam where to copy to |
| * @return destination method |
| */ |
| public static MethodBinding mapMethod(MethodBinding srcMethod, |
| MethodBinding refMethodBinding, |
| MethodBinding dstMethod, |
| ReferenceBinding dstTeam) |
| { |
| return mapMethod(srcMethod, refMethodBinding, dstMethod, dstTeam, false/*addMarkerAllowed*/); |
| } |
| /** |
| * This method realizes the logic of the mapping for methods. |
| * @param srcMethod where to copy from |
| * @param refMethodBinding what to copy/remap |
| * @param dstTeam where to copy to |
| * @param addMarkerArgAllowed whether copying is allowed to add a marker arg |
| * @return destination method |
| */ |
| public static MethodBinding mapMethod(MethodBinding srcMethod, |
| MethodBinding refMethodBinding, |
| MethodBinding dstMethod, |
| ReferenceBinding dstTeam, |
| boolean addMarkerArgAllowed) |
| { |
| if (dstMethod != null) { |
| if (dstMethod.model != null) { |
| if (dstMethod.model.oldSelfcall == refMethodBinding) |
| return dstMethod.model.adjustedSelfcall; |
| } |
| if (isConfinedSuperCtor(srcMethod, refMethodBinding)) |
| return getConfinedSuperCtor(dstMethod); |
| } |
| // if Binding points at Role-Method of Superteamclass, then mapping must be done |
| if(isMappableMethod(refMethodBinding)){ |
| if (refMethodBinding.isSynthetic()) { |
| RoleModel role = ((ReferenceBinding)mapClass(srcMethod, refMethodBinding.declaringClass, dstTeam)).roleModel; |
| if (role != null) { |
| MethodBinding foundMethod = role.mapSyntheticMethod(refMethodBinding); |
| if (foundMethod != null) |
| return foundMethod; |
| } |
| } |
| boolean isDecapsAccessor = false; |
| if (CharOperation.prefixEquals(IOTConstants.OT_DECAPS, refMethodBinding.selector)) { |
| // to find a decapsulated method, first strip off the accessor's prefix, then search and ... |
| refMethodBinding = new MethodBinding(refMethodBinding, refMethodBinding.declaringClass); |
| refMethodBinding.selector = CharOperation.subarray(refMethodBinding.selector, IOTConstants.OT_DECAPS.length, -1); |
| isDecapsAccessor = true; |
| } |
| MethodBinding foundMethod = doMapMethod(srcMethod, refMethodBinding, dstMethod, dstTeam, addMarkerArgAllowed); |
| if (foundMethod != null && isDecapsAccessor) { |
| // .. append the stripped prefix after finding |
| foundMethod = new MethodBinding(foundMethod, foundMethod.declaringClass); |
| foundMethod.selector = CharOperation.concat(IOTConstants.OT_DECAPS, foundMethod.selector); |
| } |
| return foundMethod; |
| } |
| return refMethodBinding; |
| } |
| static MethodBinding doMapMethod(MethodBinding srcMethod, |
| MethodBinding refMethodBinding, |
| MethodBinding dstMethod, |
| ReferenceBinding dstTeam, |
| boolean addMarkerArgAllowed) |
| { |
| ReferenceBinding refTeamBinding=getTeam(refMethodBinding); |
| if(refTeamBinding!=null) { |
| refTeamBinding = (ReferenceBinding) refTeamBinding.erasure(); |
| ReferenceBinding srcTeamBinding = getTeam(srcMethod); |
| if(srcTeamBinding!=null){ |
| // same team (direct tsuper) or compatible (indirect tsuper) |
| if(srcTeamBinding.isCompatibleWith(refTeamBinding)) { |
| if(dstTeam!=null) { |
| MethodBinding newBinding=searchRoleMethodInTeam(dstTeam, refMethodBinding, addMarkerArgAllowed); |
| if (newBinding != null) |
| return newBinding; |
| } |
| } |
| } |
| } |
| return refMethodBinding; |
| } |
| |
| /** |
| * Is given method a super-call to the constructor of an __OT__Confined role? |
| */ |
| private static boolean isConfinedSuperCtor(MethodBinding srcMethod, MethodBinding refMethodBinding) { |
| // constructor? |
| if (!refMethodBinding.isConstructor()) |
| return false; |
| // of class __OT__Confined? |
| if (!CharOperation.equals(refMethodBinding.declaringClass.compoundName, IOTConstants.ORG_OBJECTTEAMS_TEAM_OTCONFINED)) |
| return false; |
| // is it the superclass of the current src class? |
| if (TypeBinding.equalsEquals(refMethodBinding.declaringClass, srcMethod.declaringClass.superclass())) |
| return true; |
| // current src class may have no super class which is OK if it is Team.__OT__Confined |
| return |
| srcMethod.declaringClass.superclass() == null |
| && CharOperation.equals(IOTConstants.ORG_OBJECTTEAMS_TEAM_OTCONFINED, refMethodBinding.declaringClass.compoundName); |
| } |
| |
| private static MethodBinding getConfinedSuperCtor(MethodBinding dstMethod) { |
| ReferenceBinding newSuperclass = dstMethod.declaringClass.superclass(); |
| if (newSuperclass == null) |
| throw new InternalCompilerError("copying byte code to class without superclass?"); //$NON-NLS-1$ |
| return newSuperclass.getExactConstructor(new TypeBinding[0]); |
| } |
| |
| /** |
| * searches for a matching field in the destination Team |
| * For this the method will compare all roles from destination Team with |
| * the role of the referenced Field. if one matching role ist found, |
| * search inside this role for the referenced Field. |
| * The Field exists in this Role, because we have copied it before. |
| * @param refFieldBinding the referenced FieldBinding which must be a Role-Field of the dstTeamBinding-Superclass |
| * @param dstTeamBinding the destination Team |
| * @return the found FieldBinding or null if no matching Field is found |
| */ |
| private static FieldBinding searchRoleField(FieldBinding refFieldBinding, ReferenceBinding dstTeamBinding) { |
| ReferenceBinding refRoleBinding = refFieldBinding.declaringClass; |
| if(isMappableClass(refRoleBinding)) { |
| //search for matching Role in dstTeamBinding |
| ReferenceBinding dstRefRoleBinding = searchRoleClass(refRoleBinding, dstTeamBinding); |
| //search for matching field in destination Role |
| return ConstantPoolObjectReader.findFieldByBinding(dstRefRoleBinding, refFieldBinding); |
| } |
| //never should be here |
| return null; |
| } |
| |
| /** |
| * searches for a matching Method in the destination Team |
| * For this the method will compare all roles from destination Team with |
| * the role of the referenced Method. if one matching role ist found, |
| * search inside this role for the referenced Method. |
| * The Method exists in this Role, because we have copied it before. |
| * @param dstTeam the destination Team |
| * @param refMethod the referenced MethodBinding which must be a Role-Method of the dstTeamBinding-Superclass |
| * @return the found MethodBinding or null if no matching Method is found |
| */ |
| private static MethodBinding searchRoleMethodInTeam(ReferenceBinding dstTeam, MethodBinding refMethod, boolean addMarkerArgAllowed) |
| { |
| ReferenceBinding refRoleBinding = refMethod.declaringClass; |
| if(isMappableClass(refRoleBinding)){ |
| //search for matching Role in dstTeamBinding |
| ReferenceBinding dstRefRoleBinding = searchRoleClass(refRoleBinding, dstTeam); |
| //search for matching method in destination Role |
| MethodBinding roleMethod = searchRoleMethodInRole(dstRefRoleBinding, refMethod, addMarkerArgAllowed); |
| if (roleMethod != null) |
| return roleMethod; |
| if (dstRefRoleBinding.isCompatibleWith(refRoleBinding) || ConstantPoolObjectReader.isFakedOTREMethod(refMethod.selector) != 0) { |
| return new MethodBinding(refMethod, dstRefRoleBinding); |
| } |
| // System.out.println("method "+new String(refMethod.declaringClass.qualifiedSourceName())+"."+refMethod+" not found in "+new String(dstRefRoleBinding.qualifiedSourceName())); |
| } else if (isStaticBasecallSurrogate(refMethod)) { |
| // basecall surrogates for static role methods are team methods, but need adaptation, too. |
| MethodBinding[] methods = dstTeam.getMethods(refMethod.selector); |
| for (int i = 0; i < methods.length; i++) { |
| if (methods[i].parameters.length != refMethod.parameters.length) |
| continue; |
| for (int j = 0; j < methods[i].parameters.length; j++) { |
| if (TypeBinding.notEquals(methods[i].parameters[j], refMethod.parameters[j])) // non-variant |
| continue; |
| } |
| return methods[i]; |
| } |
| // FIXME(SH): should byte code store info whether callin method is bound? |
| return refMethod; // don't change method reference, method might be a dummy (throwing OTREInternalError). |
| } |
| return null; |
| } |
| |
| private static MethodBinding searchRoleMethodInRole(ReferenceBinding role, MethodBinding refMethod, boolean addMarkerArgAllowed) |
| { |
| MethodBinding[] methods; |
| if (CopyInheritance.isCreator(refMethod)) { |
| methods = role.getMethods(refMethod.selector); |
| assert(methods.length == 1); |
| return methods[0]; |
| } |
| if ( role.isLocalType() |
| && CharOperation.equals(refMethod.selector, IOTConstants.INIT_METHOD_NAME)) |
| { |
| // have no overriding along implicit inheritance due to scoping of locals, |
| // so just retrieve the method by its name: |
| return role.getMethods(IOTConstants.INIT_METHOD_NAME)[0]; |
| } |
| int bestRank = Integer.MAX_VALUE; |
| MethodBinding bestMethod = null; |
| methods = role.getMethods(refMethod.selector); |
| for (int i=0; i<methods.length; i++) { |
| if ( refMethod.parameters.length == methods[i].parameters.length |
| || ( addMarkerArgAllowed |
| && refMethod.parameters.length + 1 == methods[i].parameters.length)) |
| { |
| int rank = rankMethod(refMethod, methods[i], 0); |
| if (rank < bestRank) { |
| bestMethod = methods[i]; |
| bestRank = rank; |
| } else if (CharOperation.equals(CallinImplementorDyn.OT_CALL_AFTER, methods[i].selector) |
| || CharOperation.equals(CallinImplementorDyn.OT_CALL_BEFORE, methods[i].selector)) { |
| // don't bother ranking these, because they're not linked via overriddenTSupers, |
| // But we need to verbatim translate super-calls in generated dispatch code |
| return methods[i]; |
| } |
| } |
| } |
| if (bestMethod != null) |
| return bestMethod; |
| if ( role.superclass() != null |
| && !CharOperation.equals(role.superclass().compoundName, TypeConstants.JAVA_LANG_OBJECT) |
| && haveCommonEnclosingType(role.superclass(), role)) |
| { |
| MethodBinding result = searchRoleMethodInRole(role.superclass(), refMethod, addMarkerArgAllowed); |
| if (result != null) |
| return result; |
| } |
| ReferenceBinding [] superInterfaces = role.superInterfaces(); |
| if (superInterfaces != null) { |
| for (int i=0; i<superInterfaces.length; i++) { |
| if (haveCommonEnclosingType(superInterfaces[i], role.enclosingType())) { |
| MethodBinding binding = searchRoleMethodInRole(superInterfaces[i], refMethod, addMarkerArgAllowed); |
| if (binding != null) |
| return binding; |
| } |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Do left and right have a common enclosing type at the same distance? |
| * @param left |
| * @param right |
| * @return the answer |
| */ |
| private static boolean haveCommonEnclosingType(ReferenceBinding left, ReferenceBinding right) |
| { |
| ReferenceBinding leftEnclosing = left.enclosingType(); |
| ReferenceBinding rightEnclosing = right.enclosingType(); |
| if (leftEnclosing == null || rightEnclosing == null) |
| return false; |
| if (TypeBinding.equalsEquals(leftEnclosing, rightEnclosing)) |
| return true; |
| return haveCommonEnclosingType(leftEnclosing, rightEnclosing); |
| } |
| |
| /** |
| * Test whether candidate is a suitable adjustment for toLookFor. |
| * Traversal uses copyInheritanceSrc and overriddenTSuper. |
| * Rank is determined by depth of traversal needed to find a connection. |
| * |
| * PRE: length of parameterlists has been checked. |
| * |
| * @return a small positiv integer value if suitable. Integer.MAX_VALUE if candidate is not suitable. |
| */ |
| private static int rankMethod(MethodBinding toLookFor, MethodBinding candidate, int currentRank) |
| { |
| currentRank++; |
| if (candidate == null || toLookFor == null) |
| return Integer.MAX_VALUE; |
| toLookFor = toLookFor.original(); |
| candidate = candidate.original(); |
| if (candidate == toLookFor) |
| return currentRank; |
| if ( currentRank > 0 // at top level we search all methods by this name any way. |
| && (candidate.modifiers & ClassFileConstants.AccBridge) != 0) |
| { |
| // bridge method: there must be a matching regular method: find it: |
| MethodBinding[] otherMethods= candidate.declaringClass.getMethods(candidate.selector); |
| methods: |
| for (MethodBinding other: otherMethods) { |
| if (other != candidate && other.parameters.length == candidate.parameters.length) { |
| for(int i=0; i<other.parameters.length; i++) |
| if (TypeBinding.notEquals(other.parameters[i], candidate.parameters[i])) |
| continue methods; |
| if (other == toLookFor) |
| return currentRank; |
| break; |
| } |
| } |
| } |
| int rank = rankMethod(toLookFor, candidate.copyInheritanceSrc, currentRank); |
| if (rank < Integer.MAX_VALUE) |
| return rank; |
| rank = rankMethod(toLookFor.copyInheritanceSrc, candidate, currentRank); |
| if (rank < Integer.MAX_VALUE) |
| return rank; |
| if (candidate.overriddenTSupers != null) |
| for (int i=0; i < candidate.overriddenTSupers.length; i++) { |
| rank = rankMethod(toLookFor, candidate.overriddenTSupers[i], currentRank); |
| if (rank < Integer.MAX_VALUE) |
| return rank; |
| } |
| if (toLookFor.overriddenTSupers != null) |
| for (int i=0; i< toLookFor.overriddenTSupers.length; i++) { |
| rank = rankMethod(toLookFor.overriddenTSupers[i], candidate, currentRank); |
| if (rank < Integer.MAX_VALUE) |
| return rank; |
| } |
| return Integer.MAX_VALUE; |
| } |
| /** |
| * Searches for enclosing Team of binding |
| * @param binding |
| * @return Team if available, null otherhwise |
| */ |
| private static ReferenceBinding getTeam(MethodBinding binding) { |
| return getTeam(binding.declaringClass); |
| } |
| |
| /** |
| * Searches for enclosing Team of binding |
| * @param binding |
| * @return Team if available, null otherhwise |
| */ |
| private static ReferenceBinding getTeam(FieldBinding binding) { |
| return getTeam(binding.declaringClass); |
| } |
| |
| /** |
| * Searches for enclosing Team of binding, including binding itself. |
| * @param binding |
| * @return Team if available, null otherhwise |
| */ |
| private static ReferenceBinding getTeam(ReferenceBinding binding) { |
| if (binding.isTeam()) |
| return binding; |
| return TeamModel.getEnclosingTeam(binding); |
| } |
| |
| private static boolean isMappableMethod(MethodBinding refMethodBinding){ |
| if (isMappableClass(refMethodBinding.declaringClass)) |
| return true; |
| if (isStaticBasecallSurrogate(refMethodBinding)) |
| return true; |
| return false; |
| } |
| |
| private static boolean isStaticBasecallSurrogate(MethodBinding refMethodBinding) { |
| return CharOperation.endsWith(refMethodBinding.selector, "$base".toCharArray()) //$NON-NLS-1$ |
| && !refMethodBinding.declaringClass.isRole(); // regular base call surrogates are role methods. |
| // if it's not a role method, the callin must be static. |
| } |
| |
| private static boolean isMappableField(FieldBinding refFieldBinding){ |
| return isMappableClass(refFieldBinding.declaringClass); |
| } |
| |
| public static boolean isMappableClass(ReferenceBinding type) { |
| if (type.isInterface()) |
| return false; // tsuper interface is good enough |
| return TeamModel.getEnclosingTeam(type) != null; |
| } |
| |
| /** |
| * Searches for a matching Roleclass in the destination Team which is given by the site of reference. |
| * For this the method will compare all roles from destination Team with the referenced Role. |
| * If one matching role ist found, this role will be returned |
| * The Role exists in this Team, because we have copied it before. |
| * |
| * @param template the referenced TypeBinding which must be a Roleclass of the dstTeamBinding-Superclass |
| * @param site the site where the new type reference is needed, either the team or one of its nested types. |
| * @return the found TypeBinding or null if no matching Role is found |
| */ |
| public static ReferenceBinding searchRoleClass(ReferenceBinding template, ReferenceBinding site) |
| { |
| if (!template.isRole()) // possible on external calls. |
| return template; |
| ReferenceBinding srcTeam = TeamModel.getEnclosingTeam(template); |
| // different names are used depending on the kind of type: |
| if (template.isLocalType()) |
| return searchRoleClass(template.constantPoolName(), srcTeam, site); |
| else |
| return searchRoleClass(template.internalName(), srcTeam, site); |
| } |
| |
| /** |
| * Implementation of the above. |
| * |
| * TODO (SH): searching by simple name is not exact for nested teams! |
| * |
| * @return null ProblemReferenceBinding or a valid type in the context of 'site' |
| */ |
| private static ReferenceBinding searchRoleClass(char[] roleName, ReferenceBinding srcTeam, ReferenceBinding site) |
| { |
| // check the site type |
| if ( site.isLocalType() |
| && CharOperation.equals(site.constantPoolName(), roleName)) |
| return site; |
| // check its member types |
| ReferenceBinding[] members = site.memberTypes(); |
| // TODO(SH): We observe binary and source of the same type. |
| // As a workaround prefer source over binary here: |
| ReferenceBinding candidate = null; |
| for (int i=0; i<members.length; i++) { |
| if (CharOperation.equals(members[i].internalName(), roleName)) |
| if (members[i].isBinaryBinding() && candidate == null) |
| candidate = members[i]; |
| else |
| return members[i]; |
| } |
| if (candidate != null) |
| return candidate; |
| for (int i=0; i<members.length; i++) { |
| ReferenceBinding result = searchRoleClass(roleName, srcTeam, members[i]); |
| if (result != null && result.isValidBinding()) |
| return result; |
| } |
| // delegate to role model, which can handle local types, too. |
| if (site.isRole()) |
| return site.roleModel.findTypeRelative( |
| TypeAnalyzer.constantPoolNameRelativeToTeam(srcTeam, roleName)); |
| return null; // caller in recursion may still find the type. |
| } |
| } |
| //Markus Witte} |
| |