core: refactor fill-array instruction processing and constants replace (fix #48)
This commit is contained in:
@@ -13,6 +13,7 @@ import jadx.core.dex.instructions.ArithOp;
|
||||
import jadx.core.dex.instructions.ConstClassNode;
|
||||
import jadx.core.dex.instructions.ConstStringNode;
|
||||
import jadx.core.dex.instructions.FillArrayNode;
|
||||
import jadx.core.dex.instructions.FilledNewArrayNode;
|
||||
import jadx.core.dex.instructions.GotoNode;
|
||||
import jadx.core.dex.instructions.IfNode;
|
||||
import jadx.core.dex.instructions.IndexInsnNode;
|
||||
@@ -34,14 +35,13 @@ import jadx.core.dex.nodes.FieldNode;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.dex.nodes.RootNode;
|
||||
import jadx.core.utils.ErrorsCounter;
|
||||
import jadx.core.utils.InsnUtils;
|
||||
import jadx.core.utils.RegionUtils;
|
||||
import jadx.core.utils.StringUtils;
|
||||
import jadx.core.utils.exceptions.CodegenException;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
@@ -359,12 +359,8 @@ public class InsnGen {
|
||||
code.add(".length");
|
||||
break;
|
||||
|
||||
case FILL_ARRAY:
|
||||
fillArray((FillArrayNode) insn, code);
|
||||
break;
|
||||
|
||||
case FILLED_NEW_ARRAY:
|
||||
filledNewArray(insn, code);
|
||||
filledNewArray((FilledNewArrayNode) insn, code);
|
||||
break;
|
||||
|
||||
case AGET:
|
||||
@@ -490,6 +486,25 @@ public class InsnGen {
|
||||
code.startLine('}');
|
||||
break;
|
||||
|
||||
case FILL_ARRAY:
|
||||
assert isFallback();
|
||||
FillArrayNode arrayNode = (FillArrayNode) insn;
|
||||
Object data = arrayNode.getData();
|
||||
String arrStr;
|
||||
if (data instanceof int[]) {
|
||||
arrStr = Arrays.toString((int[]) data);
|
||||
} else if (data instanceof short[]) {
|
||||
arrStr = Arrays.toString((short[]) data);
|
||||
} else if (data instanceof byte[]) {
|
||||
arrStr = Arrays.toString((byte[]) data);
|
||||
} else if (data instanceof long[]) {
|
||||
arrStr = Arrays.toString((long[]) data);
|
||||
} else {
|
||||
arrStr = "?";
|
||||
}
|
||||
code.add('{').add(arrStr.substring(1, arrStr.length() - 1)).add('}');
|
||||
break;
|
||||
|
||||
case NEW_INSTANCE:
|
||||
// only fallback - make new instance in constructor invoke
|
||||
assert isFallback();
|
||||
@@ -501,11 +516,11 @@ public class InsnGen {
|
||||
}
|
||||
}
|
||||
|
||||
private void filledNewArray(InsnNode insn, CodeWriter code) throws CodegenException {
|
||||
int c = insn.getArgsCount();
|
||||
private void filledNewArray(FilledNewArrayNode insn, CodeWriter code) throws CodegenException {
|
||||
code.add("new ");
|
||||
useType(code, insn.getResult().getType());
|
||||
useType(code, insn.getArrayType());
|
||||
code.add('{');
|
||||
int c = insn.getArgsCount();
|
||||
for (int i = 0; i < c; i++) {
|
||||
addArg(code, insn.getArg(i), false);
|
||||
if (i + 1 < c) {
|
||||
@@ -515,76 +530,6 @@ public class InsnGen {
|
||||
code.add('}');
|
||||
}
|
||||
|
||||
private void fillArray(FillArrayNode insn, CodeWriter code) throws CodegenException {
|
||||
String filledArray = makeArrayElements(insn);
|
||||
code.add("new ");
|
||||
useType(code, insn.getElementType());
|
||||
code.add("[]{").add(filledArray).add('}');
|
||||
}
|
||||
|
||||
private String makeArrayElements(FillArrayNode insn) throws CodegenException {
|
||||
ArgType insnArrayType = insn.getResult().getType();
|
||||
ArgType insnElementType = insnArrayType.getArrayElement();
|
||||
ArgType elType = insn.getElementType();
|
||||
if (!elType.equals(insnElementType) && !insnArrayType.equals(ArgType.OBJECT)) {
|
||||
ErrorsCounter.methodError(mth,
|
||||
"Incorrect type for fill-array insn " + InsnUtils.formatOffset(insn.getOffset())
|
||||
+ ", element type: " + elType + ", insn element type: " + insnElementType
|
||||
);
|
||||
}
|
||||
if (!elType.isTypeKnown()) {
|
||||
LOG.warn("Unknown array element type: {} in mth: {}", elType, mth);
|
||||
elType = insnElementType.isTypeKnown() ? insnElementType : elType.selectFirst();
|
||||
if (elType == null) {
|
||||
throw new JadxRuntimeException("Null array element type");
|
||||
}
|
||||
}
|
||||
insn.mergeElementType(elType);
|
||||
|
||||
StringBuilder str = new StringBuilder();
|
||||
Object data = insn.getData();
|
||||
switch (elType.getPrimitiveType()) {
|
||||
case BOOLEAN:
|
||||
case BYTE:
|
||||
byte[] array = (byte[]) data;
|
||||
for (byte b : array) {
|
||||
str.append(TypeGen.literalToString(b, elType));
|
||||
str.append(", ");
|
||||
}
|
||||
break;
|
||||
case SHORT:
|
||||
case CHAR:
|
||||
short[] sarray = (short[]) data;
|
||||
for (short b : sarray) {
|
||||
str.append(TypeGen.literalToString(b, elType));
|
||||
str.append(", ");
|
||||
}
|
||||
break;
|
||||
case INT:
|
||||
case FLOAT:
|
||||
int[] iarray = (int[]) data;
|
||||
for (int b : iarray) {
|
||||
str.append(TypeGen.literalToString(b, elType));
|
||||
str.append(", ");
|
||||
}
|
||||
break;
|
||||
case LONG:
|
||||
case DOUBLE:
|
||||
long[] larray = (long[]) data;
|
||||
for (long b : larray) {
|
||||
str.append(TypeGen.literalToString(b, elType));
|
||||
str.append(", ");
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new CodegenException(mth, "Unknown type: " + elType);
|
||||
}
|
||||
int len = str.length();
|
||||
str.delete(len - 2, len);
|
||||
return str.toString();
|
||||
}
|
||||
|
||||
private void makeConstructor(ConstructorInsn insn, CodeWriter code)
|
||||
throws CodegenException {
|
||||
ClassNode cls = mth.dex().resolveClass(insn.getClassType());
|
||||
@@ -734,9 +679,6 @@ public class InsnGen {
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} else if (insn.getType() == InsnType.FILL_ARRAY) {
|
||||
code.add(makeArrayElements((FillArrayNode) insn));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -115,9 +115,7 @@ public class NameGen {
|
||||
}
|
||||
|
||||
private String getFallbackName(RegisterArg arg) {
|
||||
String name = arg.getName();
|
||||
String base = "r" + arg.getRegNum();
|
||||
return name != null ? base + "_" + name : base;
|
||||
return "r" + arg.getRegNum();
|
||||
}
|
||||
|
||||
private static String makeNameForType(ArgType type) {
|
||||
|
||||
@@ -2,15 +2,20 @@ package jadx.core.dex.instructions;
|
||||
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.instructions.args.InsnArg;
|
||||
import jadx.core.dex.instructions.args.LiteralArg;
|
||||
import jadx.core.dex.instructions.args.PrimitiveType;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.android.dx.io.instructions.FillArrayDataPayloadDecodedInstruction;
|
||||
|
||||
public final class FillArrayNode extends InsnNode {
|
||||
|
||||
private final Object data;
|
||||
private final int size;
|
||||
private ArgType elemType;
|
||||
|
||||
public FillArrayNode(int resReg, FillArrayDataPayloadDecodedInstruction payload) {
|
||||
@@ -36,6 +41,7 @@ public final class FillArrayNode extends InsnNode {
|
||||
setResult(InsnArg.reg(resReg, ArgType.array(elType)));
|
||||
|
||||
this.data = payload.getData();
|
||||
this.size = payload.getSize();
|
||||
this.elemType = elType;
|
||||
}
|
||||
|
||||
@@ -43,6 +49,10 @@ public final class FillArrayNode extends InsnNode {
|
||||
return data;
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public ArgType getElementType() {
|
||||
return elemType;
|
||||
}
|
||||
@@ -54,6 +64,31 @@ public final class FillArrayNode extends InsnNode {
|
||||
}
|
||||
}
|
||||
|
||||
public List<LiteralArg> getLiteralArgs() {
|
||||
List<LiteralArg> list = new ArrayList<LiteralArg>(size);
|
||||
Object array = data;
|
||||
if (array instanceof int[]) {
|
||||
for (int b : (int[]) array) {
|
||||
list.add(InsnArg.lit(b, elemType));
|
||||
}
|
||||
} else if (array instanceof byte[]) {
|
||||
for (byte b : (byte[]) array) {
|
||||
list.add(InsnArg.lit(b, elemType));
|
||||
}
|
||||
} else if (array instanceof short[]) {
|
||||
for (short b : (short[]) array) {
|
||||
list.add(InsnArg.lit(b, elemType));
|
||||
}
|
||||
} else if (array instanceof long[]) {
|
||||
for (long b : (long[]) array) {
|
||||
list.add(InsnArg.lit(b, elemType));
|
||||
}
|
||||
} else {
|
||||
throw new JadxRuntimeException("Unknown type: " + data.getClass() + ", expected: " + elemType);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSame(InsnNode obj) {
|
||||
if (this == obj) {
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package jadx.core.dex.instructions;
|
||||
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
|
||||
public class FilledNewArrayNode extends InsnNode {
|
||||
|
||||
private final ArgType elemType;
|
||||
|
||||
public FilledNewArrayNode(ArgType elemType, int size) {
|
||||
super(InsnType.FILLED_NEW_ARRAY, size);
|
||||
this.elemType = elemType;
|
||||
}
|
||||
|
||||
public ArgType getElemType() {
|
||||
return elemType;
|
||||
}
|
||||
|
||||
public ArgType getArrayType() {
|
||||
return ArgType.array(elemType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSame(InsnNode obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof FilledNewArrayNode) || !super.isSame(obj)) {
|
||||
return false;
|
||||
}
|
||||
FilledNewArrayNode other = (FilledNewArrayNode) obj;
|
||||
return elemType == other.elemType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + " elemType: " + elemType;
|
||||
}
|
||||
}
|
||||
@@ -620,9 +620,14 @@ public class InsnDecoder {
|
||||
regs[i] = InsnArg.reg(regNum, elType, typeImmutable);
|
||||
}
|
||||
}
|
||||
return insn(InsnType.FILLED_NEW_ARRAY,
|
||||
resReg == -1 ? null : InsnArg.reg(resReg, arrType),
|
||||
regs);
|
||||
InsnNode node = new FilledNewArrayNode(elType, regs == null ? 0 : regs.length);
|
||||
node.setResult(resReg == -1 ? null : InsnArg.reg(resReg, arrType));
|
||||
if (regs != null) {
|
||||
for (InsnArg arg : regs) {
|
||||
node.addArg(arg);
|
||||
}
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
private InsnNode cmp(DecodedInstruction insn, InsnType itype, ArgType argType) {
|
||||
@@ -690,17 +695,6 @@ public class InsnDecoder {
|
||||
return node;
|
||||
}
|
||||
|
||||
private InsnNode insn(InsnType type, RegisterArg res, InsnArg... args) {
|
||||
InsnNode node = new InsnNode(type, args == null ? 0 : args.length);
|
||||
node.setResult(res);
|
||||
if (args != null) {
|
||||
for (InsnArg arg : args) {
|
||||
node.addArg(arg);
|
||||
}
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
private int getMoveResultRegister(DecodedInstruction[] insnArr, int offset) {
|
||||
int nextOffset = getNextInsnOffset(insnArr, offset);
|
||||
if (nextOffset >= 0) {
|
||||
|
||||
@@ -4,9 +4,11 @@ import jadx.core.codegen.TypeGen;
|
||||
import jadx.core.deobf.NameMapper;
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.info.MethodInfo;
|
||||
import jadx.core.dex.instructions.ArithNode;
|
||||
import jadx.core.dex.instructions.ConstClassNode;
|
||||
import jadx.core.dex.instructions.ConstStringNode;
|
||||
import jadx.core.dex.instructions.FillArrayNode;
|
||||
import jadx.core.dex.instructions.FilledNewArrayNode;
|
||||
import jadx.core.dex.instructions.IndexInsnNode;
|
||||
import jadx.core.dex.instructions.InsnType;
|
||||
import jadx.core.dex.instructions.InvokeNode;
|
||||
@@ -25,9 +27,13 @@ import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.dex.trycatch.ExcHandlerAttr;
|
||||
import jadx.core.dex.trycatch.ExceptionHandler;
|
||||
import jadx.core.utils.ErrorsCounter;
|
||||
import jadx.core.utils.InsnUtils;
|
||||
import jadx.core.utils.InstructionRemover;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -95,20 +101,64 @@ public class ModVisitor extends AbstractVisitor {
|
||||
}
|
||||
break;
|
||||
|
||||
case RETURN:
|
||||
if (insn.getArgsCount() > 0 && insn.getArg(0).isLiteral()) {
|
||||
LiteralArg arg = (LiteralArg) insn.getArg(0);
|
||||
FieldNode f = parentClass.getConstFieldByLiteralArg(arg);
|
||||
if (f != null) {
|
||||
arg.wrapInstruction(new IndexInsnNode(InsnType.SGET, f.getFieldInfo(), 0));
|
||||
case NEW_ARRAY:
|
||||
// create array in 'fill-array' instruction
|
||||
int next = i + 1;
|
||||
if (next < size) {
|
||||
InsnNode ni = block.getInstructions().get(next);
|
||||
if (ni.getType() == InsnType.FILL_ARRAY) {
|
||||
ni.getResult().merge(insn.getResult());
|
||||
((FillArrayNode) ni).mergeElementType(insn.getResult().getType().getArrayElement());
|
||||
remover.add(insn);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case FILL_ARRAY:
|
||||
InsnNode filledArr = makeFilledArrayInsn(mth, (FillArrayNode) insn);
|
||||
replaceInsn(block, i, filledArr);
|
||||
break;
|
||||
|
||||
case MOVE_EXCEPTION:
|
||||
processMoveException(mth, block, insn, remover);
|
||||
break;
|
||||
|
||||
case ARITH:
|
||||
ArithNode arithNode = (ArithNode) insn;
|
||||
if (arithNode.getArgsCount() == 2) {
|
||||
InsnArg litArg = arithNode.getArg(1);
|
||||
if (litArg.isLiteral()) {
|
||||
FieldNode f = parentClass.getConstFieldByLiteralArg((LiteralArg) litArg);
|
||||
if (f != null) {
|
||||
InsnNode fGet = new IndexInsnNode(InsnType.SGET, f.getFieldInfo(), 0);
|
||||
insn.replaceArg(litArg, InsnArg.wrapArg(fGet));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
remover.perform();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove unnecessary instructions
|
||||
*/
|
||||
private static void removeStep(MethodNode mth, InstructionRemover remover) {
|
||||
for (BlockNode block : mth.getBasicBlocks()) {
|
||||
remover.setBlock(block);
|
||||
for (InsnNode insn : block.getInstructions()) {
|
||||
switch (insn.getType()) {
|
||||
case NOP:
|
||||
case GOTO:
|
||||
case NEW_INSTANCE:
|
||||
remover.add(insn);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -122,50 +172,51 @@ public class ModVisitor extends AbstractVisitor {
|
||||
InsnNode insn = block.getInstructions().get(insnNumber);
|
||||
InvokeNode inv = (InvokeNode) insn;
|
||||
MethodInfo callMth = inv.getCallMth();
|
||||
if (callMth.isConstructor()) {
|
||||
InsnNode instArgAssignInsn = ((RegisterArg) inv.getArg(0)).getAssignInsn();
|
||||
ConstructorInsn co = new ConstructorInsn(mth, inv);
|
||||
boolean remove = false;
|
||||
if (co.isSuper() && (co.getArgsCount() == 0 || parentClass.isEnum())) {
|
||||
remove = true;
|
||||
} else if (co.isThis() && co.getArgsCount() == 0) {
|
||||
MethodNode defCo = parentClass.searchMethodByName(callMth.getShortId());
|
||||
if (defCo == null || defCo.isNoCode()) {
|
||||
// default constructor not implemented
|
||||
remove = true;
|
||||
}
|
||||
}
|
||||
// remove super() call in instance initializer
|
||||
if (parentClass.isAnonymous() && mth.isDefaultConstructor() && co.isSuper()) {
|
||||
if (!callMth.isConstructor()) {
|
||||
return;
|
||||
}
|
||||
InsnNode instArgAssignInsn = ((RegisterArg) inv.getArg(0)).getAssignInsn();
|
||||
ConstructorInsn co = new ConstructorInsn(mth, inv);
|
||||
boolean remove = false;
|
||||
if (co.isSuper() && (co.getArgsCount() == 0 || parentClass.isEnum())) {
|
||||
remove = true;
|
||||
} else if (co.isThis() && co.getArgsCount() == 0) {
|
||||
MethodNode defCo = parentClass.searchMethodByName(callMth.getShortId());
|
||||
if (defCo == null || defCo.isNoCode()) {
|
||||
// default constructor not implemented
|
||||
remove = true;
|
||||
}
|
||||
if (remove) {
|
||||
remover.add(insn);
|
||||
} else {
|
||||
replaceInsn(block, insnNumber, co);
|
||||
if (co.isNewInstance()) {
|
||||
InsnNode newInstInsn = removeAssignChain(instArgAssignInsn, remover, InsnType.NEW_INSTANCE);
|
||||
if (newInstInsn != null) {
|
||||
RegisterArg instArg = newInstInsn.getResult();
|
||||
RegisterArg resultArg = co.getResult();
|
||||
if (!resultArg.equals(instArg)) {
|
||||
// replace all usages of 'instArg' with result of this constructor instruction
|
||||
for (RegisterArg useArg : new ArrayList<RegisterArg>(instArg.getSVar().getUseList())) {
|
||||
RegisterArg dup = resultArg.duplicate();
|
||||
InsnNode parentInsn = useArg.getParentInsn();
|
||||
parentInsn.replaceArg(useArg, dup);
|
||||
dup.setParentInsn(parentInsn);
|
||||
resultArg.getSVar().use(dup);
|
||||
}
|
||||
}
|
||||
}
|
||||
// remove super() call in instance initializer
|
||||
if (parentClass.isAnonymous() && mth.isDefaultConstructor() && co.isSuper()) {
|
||||
remove = true;
|
||||
}
|
||||
if (remove) {
|
||||
remover.add(insn);
|
||||
return;
|
||||
}
|
||||
replaceInsn(block, insnNumber, co);
|
||||
if (co.isNewInstance()) {
|
||||
InsnNode newInstInsn = removeAssignChain(instArgAssignInsn, remover, InsnType.NEW_INSTANCE);
|
||||
if (newInstInsn != null) {
|
||||
RegisterArg instArg = newInstInsn.getResult();
|
||||
RegisterArg resultArg = co.getResult();
|
||||
if (!resultArg.equals(instArg)) {
|
||||
// replace all usages of 'instArg' with result of this constructor instruction
|
||||
for (RegisterArg useArg : new ArrayList<RegisterArg>(instArg.getSVar().getUseList())) {
|
||||
RegisterArg dup = resultArg.duplicate();
|
||||
InsnNode parentInsn = useArg.getParentInsn();
|
||||
parentInsn.replaceArg(useArg, dup);
|
||||
dup.setParentInsn(parentInsn);
|
||||
resultArg.getSVar().use(dup);
|
||||
}
|
||||
}
|
||||
ConstructorInsn replace = processConstructor(mth, co);
|
||||
if (replace != null) {
|
||||
replaceInsn(block, insnNumber, replace);
|
||||
}
|
||||
}
|
||||
}
|
||||
ConstructorInsn replace = processConstructor(mth, co);
|
||||
if (replace != null) {
|
||||
replaceInsn(block, insnNumber, replace);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -193,6 +244,41 @@ public class ModVisitor extends AbstractVisitor {
|
||||
return newInsn;
|
||||
}
|
||||
|
||||
private static InsnNode makeFilledArrayInsn(MethodNode mth, FillArrayNode insn) {
|
||||
ArgType insnArrayType = insn.getResult().getType();
|
||||
ArgType insnElementType = insnArrayType.getArrayElement();
|
||||
ArgType elType = insn.getElementType();
|
||||
if (!elType.equals(insnElementType) && !insnArrayType.equals(ArgType.OBJECT)) {
|
||||
ErrorsCounter.methodError(mth,
|
||||
"Incorrect type for fill-array insn " + InsnUtils.formatOffset(insn.getOffset())
|
||||
+ ", element type: " + elType + ", insn element type: " + insnElementType
|
||||
);
|
||||
}
|
||||
if (!elType.isTypeKnown()) {
|
||||
LOG.warn("Unknown array element type: {} in mth: {}", elType, mth);
|
||||
elType = insnElementType.isTypeKnown() ? insnElementType : elType.selectFirst();
|
||||
if (elType == null) {
|
||||
throw new JadxRuntimeException("Null array element type");
|
||||
}
|
||||
}
|
||||
insn.mergeElementType(elType);
|
||||
elType = insn.getElementType();
|
||||
|
||||
List<LiteralArg> list = insn.getLiteralArgs();
|
||||
InsnNode filledArr = new FilledNewArrayNode(elType, list.size());
|
||||
filledArr.setResult(insn.getResult());
|
||||
for (LiteralArg arg : list) {
|
||||
FieldNode f = mth.getParentClass().getConstFieldByLiteralArg(arg);
|
||||
if (f != null) {
|
||||
InsnNode fGet = new IndexInsnNode(InsnType.SGET, f.getFieldInfo(), 0);
|
||||
filledArr.addArg(InsnArg.wrapArg(fGet));
|
||||
} else {
|
||||
filledArr.addArg(arg);
|
||||
}
|
||||
}
|
||||
return filledArr;
|
||||
}
|
||||
|
||||
private static boolean allArgsNull(InsnNode insn) {
|
||||
for (InsnArg insnArg : insn.getArguments()) {
|
||||
if (insnArg.isLiteral()) {
|
||||
@@ -226,53 +312,6 @@ public class ModVisitor extends AbstractVisitor {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove unnecessary instructions
|
||||
*/
|
||||
private static void removeStep(MethodNode mth, InstructionRemover remover) {
|
||||
for (BlockNode block : mth.getBasicBlocks()) {
|
||||
remover.setBlock(block);
|
||||
int size = block.getInstructions().size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
InsnNode insn = block.getInstructions().get(i);
|
||||
switch (insn.getType()) {
|
||||
case NOP:
|
||||
case GOTO:
|
||||
case NEW_INSTANCE:
|
||||
remover.add(insn);
|
||||
break;
|
||||
|
||||
case NEW_ARRAY:
|
||||
// create array in 'fill-array' instruction
|
||||
int next = i + 1;
|
||||
if (next < size) {
|
||||
InsnNode ni = block.getInstructions().get(next);
|
||||
if (ni.getType() == InsnType.FILL_ARRAY) {
|
||||
ni.getResult().merge(insn.getResult());
|
||||
((FillArrayNode) ni).mergeElementType(insn.getResult().getType().getArrayElement());
|
||||
remover.add(insn);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case RETURN:
|
||||
if (insn.getArgsCount() == 0) {
|
||||
if (mth.getBasicBlocks().size() == 1 && i == size - 1) {
|
||||
remover.add(insn);
|
||||
} else if (mth.getMethodInfo().isClassInit()) {
|
||||
remover.add(insn);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
remover.perform();
|
||||
}
|
||||
}
|
||||
|
||||
private static void processMoveException(MethodNode mth, BlockNode block, InsnNode insn,
|
||||
InstructionRemover remover) {
|
||||
ExcHandlerAttr excHandlerAttr = block.get(AType.EXC_HANDLER);
|
||||
@@ -310,6 +349,7 @@ public class ModVisitor extends AbstractVisitor {
|
||||
private static void replaceInsn(BlockNode block, int i, InsnNode insn) {
|
||||
InsnNode prevInsn = block.getInstructions().get(i);
|
||||
insn.copyAttributesFrom(prevInsn);
|
||||
insn.setSourceLine(prevInsn.getSourceLine());
|
||||
block.getInstructions().set(i, insn);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,10 +5,12 @@ import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.nodes.EnumMapAttr;
|
||||
import jadx.core.dex.info.AccessInfo;
|
||||
import jadx.core.dex.info.FieldInfo;
|
||||
import jadx.core.dex.instructions.FilledNewArrayNode;
|
||||
import jadx.core.dex.instructions.IndexInsnNode;
|
||||
import jadx.core.dex.instructions.InsnType;
|
||||
import jadx.core.dex.instructions.InvokeNode;
|
||||
import jadx.core.dex.instructions.SwitchNode;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.instructions.args.InsnArg;
|
||||
import jadx.core.dex.instructions.args.InsnWrapArg;
|
||||
import jadx.core.dex.instructions.args.LiteralArg;
|
||||
@@ -78,7 +80,8 @@ public class ReSugarCode extends AbstractVisitor {
|
||||
if (len <= 0 || i + len >= size || instructions.get(i + len).getType() != InsnType.APUT) {
|
||||
return null;
|
||||
}
|
||||
InsnNode filledArr = new InsnNode(InsnType.FILLED_NEW_ARRAY, len);
|
||||
ArgType arrType = insn.getResult().getType();
|
||||
InsnNode filledArr = new FilledNewArrayNode(arrType.getArrayElement(), len);
|
||||
filledArr.setResult(insn.getResult());
|
||||
for (int j = 0; j < len; j++) {
|
||||
InsnNode put = instructions.get(i + 1 + j);
|
||||
|
||||
@@ -17,8 +17,6 @@ import jadx.core.dex.visitors.AbstractVisitor;
|
||||
import jadx.core.utils.BlockUtils;
|
||||
import jadx.core.utils.InstructionRemover;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
public class BlockExceptionHandler extends AbstractVisitor {
|
||||
|
||||
@Override
|
||||
@@ -38,16 +36,6 @@ public class BlockExceptionHandler extends AbstractVisitor {
|
||||
for (BlockNode block : mth.getBasicBlocks()) {
|
||||
processTryCatchBlocks(mth, block);
|
||||
}
|
||||
|
||||
for (BlockNode block : mth.getBasicBlocks()) {
|
||||
Iterator<InsnNode> it = block.getInstructions().iterator();
|
||||
while (it.hasNext()) {
|
||||
InsnNode insn = it.next();
|
||||
if (insn.getType() == InsnType.NOP) {
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package jadx.tests.integration.arrays;
|
||||
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.tests.api.IntegrationTest;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static jadx.tests.api.utils.JadxMatchers.containsOne;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
public class TestArrayFillConstReplace extends IntegrationTest {
|
||||
|
||||
public static class TestCls {
|
||||
public static final int CONST_INT = 0xffff;
|
||||
|
||||
public int[] test() {
|
||||
return new int[]{127, 129, CONST_INT};
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
ClassNode cls = getClassNode(TestCls.class);
|
||||
String code = cls.getCode().toString();
|
||||
|
||||
assertThat(code, containsOne("return new int[]{127, 129, CONST_INT};"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package jadx.tests.integration.arrays;
|
||||
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.tests.api.IntegrationTest;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static jadx.tests.api.utils.JadxMatchers.containsOne;
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
public class TestArrays2 extends IntegrationTest {
|
||||
public static class TestCls {
|
||||
|
||||
private static Object test4(int type) {
|
||||
if (type == 1) {
|
||||
return new int[]{1, 2};
|
||||
} else if (type == 2) {
|
||||
return new float[]{1, 2};
|
||||
} else if (type == 3) {
|
||||
return new short[]{1, 2};
|
||||
} else if (type == 4) {
|
||||
return new byte[]{1, 2};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void check() {
|
||||
assertThat(test4(4), instanceOf(byte[].class));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
noDebugInfo();
|
||||
ClassNode cls = getClassNode(TestCls.class);
|
||||
String code = cls.getCode().toString();
|
||||
|
||||
assertThat(code, containsOne("new int[]{1, 2}"));
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,7 @@ public class TestFallbackMode extends IntegrationTest {
|
||||
String code = cls.getCode().toString();
|
||||
|
||||
assertThat(code, containsString("public int test(int r2) {"));
|
||||
assertThat(code, containsString("r1_this = this;"));
|
||||
assertThat(code, containsString("r1 = this;"));
|
||||
assertThat(code, containsString("L_0x0004:"));
|
||||
assertThat(code, not(containsString("throw new UnsupportedOperationException")));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package jadx.tests.smali;
|
||||
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.tests.api.SmaliTest;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static jadx.tests.api.utils.JadxMatchers.containsOne;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
public class TestArithConst extends SmaliTest {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
noDebugInfo();
|
||||
ClassNode cls = getClassNodeFromSmali("TestArithConst");
|
||||
String code = cls.getCode().toString();
|
||||
|
||||
assertThat(code, containsOne("return i + CONST_INT;"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
.class public LTestArithConst;
|
||||
.super Ljava/lang/Object;
|
||||
|
||||
.field public static final CONST_INT:I = 0xff
|
||||
|
||||
.method private test(I)I
|
||||
.registers 2
|
||||
|
||||
add-int/lit16 v0, p1, 0xff
|
||||
|
||||
return v0
|
||||
.end method
|
||||
Reference in New Issue
Block a user