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