FindUsages.java revision 38b861bc63b91114d52ba01e74d31fbf316a5784
1/* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.dx.command.findusages; 18 19import com.android.dx.io.ClassData; 20import com.android.dx.io.ClassDef; 21import com.android.dx.io.CodeReader; 22import com.android.dx.io.DecodedInstruction; 23import com.android.dx.io.DexBuffer; 24import com.android.dx.io.FieldId; 25import com.android.dx.io.MethodId; 26import java.io.PrintStream; 27import java.util.Collections; 28import java.util.HashSet; 29import java.util.Set; 30 31public final class FindUsages { 32 private final DexBuffer dex; 33 private final Set<Integer> methodIds; 34 private final Set<Integer> fieldIds; 35 private final CodeReader codeReader = new CodeReader(); 36 private final PrintStream out; 37 38 private ClassDef currentClass; 39 private ClassData.Method currentMethod; 40 41 public FindUsages(DexBuffer dex, String declaredBy, String memberName, final PrintStream out) { 42 this.dex = dex; 43 this.out = out; 44 45 int typeStringIndex = Collections.binarySearch(dex.strings(), declaredBy); 46 int memberNameIndex = Collections.binarySearch(dex.strings(), memberName); 47 if (typeStringIndex < 0 || memberNameIndex < 0) { 48 methodIds = null; 49 fieldIds = null; 50 return; // these symbols are not mentioned in this dex 51 } 52 53 int typeIndex = Collections.binarySearch(dex.typeIds(), typeStringIndex); 54 if (typeIndex < 0) { 55 methodIds = null; 56 fieldIds = null; 57 return; // this type name isn't used as a type in this dex 58 } 59 60 methodIds = getMethodIds(dex, memberNameIndex, typeIndex); 61 fieldIds = getFieldIds(dex, memberNameIndex, typeIndex); 62 63 codeReader.setFieldVisitor(new CodeReader.Visitor() { 64 public void visit(CodeReader.Instruction instruction, 65 DecodedInstruction[] all, DecodedInstruction one) { 66 int fieldId = one.getIndex(); 67 if (fieldIds.contains(fieldId)) { 68 out.println(location() + ": field reference (" + instruction + ")"); 69 } 70 } 71 }); 72 73 codeReader.setMethodVisitor(new CodeReader.Visitor() { 74 public void visit(CodeReader.Instruction instruction, 75 DecodedInstruction[] all, DecodedInstruction one) { 76 int methodId = one.getIndex(); 77 if (methodIds.contains(methodId)) { 78 out.println(location() + ": method reference (" + instruction + ")"); 79 } 80 } 81 }); 82 } 83 84 private String location() { 85 String className = dex.typeNames().get(currentClass.getTypeIndex()); 86 if (currentMethod != null) { 87 MethodId methodId = dex.methodIds().get(currentMethod.getMethodIndex()); 88 return className + "." + dex.strings().get(methodId.getNameIndex()); 89 } else { 90 return className; 91 } 92 } 93 94 /** 95 * Prints usages to out. 96 */ 97 public void findUsages() { 98 if (fieldIds == null || methodIds == null) { 99 return; 100 } 101 102 for (ClassDef classDef : dex.classDefs()) { 103 currentClass = classDef; 104 currentMethod = null; 105 106 if (classDef.getClassDataOffset() == 0) { 107 continue; 108 } 109 110 ClassData classData = dex.readClassData(classDef); 111 for (ClassData.Field field : classData.allFields()) { 112 if (fieldIds.contains(field.getFieldIndex())) { 113 out.println(location() + " field declared"); 114 } 115 } 116 117 for (ClassData.Method method : classData.allMethods()) { 118 currentMethod = method; 119 if (methodIds.contains(method.getMethodIndex())) { 120 out.println(location() + " method declared"); 121 } 122 if (method.getCodeOffset() != 0) { 123 codeReader.visitAll(dex.readCode(method).getInstructions()); 124 } 125 } 126 } 127 128 currentClass = null; 129 currentMethod = null; 130 } 131 132 /** 133 * Returns the fields with {@code memberNameIndex} declared by {@code 134 * declaringType}. 135 */ 136 private Set<Integer> getFieldIds(DexBuffer dex, int memberNameIndex, int declaringType) { 137 Set<Integer> fields = new HashSet<Integer>(); 138 int fieldIndex = 0; 139 for (FieldId fieldId : dex.fieldIds()) { 140 if (fieldId.getNameIndex() == memberNameIndex 141 && declaringType == (int) fieldId.getDeclaringClassIndex()) { 142 fields.add(fieldIndex); 143 } 144 fieldIndex++; 145 } 146 return fields; 147 } 148 149 /** 150 * Returns the methods with {@code memberNameIndex} declared by {@code 151 * declaringType} and its subtypes. 152 */ 153 private Set<Integer> getMethodIds(DexBuffer dex, int memberNameIndex, int declaringType) { 154 Set<Integer> subtypes = findAssignableTypes(dex, declaringType); 155 156 Set<Integer> methods = new HashSet<Integer>(); 157 int methodIndex = 0; 158 for (MethodId method : dex.methodIds()) { 159 if (method.getNameIndex() == memberNameIndex 160 && subtypes.contains((int) method.getDeclaringClassIndex())) { 161 methods.add(methodIndex); 162 } 163 methodIndex++; 164 } 165 return methods; 166 } 167 168 /** 169 * Returns the set of types that can be assigned to {@code typeIndex}. 170 */ 171 private Set<Integer> findAssignableTypes(DexBuffer dex, int typeIndex) { 172 Set<Integer> assignableTypes = new HashSet<Integer>(); 173 assignableTypes.add(typeIndex); 174 175 for (ClassDef classDef : dex.classDefs()) { 176 if (assignableTypes.contains(classDef.getSupertypeIndex())) { 177 assignableTypes.add(classDef.getTypeIndex()); 178 continue; 179 } 180 181 for (int implemented : classDef.getInterfaces()) { 182 if (assignableTypes.contains(implemented)) { 183 assignableTypes.add(classDef.getTypeIndex()); 184 break; 185 } 186 } 187 } 188 189 return assignableTypes; 190 } 191} 192