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