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