Skip to content

Commit a56c911

Browse files
committed
[DEX] Batch refactor/rename dex elements
1 parent 7e457a3 commit a56c911

File tree

6 files changed

+526
-24
lines changed

6 files changed

+526
-24
lines changed

src/main/java/com/reandroid/dex/refactor/Rename.java

Lines changed: 209 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,30 @@
1818
import com.reandroid.dex.key.Key;
1919
import com.reandroid.dex.key.KeyPair;
2020
import com.reandroid.dex.model.DexClassRepository;
21+
import com.reandroid.dex.smali.SmaliDirective;
22+
import com.reandroid.dex.smali.SmaliFormat;
23+
import com.reandroid.dex.smali.SmaliParseException;
24+
import com.reandroid.dex.smali.SmaliParser;
25+
import com.reandroid.dex.smali.SmaliReader;
26+
import com.reandroid.dex.smali.SmaliWriter;
2127
import com.reandroid.utils.CompareUtil;
2228
import com.reandroid.utils.ObjectsUtil;
23-
import com.reandroid.utils.StringsUtil;
2429
import com.reandroid.utils.collection.ArrayCollection;
30+
import com.reandroid.utils.collection.CollectionUtil;
2531

26-
import java.util.*;
32+
import java.io.File;
33+
import java.io.IOException;
34+
import java.util.Collection;
35+
import java.util.Comparator;
36+
import java.util.HashMap;
37+
import java.util.HashSet;
38+
import java.util.Iterator;
39+
import java.util.List;
40+
import java.util.Map;
41+
import java.util.Set;
2742

