/* * Copyright 2008 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package com.google.gwt.dev.jjs.ast; import com.google.gwt.dev.jjs.ast.js.JMultiExpression; import com.google.gwt.dev.jjs.impl.HasNameSort; import com.google.gwt.dev.util.collect.HashMap; import com.google.gwt.dev.util.collect.IdentityHashMap; import com.google.gwt.dev.util.collect.IdentityHashSet; import com.google.gwt.dev.util.collect.IdentitySets; import com.google.gwt.dev.util.collect.Lists; import com.google.gwt.dev.util.collect.Maps; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; /** * Oracle that can answer questions regarding the types in a program. */ public class JTypeOracle implements Serializable { /** * Checks a clinit method to find out a few things. * * <ol> * <li>What other clinits it calls.</li> * <li>If it runs any code other than clinit calls.</li> * </ol> * * This is used to remove "dead clinit cycles" where self-referential cycles * of empty clinits can keep each other alive. */ private static final class CheckClinitVisitor extends JVisitor { private final Set<JDeclaredType> clinitTargets = new IdentityHashSet<JDeclaredType>(); /** * Tracks whether any live code is run in this clinit. This is only reliable * because we explicitly visit all AST structures that might contain * non-clinit-calling code. * * @see #mightBeDeadCode(JExpression) * @see #mightBeDeadCode(JStatement) */ private boolean hasLiveCode = false; public JDeclaredType[] getClinitTargets() { return clinitTargets.toArray(new JDeclaredType[clinitTargets.size()]); } public boolean hasLiveCode() { return hasLiveCode; } @Override public boolean visit(JBlock x, Context ctx) { for (JStatement stmt : x.getStatements()) { if (mightBeDeadCode(stmt)) { accept(stmt); } else { hasLiveCode = true; } } return false; } @Override public boolean visit(JDeclarationStatement x, Context ctx) { JVariable target = x.getVariableRef().getTarget(); if (target instanceof JField) { JField field = (JField) target; if (field.getLiteralInitializer() != null) { // Top level initializations generate no code. return false; } } hasLiveCode = true; return false; } @Override public boolean visit(JExpressionStatement x, Context ctx) { JExpression expr = x.getExpr(); if (mightBeDeadCode(expr)) { accept(expr); } else { hasLiveCode = true; } return false; } @Override public boolean visit(JMethodCall x, Context ctx) { JMethod target = x.getTarget(); if (JProgram.isClinit(target)) { clinitTargets.add(target.getEnclosingType()); } else { hasLiveCode = true; } return false; } @Override public boolean visit(JMultiExpression x, Context ctx) { for (JExpression expr : x.exprs) { // Only a JMultiExpression or JMethodCall can contain clinit calls. if (mightBeDeadCode(expr)) { accept(expr); } else { hasLiveCode = true; } } return false; } @Override public boolean visit(JNewInstance x, Context ctx) { if (x.hasSideEffects()) { hasLiveCode = true; } return false; } private boolean mightBeDeadCode(JExpression expr) { // Must have a visit method for every subtype that answers yes! return expr instanceof JMultiExpression || expr instanceof JMethodCall || expr instanceof JNewInstance; } private boolean mightBeDeadCode(JStatement stmt) { // Must have a visit method for every subtype that answers yes! return stmt instanceof JBlock || stmt instanceof JExpressionStatement || stmt instanceof JDeclarationStatement; } } /** * Compare two methods based on name and original argument types * {@link JMethod#getOriginalParamTypes()}. Note that nothing special is done * here regarding methods with type parameters in their argument lists. The * caller must be careful that this level of matching is sufficient. */ public static boolean methodsDoMatch(JMethod method1, JMethod method2) { // static methods cannot match each other if (method1.isStatic() || method2.isStatic()) { return false; } // names must be identical if (!method1.getName().equals(method2.getName())) { return false; } // original return type must be identical if (method1.getOriginalReturnType() != method2.getOriginalReturnType()) { return false; } // original parameter types must be identical List<JType> params1 = method1.getOriginalParamTypes(); List<JType> params2 = method2.getOriginalParamTypes(); int params1size = params1.size(); if (params1size != params2.size()) { return false; } for (int i = 0; i < params1size; ++i) { if (params1.get(i) != params2.get(i)) { return false; } } return true; } private JDeclaredType baseArrayType; /** * A map of all interfaces to the set of classes that could theoretically * implement them. */ private final Map<JInterfaceType, Set<JClassType>> couldBeImplementedMap = new IdentityHashMap<JInterfaceType, Set<JClassType>>(); /** * A map of all classes to the set of interfaces that they could theoretically * implement. */ private final Map<JClassType, Set<JInterfaceType>> couldImplementMap = new IdentityHashMap<JClassType, Set<JInterfaceType>>(); /** * The set of all interfaces that are initially implemented by both a Java and * Overlay type. */ private final Set<JInterfaceType> dualImpls = new IdentityHashSet<JInterfaceType>(); /** * A map of all classes to the set of interfaces they directly implement, * possibly through inheritance. */ private final Map<JClassType, Set<JInterfaceType>> implementsMap = new IdentityHashMap<JClassType, Set<JInterfaceType>>(); /** * The types in the program that are instantiable. All types in this set * should be run-time types as defined at * {@link JProgram#getRunTimeType(JReferenceType)}. */ private Set<JReferenceType> instantiatedTypes = null; /** * A map of all interfaces to the set of classes that directly implement them, * possibly through inheritance. */ private final Map<JInterfaceType, Set<JClassType>> isImplementedMap = new IdentityHashMap<JInterfaceType, Set<JClassType>>(); private JDeclaredType javaIoSerializable; private JDeclaredType javaLangCloneable; /** * Caches the {@link Object} class. */ private JClassType javaLangObject = null; /** * A map of all interfaces that are implemented by overlay types to the * overlay type that initially implements it. */ private final Map<JInterfaceType, JClassType> jsoSingleImpls = new IdentityHashMap<JInterfaceType, JClassType>(); /** * The associated {@link JProgram}. */ private final JProgram program; /** * A map of all classes to the set of classes that extend them, directly or * indirectly. */ private final Map<JClassType, Set<JClassType>> subClassMap = new IdentityHashMap<JClassType, Set<JClassType>>(); /** * A map of all interfaces to the set of interfaces that extend them, directly * or indirectly. */ private final Map<JInterfaceType, Set<JInterfaceType>> subInterfaceMap = new IdentityHashMap<JInterfaceType, Set<JInterfaceType>>(); /** * A map of all classes to the set of classes they extend, directly or * indirectly. */ private final Map<JClassType, Set<JClassType>> superClassMap = new IdentityHashMap<JClassType, Set<JClassType>>(); /** * A map of all interfaces to the set of interfaces they extend, directly or * indirectly. */ private final Map<JInterfaceType, Set<JInterfaceType>> superInterfaceMap = new IdentityHashMap<JInterfaceType, Set<JInterfaceType>>(); /** * A map of all methods with virtual overrides, onto the collection of * overridden methods. Each key method's collections is a map of the set of * subclasses who inherit the key method mapped onto the set of interface * methods the key method virtually implements. For a definition of a virtual * override, see {@link #getAllVirtualOverrides(JMethod)}. */ private final Map<JMethod, Map<JClassType, Set<JMethod>>> virtualUpRefMap = new IdentityHashMap<JMethod, Map<JClassType, Set<JMethod>>>(); /** * An index of all polymorphic methods for each class. */ private final Map<JClassType, Map<String, JMethod>> polyClassMethodMap = new IdentityHashMap<JClassType, Map<String, JMethod>>(); public JTypeOracle(JProgram program) { this.program = program; } /** * True if the type is a JSO or interface implemented by JSO.. * * @param type * @return */ public boolean canBeJavaScriptObject(JType type) { if (type instanceof JNonNullType) { type = ((JNonNullType) type).getUnderlyingType(); } return program.isJavaScriptObject(type) || program.typeOracle.isSingleJsoImpl(type); } public boolean canTheoreticallyCast(JReferenceType type, JReferenceType qType) { if (!type.canBeNull() && qType == program.getTypeNull()) { // Cannot cast non-nullable to null return false; } // Compare the underlying types. type = type.getUnderlyingType(); qType = qType.getUnderlyingType(); JClassType jlo = program.getTypeJavaLangObject(); if (type == qType || type == jlo) { return true; } /** * Cross-cast allowed in theory, prevents TypeTightener from turning * cross-casts into null-casts. */ if (canBeJavaScriptObject(type) && canBeJavaScriptObject(qType)) { return true; } if (canTriviallyCast(type, qType)) { return true; } if (type instanceof JArrayType) { JArrayType aType = (JArrayType) type; if (qType instanceof JArrayType) { JArrayType qaType = (JArrayType) qType; JType leafType = aType.getLeafType(); JType qLeafType = qaType.getLeafType(); int dims = aType.getDims(); int qDims = qaType.getDims(); // null[] or Object[] -> int[][] might work, other combinations won't if (dims < qDims && leafType != program.getTypeJavaLangObject() && !(leafType instanceof JNullType)) { return false; } if (dims == qDims) { if (leafType instanceof JReferenceType && qLeafType instanceof JReferenceType) { return canTheoreticallyCast((JReferenceType) leafType, (JReferenceType) qLeafType); } } } /* * Warning: If this code is ever updated to consider casts of array types * to interface types, then be sure to consider that casting an array to * Serializable and Cloneable succeeds. Currently all casts of an array to * an interface return true, which is overly conservative but is safe. */ } else if (type instanceof JClassType) { JClassType cType = (JClassType) type; if (qType instanceof JClassType) { return isSubClass(cType, (JClassType) qType); } else if (qType instanceof JInterfaceType) { return get(couldImplementMap, cType).contains(qType); } } else if (type instanceof JInterfaceType) { JInterfaceType iType = (JInterfaceType) type; if (qType instanceof JClassType) { return get(couldBeImplementedMap, iType).contains(qType); } } else if (type instanceof JNullType) { } return true; } public boolean canTriviallyCast(JReferenceType type, JReferenceType qType) { if (type.canBeNull() && !qType.canBeNull()) { // Cannot reliably cast nullable to non-nullable return false; } // Compare the underlying types. type = type.getUnderlyingType(); qType = qType.getUnderlyingType(); JClassType jlo = program.getTypeJavaLangObject(); if (type == qType || qType == jlo) { return true; } if (type instanceof JArrayType) { JArrayType aType = (JArrayType) type; if (qType instanceof JArrayType) { JArrayType qaType = (JArrayType) qType; JType leafType = aType.getLeafType(); JType qLeafType = qaType.getLeafType(); int dims = aType.getDims(); int qDims = qaType.getDims(); // int[][] -> Object[] or null[] trivially true if (dims > qDims && (qLeafType == jlo || qLeafType instanceof JNullType)) { return true; } if (dims == qDims) { if (leafType instanceof JReferenceType && qLeafType instanceof JReferenceType) { return canTriviallyCast((JReferenceType) leafType, (JReferenceType) qLeafType); } } } if (qType == javaIoSerializable || qType == javaLangCloneable || qType == baseArrayType) { return true; } } else if (type instanceof JClassType) { JClassType cType = (JClassType) type; if (qType instanceof JClassType) { JClassType qcType = (JClassType) qType; if (isSuperClass(cType, qcType)) { return true; } } else if (qType instanceof JInterfaceType) { return implementsInterface(cType, (JInterfaceType) qType); } } else if (type instanceof JInterfaceType) { JInterfaceType iType = (JInterfaceType) type; if (qType instanceof JInterfaceType) { return extendsInterface(iType, (JInterfaceType) qType); } } else if (type instanceof JNullType) { return true; } return false; } public boolean canTriviallyCast(JType type, JType qType) { if (type instanceof JPrimitiveType && qType instanceof JPrimitiveType) { return type == qType; } else if (type instanceof JReferenceType && qType instanceof JReferenceType) { return canTriviallyCast((JReferenceType) type, (JReferenceType) qType); } return false; } public void computeBeforeAST() { baseArrayType = program.getIndexedType("Array"); javaLangObject = program.getTypeJavaLangObject(); javaIoSerializable = program.getFromTypeMap(Serializable.class.getName()); javaLangCloneable = program.getFromTypeMap(Cloneable.class.getName()); superClassMap.clear(); subClassMap.clear(); superInterfaceMap.clear(); subInterfaceMap.clear(); implementsMap.clear(); couldImplementMap.clear(); isImplementedMap.clear(); couldBeImplementedMap.clear(); jsoSingleImpls.clear(); dualImpls.clear(); for (JDeclaredType type : program.getDeclaredTypes()) { if (type instanceof JClassType) { recordSuperSubInfo((JClassType) type); } else { recordSuperSubInfo((JInterfaceType) type); } } /* * Now that the basic type hierarchy is computed, compute which JSOs * implement interfaces singlely or dually. */ JClassType jsoType = program.getJavaScriptObject(); List<JClassType> jsoSubTypes = Lists.create(); if (jsoType != null) { jsoSubTypes = new ArrayList<JClassType>(get(subClassMap, jsoType)); Collections.sort(jsoSubTypes, new HasNameSort()); for (JClassType jsoSubType : jsoSubTypes) { for (JInterfaceType intf : jsoSubType.getImplements()) { jsoSingleImpls.put(intf, jsoSubType); for (JInterfaceType superIntf : get(superInterfaceMap, intf)) { if (!jsoSingleImpls.containsKey(superIntf)) { jsoSingleImpls.put(superIntf, jsoSubType); } } } } } for (JDeclaredType type : program.getDeclaredTypes()) { if (type instanceof JClassType) { computeImplements((JClassType) type); } } for (JDeclaredType type : program.getDeclaredTypes()) { if (type instanceof JClassType) { computeCouldImplement((JClassType) type); } } for (JDeclaredType type : program.getDeclaredTypes()) { if (type instanceof JClassType) { computeVirtualUpRefs((JClassType) type); } } // Create dual mappings for any jso interface with a Java implementor. for (JInterfaceType jsoIntf : jsoSingleImpls.keySet()) { Set<JClassType> implementors = get(isImplementedMap, jsoIntf); for (JClassType implementor : implementors) { if (!program.isJavaScriptObject(implementor)) { dualImpls.add(jsoIntf); break; } } } } /** * References to any methods which this method implementation might override * or implement in any instantiable class, including strange cases where there * is no direct relationship between the methods except in a subclass that * inherits one and implements the other. Example: * * <pre> * interface IFoo { * foo(); * } * class Unrelated { * foo() { ... } * } * class Foo extends Unrelated implements IFoo { * } * </pre> * * In this case, <code>Unrelated.foo()</code> virtually implements * <code>IFoo.foo()</code> in subclass <code>Foo</code>. */ public Set<JMethod> getAllOverrides(JMethod method) { Set<JMethod> results = new IdentityHashSet<JMethod>(); getAllRealOverrides(method, results); getAllVirtualOverrides(method, results); return results; } public Set<JReferenceType> getInstantiatedTypes() { return instantiatedTypes; } public JMethod getPolyMethod(JClassType type, String signature) { return getOrCreatePolyMap(type).get(signature); } public JClassType getSingleJsoImpl(JReferenceType maybeSingleJsoIntf) { return jsoSingleImpls.get(maybeSingleJsoIntf.getUnderlyingType()); } public boolean isDualJsoInterface(JReferenceType maybeDualImpl) { return dualImpls.contains(maybeDualImpl.getUnderlyingType()); } /** * True if either a JSO, or is an interface that is ONLY implemented by a JSO. */ public boolean isEffectivelyJavaScriptObject(JType type) { if (type instanceof JReferenceType) { JReferenceType refType = (JReferenceType) type; return program.isJavaScriptObject(refType) || (isSingleJsoImpl(refType) && !isDualJsoInterface(refType)); } else { return false; } } /** * Determine whether a type is instantiated. */ public boolean isInstantiatedType(JDeclaredType type) { return instantiatedTypes == null || instantiatedTypes.contains(type); } /** * Determine whether a type is instantiated. */ public boolean isInstantiatedType(JReferenceType type) { type = type.getUnderlyingType(); if (instantiatedTypes == null || instantiatedTypes.contains(type)) { return true; } if (type.isExternal()) { // TODO(tobyr) I don't know under what situations it is safe to assume // that an external type won't be instantiated. For example, if we // assumed that an external exception weren't instantiated, because we // didn't see it constructed in our code, dead code elimination would // incorrectly elide any catch blocks for that exception. // // We should see how this effects optimization and if we can limit its // impact if necessary. return true; } if (type instanceof JNullType) { return true; } else if (type instanceof JArrayType) { JArrayType arrayType = (JArrayType) type; if (arrayType.getLeafType() instanceof JNullType) { return true; } } return false; } public boolean isSameOrSuper(JClassType type, JClassType qType) { return (type == qType || isSuperClass(type, qType)); } public boolean isSingleJsoImpl(JType type) { return type instanceof JReferenceType && getSingleJsoImpl((JReferenceType) type) != null; } /** * Returns true if qType is a subclass of type, directly or indirectly. */ public boolean isSubClass(JClassType type, JClassType qType) { return get(subClassMap, type).contains(qType); } /** * Returns true if qType is a superclass of type, directly or indirectly. */ public boolean isSuperClass(JClassType type, JClassType qType) { return get(superClassMap, type).contains(qType); } /** * This method should be called after altering the types that are live in the * associated JProgram. */ public void recomputeAfterOptimizations() { Set<JDeclaredType> computed = new IdentityHashSet<JDeclaredType>(); for (JDeclaredType type : program.getDeclaredTypes()) { computeClinitTarget(type, computed); } nextDual : for (Iterator<JInterfaceType> it = dualImpls.iterator(); it.hasNext();) { JInterfaceType dualIntf = it.next(); Set<JClassType> implementors = get(isImplementedMap, dualIntf); for (JClassType implementor : implementors) { if (isInstantiatedType(implementor) && !program.isJavaScriptObject(implementor)) { // This dual is still implemented by a Java class. continue nextDual; } } // No Java implementors. it.remove(); } // Prune jsoSingleImpls when implementor isn't live Iterator<JClassType> jit = jsoSingleImpls.values().iterator(); while (jit.hasNext()) { if (!isInstantiatedType(jit.next())) { jit.remove(); } } } public void setInstantiatedTypes(Set<JReferenceType> instantiatedTypes) { this.instantiatedTypes = instantiatedTypes; polyClassMethodMap.keySet().retainAll(instantiatedTypes); } private <K, V> void add(Map<K, Set<V>> map, K key, V value) { getOrCreate(map, key).add(value); } private void computeClinitTarget(JDeclaredType type, Set<JDeclaredType> computed) { if (type.isExternal() || !type.hasClinit() || computed.contains(type)) { return; } JClassType superClass = null; if (type instanceof JClassType) { superClass = ((JClassType) type).getSuperClass(); } if (superClass != null) { /* * Compute super first so that it's already been tightened to the tightest * possible target; this ensures if we're tightened as well it's to the * transitively tightest target. */ computeClinitTarget(superClass, computed); } if (type.getClinitTarget() != type) { // I already have a trivial clinit, just follow my super chain. type.setClinitTarget(superClass.getClinitTarget()); } else { // I still have a real clinit, actually compute. JDeclaredType target = computeClinitTargetRecursive(type, computed, new IdentityHashSet<JDeclaredType>()); type.setClinitTarget(target); } computed.add(type); } private JDeclaredType computeClinitTargetRecursive(JDeclaredType type, Set<JDeclaredType> computed, Set<JDeclaredType> alreadySeen) { // Track that we've been seen. alreadySeen.add(type); JMethod method = type.getMethods().get(0); assert (JProgram.isClinit(method)); CheckClinitVisitor v = new CheckClinitVisitor(); v.accept(method); if (v.hasLiveCode()) { return type; } // Check for trivial super clinit. JDeclaredType[] clinitTargets = v.getClinitTargets(); if (clinitTargets.length == 1) { JDeclaredType singleTarget = clinitTargets[0]; if (type instanceof JClassType && singleTarget instanceof JClassType && isSuperClass((JClassType) type, (JClassType) singleTarget)) { return singleTarget.getClinitTarget(); } } for (JDeclaredType target : clinitTargets) { if (!target.hasClinit()) { // A false result is always accurate. continue; } /* * If target has a clinit, so do I; but only if target has already been * recomputed this run. */ if (target.hasClinit() && computed.contains(target)) { return type; } /* * Prevent recursion sickness: ignore this call for now since this call is * being accounted for higher on the stack. */ if (alreadySeen.contains(target)) { continue; } if (computeClinitTargetRecursive(target, computed, alreadySeen) != null) { // Calling a non-empty clinit means I am a real clinit. return type; } else { // This clinit is okay, keep going. continue; } } return null; } /** * Compute all of the things I might conceivably implement, either through * super types or sub types. */ private void computeCouldImplement(JClassType type) { Set<JInterfaceType> couldImplementSet = new IdentityHashSet<JInterfaceType>(); // all of my direct implements are trivially true couldImplementSet.addAll(get(implementsMap, type)); List<JClassType> subclasses = new ArrayList<JClassType>(); subclasses.addAll(get(subClassMap, type)); for (JClassType subclass : subclasses) { for (JInterfaceType intf : subclass.getImplements()) { couldImplementSet.add(intf); for (JInterfaceType isup : get(superInterfaceMap, intf)) { couldImplementSet.add(isup); } } } if (!couldImplementSet.isEmpty()) { couldImplementMap.put(type, IdentitySets.normalize(couldImplementSet)); for (JInterfaceType couldImpl : couldImplementSet) { add(couldBeImplementedMap, couldImpl, type); } } } /** * Compute all of the things I implement directly, through super types. */ private void computeImplements(JClassType type) { Set<JInterfaceType> implementsSet = new IdentityHashSet<JInterfaceType>(); List<JClassType> list = new ArrayList<JClassType>(); list.add(type); list.addAll(get(superClassMap, type)); for (JClassType superclass : list) { for (JInterfaceType intf : superclass.getImplements()) { implementsSet.add(intf); for (JInterfaceType isup : get(superInterfaceMap, intf)) { implementsSet.add(isup); } } } if (!implementsSet.isEmpty()) { implementsMap.put(type, IdentitySets.normalize(implementsSet)); for (JInterfaceType impl : implementsSet) { add(isImplementedMap, impl, type); } } } /** * WEIRD: Suppose class Foo declares void f(){} and unrelated interface I also * declares void f(). Then suppose Bar extends Foo implements I and doesn't * override f(). We need to record a "virtual" upref from Foo.f() to I.f() so * that if I.f() is rescued AND Bar is instantiable, Foo.f() does not get * pruned. */ private void computeVirtualUpRefs(JClassType type) { if (type.getSuperClass() == null || type.getSuperClass() == javaLangObject) { return; } /* * For each interface I directly implement, check all methods and make sure * I define implementations for them. If I don't, then check all my super * classes to find virtual overrides. */ for (JInterfaceType intf : type.getImplements()) { computeVirtualUpRefs(type, intf); for (JInterfaceType superIntf : get(superInterfaceMap, intf)) { computeVirtualUpRefs(type, superIntf); } } } /** * For each interface I directly implement, check all methods and make sure I * define implementations for them. If I don't, then check all my super * classes to find virtual overrides. */ private void computeVirtualUpRefs(JClassType type, JInterfaceType intf) { outer : for (JMethod intfMethod : intf.getMethods()) { for (JMethod classMethod : type.getMethods()) { if (methodsDoMatch(intfMethod, classMethod)) { // this class directly implements the interface method continue outer; } } // this class does not directly implement the interface method // if any super classes do, create a virtual up ref for (JClassType superType = type.getSuperClass(); superType != javaLangObject; superType = superType.getSuperClass()) { for (JMethod superMethod : superType.getMethods()) { if (methodsDoMatch(intfMethod, superMethod)) { // this super class directly implements the interface method // create a virtual up ref // System.out.println("Virtual upref from " + superType.getName() // + "." + superMethod.getName() + " to " + intf.getName() + "." // + intfMethod.getName() + " via " + type.getName()); Map<JClassType, Set<JMethod>> classToMethodMap = getOrCreateMap(virtualUpRefMap, superMethod); add(classToMethodMap, type, intfMethod); // do not search additional super types continue outer; } } } } } /** * Returns true if type extends the interface represented by qType, either * directly or indirectly. */ private boolean extendsInterface(JInterfaceType type, JInterfaceType qType) { return get(superInterfaceMap, type).contains(qType); } private <K, V> Set<V> get(Map<K, Set<V>> map, K key) { Set<V> set = map.get(key); if (set == null) { return Collections.emptySet(); } return set; } private void getAllRealOverrides(JMethod method, Set<JMethod> results) { for (JMethod possibleOverride : method.getOverrides()) { results.add(possibleOverride); } } private void getAllVirtualOverrides(JMethod method, Set<JMethod> results) { Map<JClassType, Set<JMethod>> overrideMap = virtualUpRefMap.get(method); if (overrideMap != null) { for (Map.Entry<JClassType, Set<JMethod>> entry : overrideMap.entrySet()) { JClassType classType = entry.getKey(); if (isInstantiatedType(classType)) { results.addAll(entry.getValue()); } } } } private <K, V> Set<V> getOrCreate(Map<K, Set<V>> map, K key) { Set<V> set = map.get(key); if (set == null) { set = new IdentityHashSet<V>(); map.put(key, set); } return set; } private <K, K2, V> Map<K2, V> getOrCreateMap(Map<K, Map<K2, V>> map, K key) { Map<K2, V> map2 = map.get(key); if (map2 == null) { map2 = new IdentityHashMap<K2, V>(); map.put(key, map2); } return map2; } private Map<String, JMethod> getOrCreatePolyMap(JClassType type) { Map<String, JMethod> polyMap = polyClassMethodMap.get(type); if (polyMap == null) { JClassType superClass = type.getSuperClass(); if (superClass == null) { polyMap = new HashMap<String, JMethod>(); } else { Map<String, JMethod> superPolyMap = getOrCreatePolyMap(type.getSuperClass()); polyMap = new HashMap<String, JMethod>(superPolyMap); } for (JMethod method : type.getMethods()) { if (method.canBePolymorphic()) { polyMap.put(method.getSignature(), method); } } polyMap = Maps.normalize(polyMap); polyClassMethodMap.put(type, polyMap); } return polyMap; } /** * Returns true if type implements the interface represented by qType, either * directly or indirectly. */ private boolean implementsInterface(JClassType type, JInterfaceType qType) { return get(implementsMap, type).contains(qType); } /** * Record the all of my super classes (and myself as a subclass of them). */ private void recordSuperSubInfo(JClassType type) { Set<JClassType> superSet = new IdentityHashSet<JClassType>(); for (JClassType t = type.getSuperClass(); t != null; t = t.getSuperClass()) { superSet.add(t); add(subClassMap, t, type); } if (!superSet.isEmpty()) { superClassMap.put(type, IdentitySets.normalize(superSet)); } } /** * Record the all of my super interfaces (and myself as a sub interface of * them). */ private void recordSuperSubInfo(JInterfaceType type) { if (!type.getImplements().isEmpty()) { Set<JInterfaceType> superSet = new IdentityHashSet<JInterfaceType>(); recordSuperSubInfo(type, superSet, type); superInterfaceMap.put(type, IdentitySets.normalize(superSet)); } } /** * Recursively record all of my super interfaces. */ private void recordSuperSubInfo(JInterfaceType base, Set<JInterfaceType> superSet, JInterfaceType cur) { for (JInterfaceType intf : cur.getImplements()) { superSet.add(intf); add(subInterfaceMap, intf, base); recordSuperSubInfo(base, superSet, intf); } } }