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.grep;
18 
19 import com.android.dex.ClassData;
20 import com.android.dex.ClassDef;
21 import com.android.dex.Dex;
22 import com.android.dex.EncodedValueReader;
23 import com.android.dex.MethodId;
24 import com.android.dx.io.CodeReader;
25 import com.android.dx.io.instructions.DecodedInstruction;
26 import java.io.PrintWriter;
27 import java.util.HashSet;
28 import java.util.Set;
29 import java.util.regex.Pattern;
30 
31 public final class Grep {
32     private final Dex dex;
33     private final CodeReader codeReader = new CodeReader();
34     private final Set<Integer> stringIds;
35 
36     private final PrintWriter out;
37     private int count = 0;
38 
39     private ClassDef currentClass;
40     private ClassData.Method currentMethod;
41 
Grep(final Dex dex, Pattern pattern, final PrintWriter out)42     public Grep(final Dex dex, Pattern pattern, final PrintWriter out) {
43         this.dex = dex;
44         this.out = out;
45 
46         stringIds = getStringIds(dex, pattern);
47 
48         codeReader.setStringVisitor(new CodeReader.Visitor() {
49             @Override
50             public void visit(DecodedInstruction[] all, DecodedInstruction one) {
51                 encounterString(one.getIndex());
52             }
53         });
54     }
55 
readArray(EncodedValueReader reader)56     private void readArray(EncodedValueReader reader) {
57         for (int i = 0, size = reader.readArray(); i < size; i++) {
58             switch (reader.peek()) {
59             case EncodedValueReader.ENCODED_STRING:
60                 encounterString(reader.readString());
61                 break;
62             case EncodedValueReader.ENCODED_ARRAY:
63                 readArray(reader);
64                 break;
65             }
66         }
67     }
68 
encounterString(int index)69     private void encounterString(int index) {
70         if (stringIds.contains(index)) {
71             out.println(location() + " " + dex.strings().get(index));
72             count++;
73         }
74     }
75 
location()76     private String location() {
77         String className = dex.typeNames().get(currentClass.getTypeIndex());
78         if (currentMethod != null) {
79             MethodId methodId = dex.methodIds().get(currentMethod.getMethodIndex());
80             return className + "." + dex.strings().get(methodId.getNameIndex());
81         } else {
82             return className;
83         }
84     }
85 
86     /**
87      * Prints usages to out. Returns the number of matches found.
88      */
grep()89     public int grep() {
90         for (ClassDef classDef : dex.classDefs()) {
91             currentClass = classDef;
92             currentMethod = null;
93 
94             if (classDef.getClassDataOffset() == 0) {
95                 continue;
96             }
97 
98             ClassData classData = dex.readClassData(classDef);
99 
100             // find the strings in encoded constants
101             int staticValuesOffset = classDef.getStaticValuesOffset();
102             if (staticValuesOffset != 0) {
103                 readArray(new EncodedValueReader(dex.open(staticValuesOffset)));
104             }
105 
106             // find the strings in method bodies
107             for (ClassData.Method method : classData.allMethods()) {
108                 currentMethod = method;
109                 if (method.getCodeOffset() != 0) {
110                     codeReader.visitAll(dex.readCode(method).getInstructions());
111                 }
112             }
113         }
114 
115         currentClass = null;
116         currentMethod = null;
117         return count;
118     }
119 
getStringIds(Dex dex, Pattern pattern)120     private Set<Integer> getStringIds(Dex dex, Pattern pattern) {
121         Set<Integer> stringIds = new HashSet<Integer>();
122         int stringIndex = 0;
123         for (String s : dex.strings()) {
124             if (pattern.matcher(s).find()) {
125                 stringIds.add(stringIndex);
126             }
127             stringIndex++;
128         }
129         return stringIds;
130     }
131 }
132