未验证 提交 683c2dfb 编写于 作者: S Skylot

fix: improve ternary inline, resolve more enum cases (#1686)

上级 266cbcc6
...@@ -171,7 +171,7 @@ public class ClassGen { ...@@ -171,7 +171,7 @@ public class ClassGen {
ArgType sup = cls.getSuperClass(); ArgType sup = cls.getSuperClass();
if (sup != null if (sup != null
&& !sup.equals(ArgType.OBJECT) && !sup.equals(ArgType.OBJECT)
&& !cls.isEnum()) { && !cls.contains(AFlag.REMOVE_SUPER_CLASS)) {
clsCode.add("extends "); clsCode.add("extends ");
useClass(clsCode, sup); useClass(clsCode, sup);
clsCode.add(' '); clsCode.add(' ');
......
...@@ -21,6 +21,7 @@ public enum AFlag { ...@@ -21,6 +21,7 @@ public enum AFlag {
DONT_GENERATE, // process as usual, but don't output to generated code DONT_GENERATE, // process as usual, but don't output to generated code
COMMENT_OUT, // process as usual, but comment insn in generated code COMMENT_OUT, // process as usual, but comment insn in generated code
REMOVE, // can be completely removed REMOVE, // can be completely removed
REMOVE_SUPER_CLASS, // don't add super class
HIDDEN, // instruction used inside other instruction but not listed in args HIDDEN, // instruction used inside other instruction but not listed in args
......
...@@ -156,7 +156,7 @@ public final class IfCondition extends AttrNode { ...@@ -156,7 +156,7 @@ public final class IfCondition extends AttrNode {
return i; return i;
} }
if (c.getOp() == IfOp.EQ && c.getB().isFalse()) { if (c.getOp() == IfOp.EQ && c.getB().isFalse()) {
cond = not(new IfCondition(c.invert())); cond = new IfCondition(Mode.NOT, Collections.singletonList(new IfCondition(c.invert())));
} else { } else {
c.normalize(); c.normalize();
} }
......
...@@ -62,45 +62,47 @@ public class ConstInlineVisitor extends AbstractVisitor { ...@@ -62,45 +62,47 @@ public class ConstInlineVisitor extends AbstractVisitor {
|| insn.getResult() == null) { || insn.getResult() == null) {
return; return;
} }
SSAVar sVar = insn.getResult().getSVar(); SSAVar sVar = insn.getResult().getSVar();
InsnArg constArg; InsnArg constArg;
Runnable onSuccess = null; Runnable onSuccess = null;
switch (insn.getType()) {
InsnType insnType = insn.getType(); case CONST:
if (insnType == InsnType.CONST || insnType == InsnType.MOVE) { case MOVE: {
constArg = insn.getArg(0); constArg = insn.getArg(0);
if (!constArg.isLiteral()) { if (!constArg.isLiteral()) {
return; return;
} }
long lit = ((LiteralArg) constArg).getLiteral(); long lit = ((LiteralArg) constArg).getLiteral();
if (lit == 0 && forbidNullInlines(sVar)) { if (lit == 0 && forbidNullInlines(sVar)) {
// all usages forbids inlining // all usages forbids inlining
return; return;
}
break;
} }
} else if (insnType == InsnType.CONST_STR) { case CONST_STR: {
if (sVar.isUsedInPhi()) { String s = ((ConstStringNode) insn).getString();
return; FieldNode f = mth.getParentClass().getConstField(s);
if (f == null) {
InsnNode copy = insn.copyWithoutResult();
constArg = InsnArg.wrapArg(copy);
} else {
InsnNode constGet = new IndexInsnNode(InsnType.SGET, f.getFieldInfo(), 0);
constArg = InsnArg.wrapArg(constGet);
constArg.setType(ArgType.STRING);
onSuccess = () -> f.addUseIn(mth);
}
break;
} }
String s = ((ConstStringNode) insn).getString(); case CONST_CLASS: {
FieldNode f = mth.getParentClass().getConstField(s); if (sVar.isUsedInPhi()) {
if (f == null) { return;
InsnNode copy = insn.copyWithoutResult(); }
constArg = InsnArg.wrapArg(copy); constArg = InsnArg.wrapArg(insn.copyWithoutResult());
} else { constArg.setType(ArgType.CLASS);
InsnNode constGet = new IndexInsnNode(InsnType.SGET, f.getFieldInfo(), 0); break;
constArg = InsnArg.wrapArg(constGet);
constArg.setType(ArgType.STRING);
onSuccess = () -> f.addUseIn(mth);
} }
} else if (insnType == InsnType.CONST_CLASS) { default:
if (sVar.isUsedInPhi()) {
return; return;
}
constArg = InsnArg.wrapArg(insn.copyWithoutResult());
constArg.setType(ArgType.CLASS);
} else {
return;
} }
// all check passed, run replace // all check passed, run replace
......
...@@ -40,13 +40,18 @@ import jadx.core.dex.nodes.FieldNode; ...@@ -40,13 +40,18 @@ 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.dex.regions.Region;
import jadx.core.dex.visitors.regions.CheckRegions;
import jadx.core.dex.visitors.regions.IfRegionVisitor;
import jadx.core.dex.visitors.shrink.CodeShrinkVisitor; import jadx.core.dex.visitors.shrink.CodeShrinkVisitor;
import jadx.core.utils.BlockInsnPair; import jadx.core.utils.BlockInsnPair;
import jadx.core.utils.BlockUtils; import jadx.core.utils.BlockUtils;
import jadx.core.utils.InsnRemover; import jadx.core.utils.InsnRemover;
import jadx.core.utils.InsnUtils; import jadx.core.utils.InsnUtils;
import jadx.core.utils.ListUtils;
import jadx.core.utils.Utils; import jadx.core.utils.Utils;
import jadx.core.utils.exceptions.JadxException; import jadx.core.utils.exceptions.JadxException;
import jadx.core.utils.exceptions.JadxRuntimeException;
import static jadx.core.utils.InsnUtils.checkInsnType; import static jadx.core.utils.InsnUtils.checkInsnType;
import static jadx.core.utils.InsnUtils.getSingleArg; import static jadx.core.utils.InsnUtils.getSingleArg;
...@@ -55,8 +60,16 @@ import static jadx.core.utils.InsnUtils.getWrappedInsn; ...@@ -55,8 +60,16 @@ import static jadx.core.utils.InsnUtils.getWrappedInsn;
@JadxVisitor( @JadxVisitor(
name = "EnumVisitor", name = "EnumVisitor",
desc = "Restore enum classes", desc = "Restore enum classes",
runAfter = { CodeShrinkVisitor.class, ModVisitor.class, ReSugarCode.class }, runAfter = {
runBefore = { ExtractFieldInit.class } CodeShrinkVisitor.class, // all possible instructions already inlined
ModVisitor.class,
ReSugarCode.class,
IfRegionVisitor.class, // ternary operator inlined
CheckRegions.class // regions processing finished
},
runBefore = {
ExtractFieldInit.class
}
) )
public class EnumVisitor extends AbstractVisitor { public class EnumVisitor extends AbstractVisitor {
...@@ -92,7 +105,7 @@ public class EnumVisitor extends AbstractVisitor { ...@@ -92,7 +105,7 @@ public class EnumVisitor extends AbstractVisitor {
AccessInfo accessFlags = cls.getAccessFlags(); AccessInfo accessFlags = cls.getAccessFlags();
if (accessFlags.isEnum()) { if (accessFlags.isEnum()) {
cls.setAccessFlags(accessFlags.remove(AccessFlags.ENUM)); cls.setAccessFlags(accessFlags.remove(AccessFlags.ENUM));
cls.addWarnComment("Failed to restore enum class, 'enum' modifier removed"); cls.addWarnComment("Failed to restore enum class, 'enum' modifier and super class removed");
} }
} }
return true; return true;
...@@ -102,14 +115,24 @@ public class EnumVisitor extends AbstractVisitor { ...@@ -102,14 +115,24 @@ public class EnumVisitor extends AbstractVisitor {
if (!cls.isEnum()) { if (!cls.isEnum()) {
return false; return false;
} }
ArgType superType = cls.getSuperClass();
if (superType != null && superType.getObject().equals(ArgType.ENUM.getObject())) {
cls.add(AFlag.REMOVE_SUPER_CLASS);
}
MethodNode classInitMth = cls.getClassInitMth(); MethodNode classInitMth = cls.getClassInitMth();
if (classInitMth == null) { if (classInitMth == null) {
cls.addWarnComment("Enum class init method not found"); cls.addWarnComment("Enum class init method not found");
return false; return false;
} }
if (classInitMth.getBasicBlocks().isEmpty()) { Region staticRegion = classInitMth.getRegion();
if (staticRegion == null || classInitMth.getBasicBlocks().isEmpty()) {
return false; return false;
} }
if (!ListUtils.allMatch(staticRegion.getSubBlocks(), BlockNode.class::isInstance)) {
cls.addWarnComment("Unexpected branching instructions in enum static init block");
return false;
}
List<BlockNode> staticBlocks = ListUtils.map(staticRegion.getSubBlocks(), BlockNode.class::cast);
ArgType clsType = cls.getClassInfo().getType(); ArgType clsType = cls.getClassInfo().getType();
// search "$VALUES" field (holds all enum values) // search "$VALUES" field (holds all enum values)
...@@ -137,7 +160,6 @@ public class EnumVisitor extends AbstractVisitor { ...@@ -137,7 +160,6 @@ public class EnumVisitor extends AbstractVisitor {
return false; return false;
} }
FieldNode valuesField = valuesCandidates.get(0); FieldNode valuesField = valuesCandidates.get(0);
List<InsnNode> toRemove = new ArrayList<>();
// search "$VALUES" array init and collect enum fields // search "$VALUES" array init and collect enum fields
BlockInsnPair valuesInitPair = getValuesInitInsn(classInitMth, valuesField); BlockInsnPair valuesInitPair = getValuesInitInsn(classInitMth, valuesField);
...@@ -147,16 +169,19 @@ public class EnumVisitor extends AbstractVisitor { ...@@ -147,16 +169,19 @@ public class EnumVisitor extends AbstractVisitor {
BlockNode staticBlock = valuesInitPair.getBlock(); BlockNode staticBlock = valuesInitPair.getBlock();
InsnNode valuesInitInsn = valuesInitPair.getInsn(); InsnNode valuesInitInsn = valuesInitPair.getInsn();
EnumData enumData = new EnumData(cls, valuesField, staticBlocks);
List<EnumField> enumFields = null; List<EnumField> enumFields = null;
InsnArg arrArg = valuesInitInsn.getArg(0); InsnArg arrArg = valuesInitInsn.getArg(0);
if (arrArg.isInsnWrap()) { if (arrArg.isInsnWrap()) {
InsnNode wrappedInsn = ((InsnWrapArg) arrArg).getWrapInsn(); InsnNode wrappedInsn = ((InsnWrapArg) arrArg).getWrapInsn();
enumFields = extractEnumFieldsFromInsn(cls, staticBlock, wrappedInsn, toRemove); enumFields = extractEnumFieldsFromInsn(enumData, wrappedInsn);
} }
if (enumFields == null) { if (enumFields == null) {
cls.addWarnComment("Unknown enum class pattern. Please report as an issue!");
return false; return false;
} }
toRemove.add(valuesInitInsn); enumData.toRemove.add(valuesInitInsn);
// all checks complete, perform transform // all checks complete, perform transform
EnumClassAttr attr = new EnumClassAttr(enumFields); EnumClassAttr attr = new EnumClassAttr(enumFields);
...@@ -176,59 +201,52 @@ public class EnumVisitor extends AbstractVisitor { ...@@ -176,59 +201,52 @@ public class EnumVisitor extends AbstractVisitor {
fieldNode.getFieldInfo().setAlias(name); fieldNode.getFieldInfo().setAlias(name);
} }
fieldNode.add(AFlag.DONT_GENERATE); fieldNode.add(AFlag.DONT_GENERATE);
processConstructorInsn(cls, enumField, classInitMth, staticBlock, toRemove); processConstructorInsn(enumData, enumField, classInitMth);
} }
valuesField.add(AFlag.DONT_GENERATE); valuesField.add(AFlag.DONT_GENERATE);
InsnRemover.removeAllAndUnbind(classInitMth, staticBlock, toRemove); InsnRemover.removeAllAndUnbind(classInitMth, enumData.toRemove);
if (classInitMth.countInsns() == 0) { if (classInitMth.countInsns() == 0) {
classInitMth.add(AFlag.DONT_GENERATE); classInitMth.add(AFlag.DONT_GENERATE);
} else if (!toRemove.isEmpty()) { } else if (!enumData.toRemove.isEmpty()) {
CodeShrinkVisitor.shrinkMethod(classInitMth); CodeShrinkVisitor.shrinkMethod(classInitMth);
} }
removeEnumMethods(cls, clsType, valuesField); removeEnumMethods(cls, clsType, valuesField);
return true; return true;
} }
private void processConstructorInsn(ClassNode cls, EnumField enumField, MethodNode classInitMth, private void processConstructorInsn(EnumData data, EnumField enumField, MethodNode classInitMth) {
BlockNode staticBlock, List<InsnNode> toRemove) {
ConstructorInsn co = enumField.getConstrInsn(); ConstructorInsn co = enumField.getConstrInsn();
ClassInfo enumClsInfo = co.getClassType(); ClassInfo enumClsInfo = co.getClassType();
if (!enumClsInfo.equals(cls.getClassInfo())) { if (!enumClsInfo.equals(data.cls.getClassInfo())) {
ClassNode enumCls = cls.root().resolveClass(enumClsInfo); ClassNode enumCls = data.cls.root().resolveClass(enumClsInfo);
if (enumCls != null) { if (enumCls != null) {
processEnumCls(cls, enumField, enumCls); processEnumCls(data.cls, enumField, enumCls);
} }
} }
List<RegisterArg> regs = new ArrayList<>(); MethodNode ctrMth = data.cls.root().resolveMethod(co.getCallMth());
co.getRegisterArgs(regs);
if (!regs.isEmpty()) {
cls.addWarnComment("Init of enum " + enumField.getField().getName() + " can be incorrect");
}
MethodNode ctrMth = cls.root().resolveMethod(co.getCallMth());
if (ctrMth != null) { if (ctrMth != null) {
markArgsForSkip(ctrMth); markArgsForSkip(ctrMth);
} }
RegisterArg coResArg = co.getResult(); RegisterArg coResArg = co.getResult();
if (coResArg == null || coResArg.getSVar().getUseList().size() <= 2) { if (coResArg == null || coResArg.getSVar().getUseList().size() <= 2) {
toRemove.add(co); data.toRemove.add(co);
} else { } else {
// constructor result used in other places -> replace constructor with enum field get (SGET) // constructor result used in other places -> replace constructor with enum field get (SGET)
IndexInsnNode enumGet = new IndexInsnNode(InsnType.SGET, enumField.getField().getFieldInfo(), 0); IndexInsnNode enumGet = new IndexInsnNode(InsnType.SGET, enumField.getField().getFieldInfo(), 0);
enumGet.setResult(coResArg.duplicate()); enumGet.setResult(coResArg.duplicate());
BlockUtils.replaceInsn(classInitMth, staticBlock, co, enumGet); BlockUtils.replaceInsn(classInitMth, co, enumGet);
} }
} }
@Nullable @Nullable
private List<EnumField> extractEnumFieldsFromInsn(ClassNode cls, BlockNode staticBlock, private List<EnumField> extractEnumFieldsFromInsn(EnumData enumData, InsnNode wrappedInsn) {
InsnNode wrappedInsn, List<InsnNode> toRemove) {
switch (wrappedInsn.getType()) { switch (wrappedInsn.getType()) {
case FILLED_NEW_ARRAY: case FILLED_NEW_ARRAY:
return extractEnumFieldsFromFilledArray(cls, wrappedInsn, staticBlock, toRemove); return extractEnumFieldsFromFilledArray(enumData, wrappedInsn);
case INVOKE: case INVOKE:
// handle redirection of values array fill (added in java 15) // handle redirection of values array fill (added in java 15)
return extractEnumFieldsFromInvoke(cls, staticBlock, (InvokeNode) wrappedInsn, toRemove); return extractEnumFieldsFromInvoke(enumData, (InvokeNode) wrappedInsn);
case NEW_ARRAY: case NEW_ARRAY:
InsnArg arg = wrappedInsn.getArg(0); InsnArg arg = wrappedInsn.getArg(0);
...@@ -243,10 +261,9 @@ public class EnumVisitor extends AbstractVisitor { ...@@ -243,10 +261,9 @@ public class EnumVisitor extends AbstractVisitor {
} }
} }
private List<EnumField> extractEnumFieldsFromInvoke(ClassNode cls, BlockNode staticBlock, private List<EnumField> extractEnumFieldsFromInvoke(EnumData enumData, InvokeNode invokeNode) {
InvokeNode invokeNode, List<InsnNode> toRemove) {
MethodInfo callMth = invokeNode.getCallMth(); MethodInfo callMth = invokeNode.getCallMth();
MethodNode valuesMth = cls.root().resolveMethod(callMth); MethodNode valuesMth = enumData.cls.root().resolveMethod(callMth);
if (valuesMth == null || valuesMth.isVoidReturn()) { if (valuesMth == null || valuesMth.isVoidReturn()) {
return null; return null;
} }
...@@ -256,7 +273,7 @@ public class EnumVisitor extends AbstractVisitor { ...@@ -256,7 +273,7 @@ public class EnumVisitor extends AbstractVisitor {
if (wrappedInsn == null) { if (wrappedInsn == null) {
return null; return null;
} }
List<EnumField> enumFields = extractEnumFieldsFromInsn(cls, staticBlock, wrappedInsn, toRemove); List<EnumField> enumFields = extractEnumFieldsFromInsn(enumData, wrappedInsn);
if (enumFields != null) { if (enumFields != null) {
valuesMth.add(AFlag.DONT_GENERATE); valuesMth.add(AFlag.DONT_GENERATE);
} }
...@@ -279,50 +296,49 @@ public class EnumVisitor extends AbstractVisitor { ...@@ -279,50 +296,49 @@ public class EnumVisitor extends AbstractVisitor {
return null; return null;
} }
private List<EnumField> extractEnumFieldsFromFilledArray(ClassNode cls, InsnNode arrFillInsn, BlockNode staticBlock, private List<EnumField> extractEnumFieldsFromFilledArray(EnumData enumData, InsnNode arrFillInsn) {
List<InsnNode> toRemove) {
List<EnumField> enumFields = new ArrayList<>(); List<EnumField> enumFields = new ArrayList<>();
for (InsnArg arg : arrFillInsn.getArguments()) { for (InsnArg arg : arrFillInsn.getArguments()) {
EnumField field = null; EnumField field = null;
if (arg.isInsnWrap()) { if (arg.isInsnWrap()) {
InsnNode wrappedInsn = ((InsnWrapArg) arg).getWrapInsn(); InsnNode wrappedInsn = ((InsnWrapArg) arg).getWrapInsn();
field = processEnumFieldByWrappedInsn(cls, wrappedInsn, staticBlock, toRemove); field = processEnumFieldByWrappedInsn(enumData, wrappedInsn);
} else if (arg.isRegister()) { } else if (arg.isRegister()) {
field = processEnumFieldByRegister(cls, (RegisterArg) arg, staticBlock, toRemove); field = processEnumFieldByRegister(enumData, (RegisterArg) arg);
} }
if (field == null) { if (field == null) {
return null; return null;
} }
enumFields.add(field); enumFields.add(field);
} }
toRemove.add(arrFillInsn); enumData.toRemove.add(arrFillInsn);
return enumFields; return enumFields;
} }
private EnumField processEnumFieldByWrappedInsn(ClassNode cls, InsnNode wrappedInsn, BlockNode staticBlock, List<InsnNode> toRemove) { private EnumField processEnumFieldByWrappedInsn(EnumData data, InsnNode wrappedInsn) {
if (wrappedInsn.getType() == InsnType.SGET) { if (wrappedInsn.getType() == InsnType.SGET) {
return processEnumFieldByField(cls, wrappedInsn, staticBlock, toRemove); return processEnumFieldByField(data, wrappedInsn);
} }
ConstructorInsn constructorInsn = castConstructorInsn(wrappedInsn); ConstructorInsn constructorInsn = castConstructorInsn(wrappedInsn);
if (constructorInsn != null) { if (constructorInsn != null) {
FieldNode enumFieldNode = createFakeField(cls, "EF" + constructorInsn.getOffset()); FieldNode enumFieldNode = createFakeField(data.cls, "EF" + constructorInsn.getOffset());
cls.addField(enumFieldNode); data.cls.addField(enumFieldNode);
return createEnumFieldByConstructor(cls, enumFieldNode, constructorInsn); return createEnumFieldByConstructor(data.cls, enumFieldNode, constructorInsn);
} }
return null; return null;
} }
@Nullable @Nullable
private EnumField processEnumFieldByField(ClassNode cls, InsnNode sgetInsn, BlockNode staticBlock, List<InsnNode> toRemove) { private EnumField processEnumFieldByField(EnumData data, InsnNode sgetInsn) {
if (sgetInsn.getType() != InsnType.SGET) { if (sgetInsn.getType() != InsnType.SGET) {
return null; return null;
} }
FieldInfo fieldInfo = (FieldInfo) ((IndexInsnNode) sgetInsn).getIndex(); FieldInfo fieldInfo = (FieldInfo) ((IndexInsnNode) sgetInsn).getIndex();
FieldNode enumFieldNode = cls.searchField(fieldInfo); FieldNode enumFieldNode = data.cls.searchField(fieldInfo);
if (enumFieldNode == null) { if (enumFieldNode == null) {
return null; return null;
} }
InsnNode sputInsn = searchFieldPutInsn(cls, staticBlock, enumFieldNode); InsnNode sputInsn = searchFieldPutInsn(data, enumFieldNode);
if (sputInsn == null) { if (sputInsn == null) {
return null; return null;
} }
...@@ -333,17 +349,17 @@ public class EnumVisitor extends AbstractVisitor { ...@@ -333,17 +349,17 @@ public class EnumVisitor extends AbstractVisitor {
} }
RegisterArg sgetResult = sgetInsn.getResult(); RegisterArg sgetResult = sgetInsn.getResult();
if (sgetResult == null || sgetResult.getSVar().getUseCount() == 1) { if (sgetResult == null || sgetResult.getSVar().getUseCount() == 1) {
toRemove.add(sgetInsn); data.toRemove.add(sgetInsn);
} }
toRemove.add(sputInsn); data.toRemove.add(sputInsn);
return createEnumFieldByConstructor(cls, enumFieldNode, co); return createEnumFieldByConstructor(data.cls, enumFieldNode, co);
} }
@Nullable @Nullable
private EnumField processEnumFieldByRegister(ClassNode cls, RegisterArg arg, BlockNode staticBlock, List<InsnNode> toRemove) { private EnumField processEnumFieldByRegister(EnumData data, RegisterArg arg) {
InsnNode assignInsn = arg.getAssignInsn(); InsnNode assignInsn = arg.getAssignInsn();
if (assignInsn != null && assignInsn.getType() == InsnType.SGET) { if (assignInsn != null && assignInsn.getType() == InsnType.SGET) {
return processEnumFieldByField(cls, assignInsn, staticBlock, toRemove); return processEnumFieldByField(data, assignInsn);
} }
SSAVar ssaVar = arg.getSVar(); SSAVar ssaVar = arg.getSVar();
...@@ -354,12 +370,12 @@ public class EnumVisitor extends AbstractVisitor { ...@@ -354,12 +370,12 @@ public class EnumVisitor extends AbstractVisitor {
if (constrInsn == null || constrInsn.getType() != InsnType.CONSTRUCTOR) { if (constrInsn == null || constrInsn.getType() != InsnType.CONSTRUCTOR) {
return null; return null;
} }
FieldNode enumFieldNode = searchEnumField(cls, ssaVar, toRemove); FieldNode enumFieldNode = searchEnumField(data, ssaVar);
if (enumFieldNode == null) { if (enumFieldNode == null) {
enumFieldNode = createFakeField(cls, "EF" + arg.getRegNum()); enumFieldNode = createFakeField(data.cls, "EF" + arg.getRegNum());
cls.addField(enumFieldNode); data.cls.addField(enumFieldNode);
} }
return createEnumFieldByConstructor(cls, enumFieldNode, (ConstructorInsn) constrInsn); return createEnumFieldByConstructor(data.cls, enumFieldNode, (ConstructorInsn) constrInsn);
} }
private FieldNode createFakeField(ClassNode cls, String name) { private FieldNode createFakeField(ClassNode cls, String name) {
...@@ -372,17 +388,17 @@ public class EnumVisitor extends AbstractVisitor { ...@@ -372,17 +388,17 @@ public class EnumVisitor extends AbstractVisitor {
} }
@Nullable @Nullable
private FieldNode searchEnumField(ClassNode cls, SSAVar ssaVar, List<InsnNode> toRemove) { private FieldNode searchEnumField(EnumData data, SSAVar ssaVar) {
InsnNode sputInsn = ssaVar.getUseList().get(0).getParentInsn(); InsnNode sputInsn = ssaVar.getUseList().get(0).getParentInsn();
if (sputInsn == null || sputInsn.getType() != InsnType.SPUT) { if (sputInsn == null || sputInsn.getType() != InsnType.SPUT) {
return null; return null;
} }
FieldInfo fieldInfo = (FieldInfo) ((IndexInsnNode) sputInsn).getIndex(); FieldInfo fieldInfo = (FieldInfo) ((IndexInsnNode) sputInsn).getIndex();
FieldNode enumFieldNode = cls.searchField(fieldInfo); FieldNode enumFieldNode = data.cls.searchField(fieldInfo);
if (enumFieldNode == null) { if (enumFieldNode == null) {
return null; return null;
} }
toRemove.add(sputInsn); data.toRemove.add(sputInsn);
return enumFieldNode; return enumFieldNode;
} }
...@@ -409,17 +425,24 @@ public class EnumVisitor extends AbstractVisitor { ...@@ -409,17 +425,24 @@ public class EnumVisitor extends AbstractVisitor {
if (ctrMth == null) { if (ctrMth == null) {
return null; return null;
} }
List<RegisterArg> regs = new ArrayList<>();
co.getRegisterArgs(regs);
if (!regs.isEmpty()) {
throw new JadxRuntimeException("Init of enum " + enumFieldNode.getName() + " uses external variables");
}
return new EnumField(enumFieldNode, co); return new EnumField(enumFieldNode, co);
} }
@Nullable @Nullable
private InsnNode searchFieldPutInsn(ClassNode cls, BlockNode staticBlock, FieldNode enumFieldNode) { private InsnNode searchFieldPutInsn(EnumData data, FieldNode enumFieldNode) {
for (InsnNode sputInsn : staticBlock.getInstructions()) { for (BlockNode block : data.staticBlocks) {
if (sputInsn != null && sputInsn.getType() == InsnType.SPUT) { for (InsnNode sputInsn : block.getInstructions()) {
FieldInfo f = (FieldInfo) ((IndexInsnNode) sputInsn).getIndex(); if (sputInsn != null && sputInsn.getType() == InsnType.SPUT) {
FieldNode fieldNode = cls.searchField(f); FieldInfo f = (FieldInfo) ((IndexInsnNode) sputInsn).getIndex();
if (Objects.equals(fieldNode, enumFieldNode)) { FieldNode fieldNode = data.cls.searchField(f);
return sputInsn; if (Objects.equals(fieldNode, enumFieldNode)) {
return sputInsn;
}
} }
} }
} }
...@@ -605,4 +628,17 @@ public class EnumVisitor extends AbstractVisitor { ...@@ -605,4 +628,17 @@ public class EnumVisitor extends AbstractVisitor {
} }
return null; return null;
} }
private static class EnumData {
final ClassNode cls;
final FieldNode valuesField;
final List<BlockNode> staticBlocks;
final List<InsnNode> toRemove = new ArrayList<>();
public EnumData(ClassNode cls, FieldNode valuesField, List<BlockNode> staticBlocks) {
this.cls = cls;
this.valuesField = valuesField;
this.staticBlocks = staticBlocks;
}
}
} }
...@@ -19,6 +19,10 @@ public class DepthRegionTraversal { ...@@ -19,6 +19,10 @@ public class DepthRegionTraversal {
traverseInternal(mth, visitor, mth.getRegion()); traverseInternal(mth, visitor, mth.getRegion());
} }
public static void traverse(MethodNode mth, IContainer container, IRegionVisitor visitor) {
traverseInternal(mth, visitor, container);
}
public static void traverseIterative(MethodNode mth, IRegionIterativeVisitor visitor) { public static void traverseIterative(MethodNode mth, IRegionIterativeVisitor visitor) {
boolean repeat; boolean repeat;
int k = 0; int k = 0;
......
...@@ -10,6 +10,7 @@ import jadx.core.dex.instructions.PhiInsn; ...@@ -10,6 +10,7 @@ import jadx.core.dex.instructions.PhiInsn;
import jadx.core.dex.instructions.args.InsnArg; import jadx.core.dex.instructions.args.InsnArg;
import jadx.core.dex.instructions.args.InsnWrapArg; import jadx.core.dex.instructions.args.InsnWrapArg;
import jadx.core.dex.instructions.args.RegisterArg; import jadx.core.dex.instructions.args.RegisterArg;
import jadx.core.dex.instructions.args.SSAVar;
import jadx.core.dex.instructions.mods.TernaryInsn; import jadx.core.dex.instructions.mods.TernaryInsn;
import jadx.core.dex.nodes.BlockNode; import jadx.core.dex.nodes.BlockNode;
import jadx.core.dex.nodes.IContainer; import jadx.core.dex.nodes.IContainer;
...@@ -73,11 +74,7 @@ public class TernaryMod extends AbstractRegionVisitor implements IRegionIterativ ...@@ -73,11 +74,7 @@ public class TernaryMod extends AbstractRegionVisitor implements IRegionIterativ
return false; return false;
} }
if (elseRegion == null) { if (elseRegion == null) {
if (mth.isConstructor()) { return processOneBranchTernary(mth, ifRegion);
// force ternary conversion to inline all code in 'super' or 'this' calls
return processOneBranchTernary(mth, ifRegion);
}
return false;
} }
BlockNode tb = getTernaryInsnBlock(thenRegion); BlockNode tb = getTernaryInsnBlock(thenRegion);
BlockNode eb = getTernaryInsnBlock(elseRegion); BlockNode eb = getTernaryInsnBlock(elseRegion);
...@@ -93,21 +90,8 @@ public class TernaryMod extends AbstractRegionVisitor implements IRegionIterativ ...@@ -93,21 +90,8 @@ public class TernaryMod extends AbstractRegionVisitor implements IRegionIterativ
InsnNode thenInsn = tb.getInstructions().get(0); InsnNode thenInsn = tb.getInstructions().get(0);
InsnNode elseInsn = eb.getInstructions().get(0); InsnNode elseInsn = eb.getInstructions().get(0);
if (mth.contains(AFlag.USE_LINES_HINTS) if (!verifyLineHints(mth, thenInsn, elseInsn)) {
&& thenInsn.getSourceLine() != elseInsn.getSourceLine()) { return false;
if (thenInsn.getSourceLine() != 0 && elseInsn.getSourceLine() != 0) {
// sometimes source lines incorrect
if (!checkLineStats(thenInsn, elseInsn)) {
return false;
}
} else {
// no debug info
if (containsTernary(thenInsn) || containsTernary(elseInsn)) {
// don't make nested ternary by default
// TODO: add addition checks
return false;
}
}
} }
RegisterArg thenResArg = thenInsn.getResult(); RegisterArg thenResArg = thenInsn.getResult();
...@@ -184,6 +168,20 @@ public class TernaryMod extends AbstractRegionVisitor implements IRegionIterativ ...@@ -184,6 +168,20 @@ public class TernaryMod extends AbstractRegionVisitor implements IRegionIterativ
return false; return false;
} }
private static boolean verifyLineHints(MethodNode mth, InsnNode thenInsn, InsnNode elseInsn) {
if (mth.contains(AFlag.USE_LINES_HINTS)
&& thenInsn.getSourceLine() != elseInsn.getSourceLine()) {
if (thenInsn.getSourceLine() != 0 && elseInsn.getSourceLine() != 0) {
// sometimes source lines incorrect
return checkLineStats(thenInsn, elseInsn);
}
// don't make nested ternary by default
// TODO: add addition checks
return !containsTernary(thenInsn) && !containsTernary(elseInsn);
}
return true;
}
private static void clearConditionBlocks(List<BlockNode> conditionBlocks, BlockNode header) { private static void clearConditionBlocks(List<BlockNode> conditionBlocks, BlockNode header) {
for (BlockNode block : conditionBlocks) { for (BlockNode block : conditionBlocks) {
if (block != header) { if (block != header) {
...@@ -277,6 +275,7 @@ public class TernaryMod extends AbstractRegionVisitor implements IRegionIterativ ...@@ -277,6 +275,7 @@ public class TernaryMod extends AbstractRegionVisitor implements IRegionIterativ
return false; return false;
} }
@SuppressWarnings("StatementWithEmptyBody")
private static void replaceWithTernary(MethodNode mth, IfRegion ifRegion, BlockNode block, InsnNode insn) { private static void replaceWithTernary(MethodNode mth, IfRegion ifRegion, BlockNode block, InsnNode insn) {
RegisterArg resArg = insn.getResult(); RegisterArg resArg = insn.getResult();
if (resArg.getSVar().getUseList().size() != 1) { if (resArg.getSVar().getUseList().size() != 1) {
...@@ -296,17 +295,45 @@ public class TernaryMod extends AbstractRegionVisitor implements IRegionIterativ ...@@ -296,17 +295,45 @@ public class TernaryMod extends AbstractRegionVisitor implements IRegionIterativ
if (otherArg == null) { if (otherArg == null) {
return; return;
} }
InsnNode elseAssign = otherArg.getAssignInsn();
if (mth.isConstructor() || (mth.getParentClass().isEnum() && mth.getMethodInfo().isClassInit())) {
// forcing ternary inline for constructors (will help in moving super call to the top) and enums
// skip code style checks
} else {
if (elseAssign != null && elseAssign.isConstInsn()) {
if (!verifyLineHints(mth, insn, elseAssign)) {
return;
}
} else {
if (insn.getResult().sameCodeVar(otherArg)) {
// don't use same variable in else branch to prevent: l = (l == 0) ? 1 : l
return;
}
}
}
// all checks passed // all checks passed
BlockNode header = ifRegion.getConditionBlocks().get(0); BlockNode header = ifRegion.getConditionBlocks().get(0);
if (!ifRegion.getParent().replaceSubBlock(ifRegion, header)) { if (!ifRegion.getParent().replaceSubBlock(ifRegion, header)) {
return; return;
} }
InsnArg elseArg;
if (elseAssign != null && elseAssign.isConstInsn()) {
// inline constant
SSAVar elseVar = elseAssign.getResult().getSVar();
if (elseVar.getUseCount() == 1 && elseVar.getOnlyOneUseInPhi() == phiInsn) {
InsnRemover.remove(mth, elseAssign);
}
elseArg = InsnArg.wrapInsnIntoArg(elseAssign);
} else {
elseArg = otherArg;
}
TernaryInsn ternInsn = new TernaryInsn(ifRegion.getCondition(), TernaryInsn ternInsn = new TernaryInsn(ifRegion.getCondition(),
phiInsn.getResult(), InsnArg.wrapInsnIntoArg(insn), otherArg); phiInsn.getResult(), InsnArg.wrapInsnIntoArg(insn), elseArg);
ternInsn.simplifyCondition();
InsnRemover.unbindResult(mth, insn); InsnRemover.unbindResult(mth, insn);
InsnList.remove(block, insn); InsnList.remove(block, insn);
InsnRemover.unbindAllArgs(mth, phiInsn); InsnRemover.unbindAllArgs(mth, phiInsn);
header.getInstructions().clear(); header.getInstructions().clear();
ternInsn.rebindArgs(); ternInsn.rebindArgs();
......
...@@ -17,6 +17,7 @@ import jadx.core.dex.instructions.args.InsnWrapArg; ...@@ -17,6 +17,7 @@ import jadx.core.dex.instructions.args.InsnWrapArg;
import jadx.core.dex.instructions.args.RegisterArg; import jadx.core.dex.instructions.args.RegisterArg;
import jadx.core.dex.instructions.args.SSAVar; import jadx.core.dex.instructions.args.SSAVar;
import jadx.core.dex.nodes.BlockNode; import jadx.core.dex.nodes.BlockNode;
import jadx.core.dex.nodes.IContainer;
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.utils.exceptions.JadxRuntimeException; import jadx.core.utils.exceptions.JadxRuntimeException;
...@@ -228,6 +229,18 @@ public class InsnRemover { ...@@ -228,6 +229,18 @@ public class InsnRemover {
removeAll(block.getInstructions(), insns); removeAll(block.getInstructions(), insns);
} }
public static void removeAllAndUnbind(MethodNode mth, IContainer container, List<InsnNode> insns) {
unbindInsns(mth, insns);
RegionUtils.visitBlocks(mth, container, b -> removeAll(b.getInstructions(), insns));
}
public static void removeAllAndUnbind(MethodNode mth, List<InsnNode> insns) {
unbindInsns(mth, insns);
for (BlockNode block : mth.getBasicBlocks()) {
removeAll(block.getInstructions(), insns);
}
}
public static void removeAllWithoutUnbind(BlockNode block, List<InsnNode> insns) { public static void removeAllWithoutUnbind(BlockNode block, List<InsnNode> insns) {
removeAll(block.getInstructions(), insns); removeAll(block.getInstructions(), insns);
} }
......
...@@ -4,6 +4,7 @@ import java.util.ArrayList; ...@@ -4,6 +4,7 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.function.Consumer;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
...@@ -26,6 +27,8 @@ import jadx.core.dex.regions.loops.LoopRegion; ...@@ -26,6 +27,8 @@ import jadx.core.dex.regions.loops.LoopRegion;
import jadx.core.dex.trycatch.CatchAttr; import jadx.core.dex.trycatch.CatchAttr;
import jadx.core.dex.trycatch.ExceptionHandler; import jadx.core.dex.trycatch.ExceptionHandler;
import jadx.core.dex.trycatch.TryCatchBlockAttr; import jadx.core.dex.trycatch.TryCatchBlockAttr;
import jadx.core.dex.visitors.regions.AbstractRegionVisitor;
import jadx.core.dex.visitors.regions.DepthRegionTraversal;
import jadx.core.utils.exceptions.JadxRuntimeException; import jadx.core.utils.exceptions.JadxRuntimeException;
public class RegionUtils { public class RegionUtils {
...@@ -473,4 +476,13 @@ public class RegionUtils { ...@@ -473,4 +476,13 @@ public class RegionUtils {
} }
return "Unknown container type: " + container.getClass(); return "Unknown container type: " + container.getClass();
} }
public static void visitBlocks(MethodNode mth, IContainer container, Consumer<IBlock> visitor) {
DepthRegionTraversal.traverse(mth, container, new AbstractRegionVisitor() {
@Override
public void processBlock(MethodNode mth, IBlock block) {
visitor.accept(block);
}
});
}
} }
package jadx.tests.integration.conditions;
import org.junit.jupiter.api.Test;
import jadx.NotYetImplemented;
import jadx.tests.api.IntegrationTest;
public class TestSwitchTryBreak extends IntegrationTest {
public static class TestCls {
public void test(int x) {
switch (x) {
case 0:
return;
case 1:
String res;
if ("android".equals(toString())) {
res = "hello";
} else {
try {
if (String.CASE_INSENSITIVE_ORDER != null) {
break;
}
res = "hi";
} catch (Exception e) {
break;
}
}
System.out.println(res);
}
System.out.println("returning");
}
}
@Test
@NotYetImplemented
public void test() {
getClassNode(TestCls.class);
}
}
package jadx.tests.integration.conditions;
import org.junit.jupiter.api.Test;
import jadx.NotYetImplemented;
import jadx.tests.api.IntegrationTest;
public class TestSwitchTryBreak2 extends IntegrationTest {
public static class TestCls {
public void test(int x) {
switch (x) {
case 0:
return;
case 1:
String res;
if ("android".equals(toString())) {
res = "hello";
} else {
try {
if (x == 5) {
break;
}
res = "hi";
} catch (Exception e) {
break;
}
}
System.out.println(res);
}
System.out.println("returning");
}
}
@Test
@NotYetImplemented
public void test() {
getClassNode(TestCls.class);
}
}
package jadx.tests.integration.enums;
import jadx.tests.api.IntegrationTest;
import jadx.tests.api.extensions.profiles.TestProfile;
import jadx.tests.api.extensions.profiles.TestWithProfiles;
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
public class TestEnumsWithTernary extends IntegrationTest {
public enum TestCls {
FIRST(useNumber() ? "1" : "A"),
SECOND(useNumber() ? "2" : "B"),
ANY(useNumber() ? "1" : "2");
private final String str;
TestCls(String str) {
this.str = str;
}
public String getStr() {
return str;
}
public static boolean useNumber() {
return false;
}
}
@TestWithProfiles({ TestProfile.DX_J8, TestProfile.D8_J8 })
public void test() {
noDebugInfo();
assertThat(getClassNode(TestCls.class))
.code()
.containsOne("ANY(useNumber() ? \"1\" : \"2\");")
.doesNotContain("static {");
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册