FindUsages.java revision 6bf7f30e6968d9917eb4d0ca2280df96a8096134
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.DexBuffer; 23import com.android.dx.io.FieldId; 24import com.android.dx.io.MethodId; 25import com.android.dx.io.OpcodeInfo; 26import com.android.dx.io.instructions.DecodedInstruction; 27import java.io.PrintStream; 28import java.io.PrintWriter; 29import java.util.Collections; 30import java.util.HashSet; 31import java.util.Set; 32 33public final class FindUsages { 34 private final DexBuffer dex; 35 private final Set<Integer> methodIds; 36 private final Set<Integer> fieldIds; 37 private final CodeReader codeReader = new CodeReader(); 38 private final PrintWriter out; 39 40 private ClassDef currentClass; 41 private ClassData.Method currentMethod; 42 43 public FindUsages(DexBuffer dex, String declaredBy, String memberName, final PrintWriter out) { 44 this.dex = dex; 45 this.out = out; 46 47 int typeStringIndex = Collections.binarySearch(dex.strings(), declaredBy); 48 int memberNameIndex = Collections.binarySearch(dex.strings(), memberName); 49 if (typeStringIndex < 0 || memberNameIndex < 0) { 50 methodIds = null; 51 fieldIds = null; 52 return; // these symbols are not mentioned in this dex 53 } 54 55 int typeIndex = Collections.binarySearch(dex.typeIds(), typeStringIndex); 56 if (typeIndex < 0) { 57 methodIds = null; 58 fieldIds = null; 59 return; // this type name isn't used as a type in this dex 60 } 61 62 methodIds = getMethodIds(dex, memberNameIndex, typeIndex); 63 fieldIds = getFieldIds(dex, memberNameIndex, typeIndex); 64 65 codeReader.setFieldVisitor(new CodeReader.Visitor() { 66 public void visit(DecodedInstruction[] all, 67 DecodedInstruction one) { 68 int fieldId = one.getIndex(); 69 if (fieldIds.contains(fieldId)) { 70 out.println(location() + ": field reference (" 71 + OpcodeInfo.getName(one.getOpcode()) + ")"); 72 } 73 } 74 }); 75 76 codeReader.setMethodVisitor(new CodeReader.Visitor() { 77 public void visit(DecodedInstruction[] all, 78 DecodedInstruction one) { 79 int methodId = one.getIndex(); 80 if (methodIds.contains(methodId)) { 81 out.println(location() + ": method reference (" 82 + OpcodeInfo.getName(one.getOpcode()) + ")"); 83 } 84 } 85 }); 86 } 87 88 private String location() { 89 String className = dex.typeNames().get(currentClass.getTypeIndex()); 90 if (currentMethod != null) { 91 MethodId methodId = dex.methodIds().get(currentMethod.getMethodIndex()); 92 return className + "." + dex.strings().get(methodId.getNameIndex()); 93 } else { 94 return className; 95 } 96 } 97 98 /** 99 * Prints usages to out. 100 */ 101 public void findUsages() { 102 if (fieldIds == null || methodIds == null) { 103 return; 104 } 105 106 for (ClassDef classDef : dex.classDefs()) { 107 currentClass = classDef; 108 currentMethod = null; 109 110 if (classDef.getClassDataOffset() == 0) { 111 continue; 112 } 113 114 ClassData classData = dex.readClassData(classDef); 115 for (ClassData.Field field : classData.allFields()) { 116 if (fieldIds.contains(field.getFieldIndex())) { 117 out.println(location() + " field declared"); 118 } 119 } 120 121 for (ClassData.Method method : classData.allMethods()) { 122 currentMethod = method; 123 if (methodIds.contains(method.getMethodIndex())) { 124 out.println(location() + " method declared"); 125 } 126 if (method.getCodeOffset() != 0) { 127 codeReader.visitAll(dex.readCode(method).getInstructions()); 128 } 129 } 130 } 131 132 currentClass = null; 133 currentMethod = null; 134 } 135 136 /** 137 * Returns the fields with {@code memberNameIndex} declared by {@code 138 * declaringType}. 139 */ 140 private Set<Integer> getFieldIds(DexBuffer dex, int memberNameIndex, int declaringType) { 141 Set<Integer> fields = new HashSet<Integer>(); 142 int fieldIndex = 0; 143 for (FieldId fieldId : dex.fieldIds()) { 144 if (fieldId.getNameIndex() == memberNameIndex 145 && declaringType == fieldId.getDeclaringClassIndex()) { 146 fields.add(fieldIndex); 147 } 148 fieldIndex++; 149 } 150 return fields; 151 } 152 153 /** 154 * Returns the methods with {@code memberNameIndex} declared by {@code 155 * declaringType} and its subtypes. 156 */ 157 private Set<Integer> getMethodIds(DexBuffer dex, int memberNameIndex, int declaringType) { 158 Set<Integer> subtypes = findAssignableTypes(dex, declaringType); 159 160 Set<Integer> methods = new HashSet<Integer>(); 161 int methodIndex = 0; 162 for (MethodId method : dex.methodIds()) { 163 if (method.getNameIndex() == memberNameIndex 164 && subtypes.contains(method.getDeclaringClassIndex())) { 165 methods.add(methodIndex); 166 } 167 methodIndex++; 168 } 169 return methods; 170 } 171 172 /** 173 * Returns the set of types that can be assigned to {@code typeIndex}. 174 */ 175 private Set<Integer> findAssignableTypes(DexBuffer dex, int typeIndex) { 176 Set<Integer> assignableTypes = new HashSet<Integer>(); 177 assignableTypes.add(typeIndex); 178 179 for (ClassDef classDef : dex.classDefs()) { 180 if (assignableTypes.contains(classDef.getSupertypeIndex())) { 181 assignableTypes.add(classDef.getTypeIndex()); 182 continue; 183 } 184 185 for (int implemented : classDef.getInterfaces()) { 186 if (assignableTypes.contains(implemented)) { 187 assignableTypes.add(classDef.getTypeIndex()); 188 break; 189 } 190 } 191 } 192 193 return assignableTypes; 194 } 195} 196