Commit 98e4c4b4 authored by Skylot's avatar Skylot

fix: merge new-array and fill-array-data with move between (#462)

parent 9d5dda12
...@@ -50,6 +50,7 @@ import jadx.core.dex.nodes.FieldNode; ...@@ -50,6 +50,7 @@ import jadx.core.dex.nodes.FieldNode;
import jadx.core.dex.nodes.InsnNode; import jadx.core.dex.nodes.InsnNode;
import jadx.core.dex.nodes.MethodNode; import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.nodes.RootNode; import jadx.core.dex.nodes.RootNode;
import jadx.core.utils.DebugUtils;
import jadx.core.utils.RegionUtils; import jadx.core.utils.RegionUtils;
import jadx.core.utils.exceptions.CodegenException; import jadx.core.utils.exceptions.CodegenException;
import jadx.core.utils.exceptions.JadxRuntimeException; import jadx.core.utils.exceptions.JadxRuntimeException;
...@@ -211,12 +212,14 @@ public class InsnGen { ...@@ -211,12 +212,14 @@ public class InsnGen {
makeInsn(insn, code, null); makeInsn(insn, code, null);
} }
private static final Set<Flags> EMPTY_FLAGS = EnumSet.noneOf(Flags.class);
private static final Set<Flags> BODY_ONLY_FLAG = EnumSet.of(Flags.BODY_ONLY);
private static final Set<Flags> BODY_ONLY_NOWRAP_FLAGS = EnumSet.of(Flags.BODY_ONLY_NOWRAP);
protected void makeInsn(InsnNode insn, CodeWriter code, Flags flag) throws CodegenException { protected void makeInsn(InsnNode insn, CodeWriter code, Flags flag) throws CodegenException {
try { try {
Set<Flags> state = EnumSet.noneOf(Flags.class);
if (flag == Flags.BODY_ONLY || flag == Flags.BODY_ONLY_NOWRAP) { if (flag == Flags.BODY_ONLY || flag == Flags.BODY_ONLY_NOWRAP) {
state.add(flag); makeInsnBody(code, insn, flag == Flags.BODY_ONLY ? BODY_ONLY_FLAG : BODY_ONLY_NOWRAP_FLAGS);
makeInsnBody(code, insn, state);
} else { } else {
if (flag != Flags.INLINE) { if (flag != Flags.INLINE) {
code.startLineWithNum(insn.getSourceLine()); code.startLineWithNum(insn.getSourceLine());
...@@ -229,7 +232,7 @@ public class InsnGen { ...@@ -229,7 +232,7 @@ public class InsnGen {
code.add(" = "); code.add(" = ");
} }
} }
makeInsnBody(code, insn, state); makeInsnBody(code, insn, EMPTY_FLAGS);
if (flag != Flags.INLINE) { if (flag != Flags.INLINE) {
code.add(';'); code.add(';');
} }
...@@ -373,6 +376,17 @@ public class InsnGen { ...@@ -373,6 +376,17 @@ public class InsnGen {
filledNewArray((FilledNewArrayNode) insn, code); filledNewArray((FilledNewArrayNode) insn, code);
break; break;
case FILL_ARRAY:
FillArrayNode arrayNode = (FillArrayNode) insn;
if (fallback) {
String arrStr = arrayNode.dataToString();
addArg(code, insn.getArg(0));
code.add(" = {").add(arrStr.substring(1, arrStr.length() - 1)).add("} // fill-array");
} else {
fillArray(code, arrayNode);
}
break;
case AGET: case AGET:
addArg(code, insn.getArg(0)); addArg(code, insn.getArg(0));
code.add('['); code.add('[');
...@@ -491,13 +505,6 @@ public class InsnGen { ...@@ -491,13 +505,6 @@ public class InsnGen {
code.startLine('}'); code.startLine('}');
break; break;
case FILL_ARRAY:
fallbackOnlyInsn(insn);
FillArrayNode arrayNode = (FillArrayNode) insn;
String arrStr = arrayNode.dataToString();
code.add('{').add(arrStr.substring(1, arrStr.length() - 1)).add('}');
break;
case NEW_INSTANCE: case NEW_INSTANCE:
// only fallback - make new instance in constructor invoke // only fallback - make new instance in constructor invoke
fallbackOnlyInsn(insn); fallbackOnlyInsn(insn);
...@@ -519,6 +526,26 @@ public class InsnGen { ...@@ -519,6 +526,26 @@ public class InsnGen {
} }
} }
/**
* In most cases must be combined with new array instructions.
* Use one by one array fill (can be replaced with System.arrayCopy)
*/
private void fillArray(CodeWriter code, FillArrayNode arrayNode) throws CodegenException {
code.add("// fill-array-data instruction");
code.startLine();
List<LiteralArg> args = arrayNode.getLiteralArgs(arrayNode.getElementType());
InsnArg arrArg = arrayNode.getArg(0);
int len = args.size();
for (int i = 0; i < len; i++) {
if (i != 0) {
code.add(';');
code.startLine();
}
addArg(code, arrArg);
code.add('[').add(Integer.toString(i)).add("] = ").add(lit(args.get(i)));
}
}
private void oneArgInsn(CodeWriter code, InsnNode insn, Set<Flags> state, char op) throws CodegenException { private void oneArgInsn(CodeWriter code, InsnNode insn, Set<Flags> state, char op) throws CodegenException {
boolean wrap = state.contains(Flags.BODY_ONLY); boolean wrap = state.contains(Flags.BODY_ONLY);
if (wrap) { if (wrap) {
...@@ -533,6 +560,7 @@ public class InsnGen { ...@@ -533,6 +560,7 @@ public class InsnGen {
private void fallbackOnlyInsn(InsnNode insn) throws CodegenException { private void fallbackOnlyInsn(InsnNode insn) throws CodegenException {
if (!fallback) { if (!fallback) {
DebugUtils.dump(mth, "fallback");
throw new CodegenException(insn.getType() + " can be used only in fallback mode"); throw new CodegenException(insn.getType() + " can be used only in fallback mode");
} }
} }
......
...@@ -8,9 +8,7 @@ import org.slf4j.Logger; ...@@ -8,9 +8,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import jadx.core.dex.attributes.AFlag; import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.nodes.DexNode;
import jadx.core.dex.nodes.InsnNode; import jadx.core.dex.nodes.InsnNode;
import jadx.core.utils.InsnUtils;
import jadx.core.utils.exceptions.JadxRuntimeException; import jadx.core.utils.exceptions.JadxRuntimeException;
public class RegisterArg extends InsnArg implements Named { public class RegisterArg extends InsnArg implements Named {
...@@ -121,17 +119,6 @@ public class RegisterArg extends InsnArg implements Named { ...@@ -121,17 +119,6 @@ public class RegisterArg extends InsnArg implements Named {
return dup; return dup;
} }
/**
* Return constant value from register assign or null if not constant
*/
public Object getConstValue(DexNode dex) {
InsnNode parInsn = getAssignInsn();
if (parInsn == null) {
return null;
}
return InsnUtils.getConstValueByInsn(dex, parInsn);
}
@Nullable @Nullable
public InsnNode getAssignInsn() { public InsnNode getAssignInsn() {
if (sVar == null) { if (sVar == null) {
......
...@@ -5,6 +5,7 @@ import java.util.LinkedHashMap; ...@@ -5,6 +5,7 @@ import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
...@@ -116,15 +117,14 @@ public class ModVisitor extends AbstractVisitor { ...@@ -116,15 +117,14 @@ public class ModVisitor extends AbstractVisitor {
case NEW_ARRAY: case NEW_ARRAY:
// replace with filled array if 'fill-array' is next instruction // replace with filled array if 'fill-array' is next instruction
int next = i + 1; NewArrayNode newArrInsn = (NewArrayNode) insn;
if (next < size) { InsnNode nextInsn = getFirstUseSkipMove(insn.getResult());
InsnNode ni = block.getInstructions().get(next); if (nextInsn != null && nextInsn.getType() == InsnType.FILL_ARRAY) {
if (ni.getType() == InsnType.FILL_ARRAY) { FillArrayNode fillArrInsn = (FillArrayNode) nextInsn;
InsnNode filledArr = makeFilledArrayInsn(mth, (NewArrayNode) insn, (FillArrayNode) ni); if (checkArrSizes(mth, newArrInsn, fillArrInsn)) {
if (filledArr != null) { InsnNode filledArr = makeFilledArrayInsn(mth, newArrInsn, fillArrInsn);
replaceInsn(block, i, filledArr); replaceInsn(block, i, filledArr);
remover.addAndUnbind(ni); remover.addAndUnbind(nextInsn);
}
} }
} }
break; break;
...@@ -168,7 +168,8 @@ public class ModVisitor extends AbstractVisitor { ...@@ -168,7 +168,8 @@ public class ModVisitor extends AbstractVisitor {
IfCondition condition = IfCondition.fromIfNode(ifNode); IfCondition condition = IfCondition.fromIfNode(ifNode);
InsnArg zero = new LiteralArg(0, type); InsnArg zero = new LiteralArg(0, type);
InsnArg one = new LiteralArg( InsnArg one = new LiteralArg(
type == ArgType.DOUBLE ? Double.doubleToLongBits(1) type == ArgType.DOUBLE
? Double.doubleToLongBits(1)
: type == ArgType.FLOAT ? Float.floatToIntBits(1) : 1, : type == ArgType.FLOAT ? Float.floatToIntBits(1) : 1,
type); type);
TernaryInsn ternary = new TernaryInsn(condition, insn.getResult(), one, zero); TernaryInsn ternary = new TernaryInsn(condition, insn.getResult(), one, zero);
...@@ -185,6 +186,17 @@ public class ModVisitor extends AbstractVisitor { ...@@ -185,6 +186,17 @@ public class ModVisitor extends AbstractVisitor {
} }
} }
private static boolean checkArrSizes(MethodNode mth, NewArrayNode newArrInsn, FillArrayNode fillArrInsn) {
int dataSize = fillArrInsn.getSize();
InsnArg arrSizeArg = newArrInsn.getArg(0);
Object value = InsnUtils.getConstValueByArg(mth.dex(), arrSizeArg);
if (value instanceof LiteralArg) {
long literal = ((LiteralArg) value).getLiteral();
return dataSize == (int) literal;
}
return false;
}
private static boolean isCastDuplicate(IndexInsnNode castInsn) { private static boolean isCastDuplicate(IndexInsnNode castInsn) {
InsnArg arg = castInsn.getArg(0); InsnArg arg = castInsn.getArg(0);
if (arg.isRegister()) { if (arg.isRegister()) {
...@@ -314,6 +326,28 @@ public class ModVisitor extends AbstractVisitor { ...@@ -314,6 +326,28 @@ public class ModVisitor extends AbstractVisitor {
return parentInsn; return parentInsn;
} }
/**
* Return first usage instruction for arg.
* If used only once try to follow move chain
*/
@Nullable
private static InsnNode getFirstUseSkipMove(RegisterArg arg) {
SSAVar sVar = arg.getSVar();
int useCount = sVar.getUseCount();
if (useCount == 0) {
return null;
}
RegisterArg useArg = sVar.getUseList().get(0);
InsnNode parentInsn = useArg.getParentInsn();
if (parentInsn == null) {
return null;
}
if (useCount == 1 && parentInsn.getType() == InsnType.MOVE) {
return getFirstUseSkipMove(parentInsn.getResult());
}
return parentInsn;
}
private static InsnNode makeFilledArrayInsn(MethodNode mth, NewArrayNode newArrayNode, FillArrayNode insn) { private static InsnNode makeFilledArrayInsn(MethodNode mth, NewArrayNode newArrayNode, FillArrayNode insn) {
ArgType insnArrayType = newArrayNode.getArrayType(); ArgType insnArrayType = newArrayNode.getArrayType();
ArgType insnElementType = insnArrayType.getArrayElement(); ArgType insnElementType = insnArrayType.getArrayElement();
......
...@@ -155,27 +155,12 @@ public class ReSugarCode extends AbstractVisitor { ...@@ -155,27 +155,12 @@ public class ReSugarCode extends AbstractVisitor {
return false; return false;
} }
InsnArg indexArg = insn.getArg(1); InsnArg indexArg = insn.getArg(1);
int index = -1; Object value = InsnUtils.getConstValueByArg(mth.dex(), indexArg);
if (indexArg.isLiteral()) { if (value instanceof LiteralArg) {
index = (int) ((LiteralArg) indexArg).getLiteral(); int index = (int) ((LiteralArg) value).getLiteral();
} else if (indexArg.isRegister()) {
RegisterArg reg = (RegisterArg) indexArg;
index = getIntConst(reg.getConstValue(mth.dex()));
} else if (indexArg.isInsnWrap()) {
InsnNode constInsn = ((InsnWrapArg) indexArg).getWrapInsn();
index = getIntConst(InsnUtils.getConstValueByInsn(mth.dex(), constInsn));
}
return index == putIndex; return index == putIndex;
} }
return false;
private static int getIntConst(Object value) {
if (value instanceof Integer) {
return (Integer) value;
}
if (value instanceof Long) {
return ((Long) value).intValue();
}
return -1;
} }
private static void processEnumSwitch(MethodNode mth, SwitchNode insn) { private static void processEnumSwitch(MethodNode mth, SwitchNode insn) {
......
...@@ -12,6 +12,9 @@ import jadx.core.dex.instructions.ConstClassNode; ...@@ -12,6 +12,9 @@ import jadx.core.dex.instructions.ConstClassNode;
import jadx.core.dex.instructions.ConstStringNode; import jadx.core.dex.instructions.ConstStringNode;
import jadx.core.dex.instructions.IndexInsnNode; import jadx.core.dex.instructions.IndexInsnNode;
import jadx.core.dex.instructions.InsnType; import jadx.core.dex.instructions.InsnType;
import jadx.core.dex.instructions.args.InsnArg;
import jadx.core.dex.instructions.args.InsnWrapArg;
import jadx.core.dex.instructions.args.RegisterArg;
import jadx.core.dex.nodes.DexNode; import jadx.core.dex.nodes.DexNode;
import jadx.core.dex.nodes.FieldNode; import jadx.core.dex.nodes.FieldNode;
import jadx.core.dex.nodes.InsnNode; import jadx.core.dex.nodes.InsnNode;
...@@ -64,6 +67,33 @@ public class InsnUtils { ...@@ -64,6 +67,33 @@ public class InsnUtils {
} }
/** /**
* Search constant assigned to provided arg.
*
* @return LiteralArg, String, ArgType or null
*/
public static Object getConstValueByArg(DexNode dex, InsnArg arg) {
if (arg.isLiteral()) {
return arg;
}
if (arg.isRegister()) {
RegisterArg reg = (RegisterArg) arg;
InsnNode parInsn = reg.getAssignInsn();
if (parInsn == null) {
return null;
}
if (parInsn.getType() == InsnType.MOVE) {
return getConstValueByArg(dex, parInsn.getArg(0));
}
return getConstValueByInsn(dex, parInsn);
}
if (arg.isInsnWrap()) {
InsnNode insn = ((InsnWrapArg) arg).getWrapInsn();
return getConstValueByInsn(dex, insn);
}
return null;
}
/**
* Return constant value from insn or null if not constant. * Return constant value from insn or null if not constant.
* *
* @return LiteralArg, String, ArgType or null * @return LiteralArg, String, ArgType or null
......
package jadx.tests.integration.arrays;
import org.junit.jupiter.api.Test;
import jadx.core.dex.nodes.ClassNode;
import jadx.tests.api.SmaliTest;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.not;
public class TestArrayFillWithMove extends SmaliTest {
@Test
public void test() {
ClassNode cls = getClassNodeFromSmaliFiles("TestCls");
String code = cls.getCode().toString();
assertThat(code, not(containsString("// fill-array-data instruction")));
assertThat(code, not(containsString("arr[0] = 0;")));
assertThat(code, containsString("return new long[]{0, 1}"));
}
}
package jadx.tests.integration.arrays;
import org.junit.jupiter.api.Test;
import jadx.core.dex.nodes.ClassNode;
import jadx.tests.api.SmaliTest;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat;
public class TestFillArrayData extends SmaliTest {
@Test
public void test() {
ClassNode cls = getClassNodeFromSmaliFiles("TestCls");
String code = cls.getCode().toString();
assertThat(code, containsString("jArr[0] = 1;"));
assertThat(code, containsString("jArr[1] = 2;"));
}
}
.class public Larrays/TestCls;
.super Ljava/lang/Object;
.method public test()[J
.registers 4
const/16 v3, 0x2
move/from16 v0, v3
new-array v0, v0, [J
move-object/from16 v1, v0
fill-array-data v1, :array_0
.local v1, "arr":[J
return v1
:array_0
.array-data 8
0x0
0x1
.end array-data
.end method
.class public Larrays/TestCls;
.super Ljava/lang/Object;
.method public test([J)V
.registers 2
fill-array-data p1, :array_0
return-void
:array_0
.array-data 8
0x1
0x2
.end array-data
.end method
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment