1ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson/*
2ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson * Copyright (C) 2011 The Android Open Source Project
3ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson *
4ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson * Licensed under the Apache License, Version 2.0 (the "License");
5ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson * you may not use this file except in compliance with the License.
6ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson * You may obtain a copy of the License at
7ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson *
8ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson *      http://www.apache.org/licenses/LICENSE-2.0
9ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson *
10ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson * Unless required by applicable law or agreed to in writing, software
11ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson * distributed under the License is distributed on an "AS IS" BASIS,
12ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson * See the License for the specific language governing permissions and
14ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson * limitations under the License.
15ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson */
16ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson
17ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilsonpackage com.android.dx.command.grep;
18ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson
19fe107fb6e3f308ac5174ebdc5a794ee880c741d9Jesse Wilsonimport com.android.dex.ClassData;
20fe107fb6e3f308ac5174ebdc5a794ee880c741d9Jesse Wilsonimport com.android.dex.ClassDef;
21fe107fb6e3f308ac5174ebdc5a794ee880c741d9Jesse Wilsonimport com.android.dex.Dex;
22fe107fb6e3f308ac5174ebdc5a794ee880c741d9Jesse Wilsonimport com.android.dex.EncodedValueReader;
23fe107fb6e3f308ac5174ebdc5a794ee880c741d9Jesse Wilsonimport com.android.dex.MethodId;
24ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilsonimport com.android.dx.io.CodeReader;
25ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilsonimport com.android.dx.io.instructions.DecodedInstruction;
266bf7f30e6968d9917eb4d0ca2280df96a8096134Jesse Wilsonimport java.io.PrintWriter;
27ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilsonimport java.util.HashSet;
28ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilsonimport java.util.Set;
29ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilsonimport java.util.regex.Pattern;
30ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson
31ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilsonpublic final class Grep {
32fe107fb6e3f308ac5174ebdc5a794ee880c741d9Jesse Wilson    private final Dex dex;
33ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson    private final CodeReader codeReader = new CodeReader();
34ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson    private final Set<Integer> stringIds;
35ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson
366bf7f30e6968d9917eb4d0ca2280df96a8096134Jesse Wilson    private final PrintWriter out;
37ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson    private int count = 0;
38ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson
39ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson    private ClassDef currentClass;
40ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson    private ClassData.Method currentMethod;
41ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson
42fe107fb6e3f308ac5174ebdc5a794ee880c741d9Jesse Wilson    public Grep(final Dex dex, Pattern pattern, final PrintWriter out) {
43ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson        this.dex = dex;
44ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson        this.out = out;
45ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson
46ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson        stringIds = getStringIds(dex, pattern);
47ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson
48ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson        codeReader.setStringVisitor(new CodeReader.Visitor() {
499dbd802c8c96c3a66873bc600bc7d1374a1d08e5Orion Hodson            @Override
50ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson            public void visit(DecodedInstruction[] all, DecodedInstruction one) {
51ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson                encounterString(one.getIndex());
52ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson            }
53ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson        });
54ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson    }
55ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson
567e85b634fbafa4a3b927c80665dea6fac337d886Jesse Wilson    private void readArray(EncodedValueReader reader) {
577e85b634fbafa4a3b927c80665dea6fac337d886Jesse Wilson        for (int i = 0, size = reader.readArray(); i < size; i++) {
587e85b634fbafa4a3b927c80665dea6fac337d886Jesse Wilson            switch (reader.peek()) {
597e85b634fbafa4a3b927c80665dea6fac337d886Jesse Wilson            case EncodedValueReader.ENCODED_STRING:
607e85b634fbafa4a3b927c80665dea6fac337d886Jesse Wilson                encounterString(reader.readString());
617e85b634fbafa4a3b927c80665dea6fac337d886Jesse Wilson                break;
627e85b634fbafa4a3b927c80665dea6fac337d886Jesse Wilson            case EncodedValueReader.ENCODED_ARRAY:
637e85b634fbafa4a3b927c80665dea6fac337d886Jesse Wilson                readArray(reader);
647e85b634fbafa4a3b927c80665dea6fac337d886Jesse Wilson                break;
65ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson            }
667e85b634fbafa4a3b927c80665dea6fac337d886Jesse Wilson        }
67ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson    }
68ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson
69ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson    private void encounterString(int index) {
70ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson        if (stringIds.contains(index)) {
71ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson            out.println(location() + " " + dex.strings().get(index));
72ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson            count++;
73ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson        }
74ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson    }
75ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson
76ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson    private String location() {
77ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson        String className = dex.typeNames().get(currentClass.getTypeIndex());
78ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson        if (currentMethod != null) {
79ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson            MethodId methodId = dex.methodIds().get(currentMethod.getMethodIndex());
80ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson            return className + "." + dex.strings().get(methodId.getNameIndex());
81ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson        } else {
82ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson            return className;
83ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson        }
84ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson    }
85ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson
86ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson    /**
87ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson     * Prints usages to out. Returns the number of matches found.
88ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson     */
89ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson    public int grep() {
90ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson        for (ClassDef classDef : dex.classDefs()) {
91ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson            currentClass = classDef;
92ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson            currentMethod = null;
93ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson
94ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson            if (classDef.getClassDataOffset() == 0) {
95ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson                continue;
96ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson            }
97ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson
98ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson            ClassData classData = dex.readClassData(classDef);
99ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson
100ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson            // find the strings in encoded constants
101ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson            int staticValuesOffset = classDef.getStaticValuesOffset();
102ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson            if (staticValuesOffset != 0) {
1037e85b634fbafa4a3b927c80665dea6fac337d886Jesse Wilson                readArray(new EncodedValueReader(dex.open(staticValuesOffset)));
104ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson            }
105ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson
106ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson            // find the strings in method bodies
107ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson            for (ClassData.Method method : classData.allMethods()) {
108ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson                currentMethod = method;
109ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson                if (method.getCodeOffset() != 0) {
110ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson                    codeReader.visitAll(dex.readCode(method).getInstructions());
111ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson                }
112ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson            }
113ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson        }
114ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson
115ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson        currentClass = null;
116ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson        currentMethod = null;
117ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson        return count;
118ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson    }
119ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson
120fe107fb6e3f308ac5174ebdc5a794ee880c741d9Jesse Wilson    private Set<Integer> getStringIds(Dex dex, Pattern pattern) {
121ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson        Set<Integer> stringIds = new HashSet<Integer>();
122ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson        int stringIndex = 0;
123ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson        for (String s : dex.strings()) {
124ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson            if (pattern.matcher(s).find()) {
125ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson                stringIds.add(stringIndex);
126ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson            }
127ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson            stringIndex++;
128ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson        }
129ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson        return stringIds;
130ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson    }
131ae38a1e705253b53abf1beff7dc3467d52c58f32Jesse Wilson}
132