28-
public abstract class Rename<T extends Key, R extends Key> {
43+
public abstract class Rename<T extends Key, R extends Key>
44+
implements SmaliFormat, SmaliParser {
2945

3046
private final Map<KeyPair<?, ?>, KeyPair<T, R>> keyPairMap;
3147
private final Map<KeyPair<?, ?>, KeyPair<T, R>> flippedKeyMap;
@@ -39,20 +55,35 @@ public Rename() {
3955
this.lockedFlippedKeys = new HashSet<>();
4056
}
4157

58+
public abstract void add(DexClassRepository classRepository, KeyPair<T, R> keyPair);
59+
60+
public void add(DexClassRepository classRepository, T search, R replace) {
61+
add(classRepository, new KeyPair<>(search, replace));
62+
}
4263
public void add(T search, R replace) {
4364
add(new KeyPair<>(search, replace));
4465
}
45-
public void add(KeyPair<T, R> keyPair){
66+
public void add(KeyPair<T, R> keyPair) {
4667
addToSet(keyPair);
4768
}
48-
public void addAll(Collection<KeyPair<T, R>> keyPairs){
69+
public void addAll(Collection<KeyPair<T, R>> keyPairs) {
4970
this.addAll(keyPairs.iterator());
5071
}
51-
public void addAll(Iterator<KeyPair<T, R>> iterator){
52-
while (iterator.hasNext()){
72+
public void addAll(Iterator<KeyPair<T, R>> iterator) {
73+
while (iterator.hasNext()) {
5374
add(iterator.next());
5475
}
5576
}
77+
public void addAll(DexClassRepository classRepository, Collection<KeyPair<T, R>> keyPairs) {
78+
if (keyPairs != null) {
79+
addAll(classRepository, keyPairs.iterator());
80+
}
81+
}
82+
public void addAll(DexClassRepository classRepository, Iterator<KeyPair<T, R>> iterator) {
83+
while (iterator.hasNext()) {
84+
add(classRepository, iterator.next());
85+
}
86+
}
5687
private void addToSet(KeyPair<T, R> keyPair) {
5788
if (keyPair == null || !keyPair.isValid()) {
5889
return;
@@ -108,9 +139,9 @@ private void lockKey(KeyPair<T, R> keyPair, KeyPair<R, T> flip) {
108139
lockKey(p2, p2.flip());
109140
}
110141
}
111-
public boolean isLocked(KeyPair<T, R> keyPair) {
142+
public boolean isLocked(KeyPair<?, ?> keyPair) {
112143
if (keyPair != null) {
113-
KeyPair<R, T> flip = keyPair.flip();
144+
KeyPair<?, ?> flip = keyPair.flip();
114145
return lockedKeys.contains(keyPair) ||
115146
lockedFlippedKeys.contains(keyPair) ||
116147
lockedKeys.contains(flip) ||
@@ -148,10 +179,27 @@ public void close() {
148179
lockedFlippedKeys.clear();
149180
}
150181

182+
public boolean isEmpty() {
183+
return size() == 0 && lockedSize() == 0;
184+
}
151185
public int size() {
152186
return keyPairMap.size();
153187
}
188+
public int lockedSize() {
189+
return lockedKeys.size();
190+
}
154191

192+
public boolean contains(KeyPair<?, ?> keyPair) {
193+
if (keyPair != null) {
194+
return contains(keyPair.getFirst()) ||
195+
contains(keyPair.getSecond()) ||
196+
isLocked(keyPair);
197+
}
198+
return false;
199+
}
200+
public boolean contains(Key key) {
201+
return get(key) != null || getFlipped(key) != null;
202+
}
155203
public KeyPair<T, R> get(Key search) {
156204
return keyPairMap.get(new KeyPair<>(search, null));
157205
}
@@ -211,13 +259,164 @@ protected boolean containsDeclaration(DexClassRepository classRepository, R repl
211259
}
212260

213261
public abstract int apply(DexClassRepository classRepository);
262+
public int apply(Rename<?, ?> rename) {
263+
if (this.isEmpty() || rename.isEmpty()) {
264+
return 0;
265+
}
266+
List<? extends KeyPair<?, ?>> list = rename.toList();
267+
int result = 0;
268+
for (KeyPair<?, ?> keyPair : list) {
269+
KeyPair<?, ?> renamedPair = apply(keyPair);
270+
if (renamedPair != keyPair) {
271+
rename.replace(keyPair, renamedPair);
272+
result ++;
273+
}
274+
}
275+
return result;
276+
}
277+
public KeyPair<?, ?> apply(KeyPair<?, ?> keyPair) {
278+
Key first = keyPair.getFirst();
279+
Key first2 = this.renameKey(first);
280+
Key second = keyPair.getSecond();
281+
Key second2 = this.renameKey(second);
282+
if (first != first2 || second != second2) {
283+
keyPair = new KeyPair<>(first2, second2);
284+
}
285+
return keyPair;
286+
}
287+
private void replace(KeyPair<?, ?> keyPair, KeyPair<?, ?> replace) {
288+
if (keyPairMap.remove(keyPair) != null) {
289+
flippedKeyMap.remove(keyPair.flip());
290+
add(ObjectsUtil.cast(replace));
291+
}
292+
}
293+
protected Key renameKey(Key key) {
294+
Iterator<? extends Key> iterator = CollectionUtil.uniqueOf(key.mentionedKeys());
295+
while (iterator.hasNext()) {
296+
Key mentioned = iterator.next();
297+
Key replace = getReplace(mentioned);
298+
if (replace != null) {
299+
key = key.replaceKey(mentioned, replace);
300+
}
301+
}
302+
return key;
303+
}
304+
public KeyPair<T, R> mergeRename(KeyPair<T, R> lower) {
305+
KeyPair<T, R> renamed = get(lower.getSecond());
306+
if (renamed != null && !renamed.equalsBoth(lower)) {
307+
return new KeyPair<>(lower.getFirst(), renamed.getSecond());
308+
}
309+
return lower;
310+
}
214311

215312
public Set<KeyPair<T, R>> getKeyPairSet() {
216313
return ObjectsUtil.cast(keyPairMap.keySet());
217314
}
218315

316+
public abstract SmaliDirective getSmaliDirective();
317+
318+
@Override
319+
public void append(SmaliWriter writer) throws IOException {
320+
SmaliDirective directive = getSmaliDirective();
321+
directive.append(writer);
322+
writer.appendComment("size = " + size());
323+
int locked = lockedSize();
324+
if (locked != 0) {
325+
writer.appendComment("locked = " + locked, false);
326+
}
327+
writer.indentPlus();
328+
List<KeyPair<T, R>> keyPairList = toList();
329+
for (KeyPair<T, R> keyPair : keyPairList) {
330+
writer.newLine();
331+
keyPair.append(writer);
332+
}
333+
List<KeyPair<T, R>> lockedKeyPairList = listLocked();
334+
for (KeyPair<T, R> keyPair : lockedKeyPairList) {
335+
writer.newLine();
336+
writer.appendComment(keyPair.toString());
337+
}
338+
writer.indentMinus();
339+
directive.appendEnd(writer);
340+
writer.newLine();
341+
}
342+
343+
@Override
344+
public void parse(SmaliReader reader) throws IOException {
345+
parse(null, reader);
346+
}
347+
public void parse(DexClassRepository classRepository, SmaliReader reader) throws IOException {
348+
reader.skipWhitespacesOrComment();
349+
if (reader.finished()) {
350+
return;
351+
}
352+
SmaliDirective directive = getSmaliDirective();
353+
if (directive.isEnd(reader)) {
354+
directive.skipEnd(reader);
355+
return;
356+
}
357+
SmaliParseException.expect(reader, directive);
358+
reader.skipWhitespacesOrComment();
359+
while (!directive.isEnd(reader)) {
360+
KeyPair<T, R> keyPair = KeyPair.read(directive, reader);
361+
if (classRepository == null) {
362+
add(keyPair);
363+
} else {
364+
add(classRepository, keyPair);
365+
}
366+
reader.skipWhitespacesOrComment();
367+
}
368+
SmaliParseException.expect(reader, directive, true);
369+
}
370+
371+
public void writeSmali(File file) throws IOException {
372+
SmaliWriter writer = new SmaliWriter();
373+
writer.setWriter(file);
374+
append(writer);
375+
writer.close();
376+
}
377+
378+
public String toSmaliString() {
379+
return SmaliWriter.toStringSafe(this);
380+
}
219381
@Override
220382
public String toString() {
221-
return StringsUtil.join(toList(), '\n');
383+
return size() + "/" + lockedSize();
384+
}
385+
386+
public static Rename<?, ?> read(SmaliReader reader) throws IOException {
387+
return read(null, null, reader);
388+
}
389+
public static Rename<?, ?> read(RenameFactory renameFactory, DexClassRepository classRepository, SmaliReader reader) throws IOException {
390+
if (renameFactory == null) {
391+
renameFactory = RenameFactory.DEFAULT_FACTORY;
392+
}
393+
reader.skipWhitespacesOrComment();
394+
SmaliDirective directive = SmaliDirective.parse(reader, false);
395+
if (directive == null) {
396+
throw new SmaliParseException(
397+
"Expecting rename directives (.class, .field, .method ...)", reader);
398+
}
399+
if (directive.isEnd(reader)) {
400+
throw new SmaliParseException("Unexpected end", reader);
401+
}
402+
Rename<?, ?> rename = renameFactory.createRename(directive);
403+
if (rename == null) {
404+
throw new SmaliParseException("Unknown rename directive", reader);
405+
}
406+
rename.parse(classRepository, reader);
407+
return rename;
408+
}
409+
410+
public static Rename<?, ?> createRenameFor(SmaliDirective directive) {
411+
if (directive == SmaliDirective.CLASS) {
412+
return new RenameTypes();
413+
}
414+
if (directive == SmaliDirective.FIELD) {
415+
return new RenameFields();
416+
}
417+
if (directive == SmaliDirective.METHOD) {
418+
return new RenameMethods();
419+
}
420+
return null;
222421
}
223422
}

0 commit comments

Comments
 (0)