WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content

Commit 681f8a9

Browse files
committed
fix: improve checks for restore new filled array (#2289)
1 parent 0b22523 commit 681f8a9

File tree

9 files changed

+125
-22
lines changed

9 files changed

+125
-22
lines changed

jadx-core/src/main/java/jadx/core/codegen/InsnGen.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -434,7 +434,7 @@ private void makeInsnBody(ICodeWriter code, InsnNode insn, Set<Flags> state) thr
434434
code.add(']');
435435
}
436436
int dim = arrayType.getArrayDimension();
437-
for (; k < dim - 1; k++) {
437+
for (; k < dim; k++) {
438438
code.add("[]");
439439
}
440440
break;

jadx-core/src/main/java/jadx/core/dex/attributes/nodes/CodeFeaturesAttr.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ public enum CodeFeature {
1414
* Code contains switch instruction
1515
*/
1616
SWITCH,
17+
18+
/**
19+
* Code contains new-array instruction
20+
*/
21+
NEW_ARRAY,
1722
}
1823

1924
public static boolean contains(MethodNode mth, CodeFeature feature) {

jadx-core/src/main/java/jadx/core/dex/instructions/InsnDecoder.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,7 @@ private InsnNode makeNewArray(InsnData insn) {
540540
for (int i = 1; i < regsCount; i++) {
541541
newArr.addArg(InsnArg.typeImmutableReg(insn, i, ArgType.INT));
542542
}
543+
CodeFeaturesAttr.add(method, CodeFeature.NEW_ARRAY);
543544
return newArr;
544545
}
545546

jadx-core/src/main/java/jadx/core/dex/instructions/args/InsnArg.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import jadx.core.dex.nodes.InsnNode;
1414
import jadx.core.dex.nodes.MethodNode;
1515
import jadx.core.utils.InsnRemover;
16+
import jadx.core.utils.InsnUtils;
1617
import jadx.core.utils.exceptions.JadxRuntimeException;
1718

1819
/**
@@ -301,6 +302,10 @@ public boolean isSameCodeVar(RegisterArg arg) {
301302
return false;
302303
}
303304

305+
public boolean isUseVar(RegisterArg arg) {
306+
return InsnUtils.containsVar(this, arg);
307+
}
308+
304309
protected final <T extends InsnArg> T copyCommonParams(T copy) {
305310
copy.copyAttributesFrom(this);
306311
copy.setParentInsn(parentInsn);

jadx-core/src/main/java/jadx/core/dex/visitors/ReplaceNewArray.java

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
package jadx.core.dex.visitors;
22

3-
import java.util.HashSet;
3+
import java.util.ArrayList;
4+
import java.util.Collections;
5+
import java.util.IdentityHashMap;
46
import java.util.List;
57
import java.util.Map;
8+
import java.util.Set;
69
import java.util.SortedMap;
710
import java.util.TreeMap;
811

912
import jadx.core.dex.attributes.AFlag;
13+
import jadx.core.dex.attributes.nodes.CodeFeaturesAttr;
14+
import jadx.core.dex.attributes.nodes.CodeFeaturesAttr.CodeFeature;
1015
import jadx.core.dex.instructions.FilledNewArrayNode;
1116
import jadx.core.dex.instructions.IndexInsnNode;
1217
import jadx.core.dex.instructions.InsnType;
@@ -35,21 +40,20 @@ public class ReplaceNewArray extends AbstractVisitor {
3540

3641
@Override
3742
public void visit(MethodNode mth) throws JadxException {
38-
if (mth.isNoCode()) {
43+
if (!CodeFeaturesAttr.contains(mth, CodeFeature.NEW_ARRAY)) {
3944
return;
4045
}
46+
InsnRemover remover = new InsnRemover(mth);
4147
int k = 0;
4248
while (true) {
4349
boolean changed = false;
44-
InsnRemover remover = new InsnRemover(mth);
4550
for (BlockNode block : mth.getBasicBlocks()) {
46-
remover.setBlock(block);
47-
List<InsnNode> instructions = block.getInstructions();
48-
int size = instructions.size();
51+
List<InsnNode> insnList = block.getInstructions();
52+
int size = insnList.size();
4953
for (int i = 0; i < size; i++) {
50-
changed |= processInsn(mth, instructions, i, remover);
54+
changed |= processInsn(mth, insnList, i, remover);
5155
}
52-
remover.perform();
56+
remover.performForBlock(block);
5357
}
5458
if (changed) {
5559
CodeShrinkVisitor.shrinkMethod(mth);
@@ -107,12 +111,11 @@ private static boolean processNewArray(MethodNode mth,
107111
SortedMap<Long, InsnNode> arrPuts = new TreeMap<>();
108112
for (RegisterArg registerArg : useList) {
109113
InsnNode parentInsn = registerArg.getParentInsn();
110-
if (parentInsn == null || parentInsn.getType() != InsnType.APUT) {
114+
if (parentInsn == null
115+
|| parentInsn.getType() != InsnType.APUT
116+
|| !arrArg.sameRegAndSVar(parentInsn.getArg(0))) {
111117
continue;
112118
}
113-
if (!arrArg.sameRegAndSVar(parentInsn.getArg(0))) {
114-
return false;
115-
}
116119
Object constVal = InsnUtils.getConstValueByArg(mth.root(), parentInsn.getArg(1));
117120
if (!(constVal instanceof LiteralArg)) {
118121
return false;
@@ -130,8 +133,7 @@ private static boolean processNewArray(MethodNode mth,
130133
if (arrPuts.size() < minLen) {
131134
return false;
132135
}
133-
// expect all puts to be in same block
134-
if (!new HashSet<>(instructions).containsAll(arrPuts.values())) {
136+
if (!verifyPutInsns(arrArg, instructions, arrPuts)) {
135137
return false;
136138
}
137139

@@ -165,6 +167,28 @@ private static boolean processNewArray(MethodNode mth,
165167
return true;
166168
}
167169

170+
private static boolean verifyPutInsns(RegisterArg arrReg, List<InsnNode> insnList, SortedMap<Long, InsnNode> arrPuts) {
171+
List<InsnNode> puts = new ArrayList<>(arrPuts.values());
172+
int putsCount = puts.size();
173+
// expect all puts to be in the same block
174+
if (insnList.size() < putsCount) {
175+
return false;
176+
}
177+
Set<InsnNode> insnSet = Collections.newSetFromMap(new IdentityHashMap<>());
178+
insnSet.addAll(insnList);
179+
if (!insnSet.containsAll(puts)) {
180+
return false;
181+
}
182+
// array arg shouldn't be used in puts insns
183+
for (InsnNode put : puts) {
184+
InsnArg putArg = put.getArg(2);
185+
if (putArg.isUseVar(arrReg)) {
186+
return false;
187+
}
188+
}
189+
return true;
190+
}
191+
168192
private static InsnArg replaceConstInArg(MethodNode mth, InsnArg valueArg) {
169193
if (valueArg.isLiteral()) {
170194
IFieldInfoRef f = mth.getParentClass().getConstFieldByLiteralArg((LiteralArg) valueArg);

jadx-core/src/main/java/jadx/core/utils/InsnRemover.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import java.util.ArrayList;
44
import java.util.Iterator;
55
import java.util.List;
6+
import java.util.Objects;
67
import java.util.stream.Collectors;
78

89
import org.jetbrains.annotations.Nullable;
@@ -75,6 +76,16 @@ public void perform() {
7576
toRemove.clear();
7677
}
7778

79+
public void performForBlock(BlockNode block) {
80+
if (toRemove.isEmpty()) {
81+
return;
82+
}
83+
instrList = Objects.requireNonNull(block.getInstructions());
84+
unbindInsns(mth, toRemove);
85+
removeAll(instrList, toRemove);
86+
toRemove.clear();
87+
}
88+
7889
public static void unbindInsn(@Nullable MethodNode mth, InsnNode insn) {
7990
unbindAllArgs(mth, insn);
8091
unbindResult(mth, insn);

jadx-core/src/main/java/jadx/core/utils/InsnUtils.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,36 @@ public static <T extends InsnArg> boolean containsVar(List<T> list, RegisterArg
263263
return false;
264264
}
265265

266+
public static boolean containsVar(InsnNode insn, RegisterArg arg) {
267+
if (insn == null) {
268+
return false;
269+
}
270+
RegisterArg result = insn.getResult();
271+
if (result != null && result.sameRegAndSVar(arg)) {
272+
return true;
273+
}
274+
if (insn.getArgsCount() == 0) {
275+
return false;
276+
}
277+
for (InsnArg insnArg : insn.getArguments()) {
278+
if (containsVar(insnArg, arg)) {
279+
return true;
280+
}
281+
}
282+
return false;
283+
}
284+
285+
public static boolean containsVar(InsnArg insnArg, RegisterArg arg) {
286+
if (insnArg.isRegister()) {
287+
return ((RegisterArg) insnArg).sameRegAndSVar(arg);
288+
}
289+
if (insnArg.isInsnWrap()) {
290+
InsnNode wrapInsn = ((InsnWrapArg) insnArg).getWrapInsn();
291+
return containsVar(wrapInsn, arg);
292+
}
293+
return false;
294+
}
295+
266296
public static boolean contains(InsnNode insn, AFlag flag) {
267297
return insn != null && insn.contains(flag);
268298
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package jadx.tests.integration.arrays;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import jadx.tests.api.IntegrationTest;
6+
7+
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
8+
9+
public class TestArrayFillNegative extends IntegrationTest {
10+
11+
public static class TestCls {
12+
public int[] test() {
13+
int[] arr = new int[3];
14+
arr[0] = 1;
15+
arr[1] = arr[0] + 1;
16+
arr[2] = arr[1] + 1;
17+
return arr;
18+
}
19+
20+
public void check() {
21+
assertThat(test()).isEqualTo(new int[] { 1, 2, 3 });
22+
}
23+
}
24+
25+
@Test
26+
public void test() {
27+
assertThat(getClassNode(TestCls.class))
28+
.code()
29+
.doesNotContain("int[] arr = {1, ")
30+
.containsOne("int[] arr = new int[3];")
31+
.containsOne("arr[1] = arr[0] + 1;");
32+
}
33+
}

jadx-core/src/test/java/jadx/tests/integration/arrays/TestMultiDimArrayFill.java

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,7 @@ public static class TestCls {
1111

1212
public static Obj test(int a, int b) {
1313
return new Obj(
14-
new int[][] {
15-
new int[] { 1 },
16-
new int[] { 2 },
17-
{ 3 },
18-
new int[] { 4, 5 },
19-
new int[0]
20-
},
14+
new int[][] { { 1 }, { 2 }, { 3 }, { 4, 5 }, new int[0] },
2115
new int[] { a, a, a, a, b });
2216
}
2317

0 commit comments

Comments
 (0)