fix: improve internal state checks

This commit is contained in:
Skylot
2023-02-17 14:24:10 +00:00
parent cedcc29e01
commit c6ed117df6
37 changed files with 381 additions and 182 deletions
@@ -8,6 +8,7 @@ public class Consts {
public static final boolean DEBUG_OVERLOADED_CASTS = false;
public static final boolean DEBUG_EXC_HANDLERS = false;
public static final boolean DEBUG_FINALLY = false;
public static final boolean DEBUG_ATTRIBUTES = false;
public static final String CLASS_OBJECT = "java.lang.Object";
public static final String CLASS_STRING = "java.lang.String";
@@ -2,9 +2,13 @@ package jadx.core.dex.attributes;
import java.util.List;
import jadx.api.CommentsLevel;
import jadx.api.plugins.input.data.annotations.IAnnotation;
import jadx.api.plugins.input.data.attributes.IJadxAttrType;
import jadx.api.plugins.input.data.attributes.IJadxAttribute;
import jadx.core.Consts;
import jadx.core.dex.attributes.nodes.JadxCommentsAttr;
import jadx.core.utils.Utils;
public abstract class AttrNode implements IAttributeNode {
@@ -15,11 +19,18 @@ public abstract class AttrNode implements IAttributeNode {
@Override
public void add(AFlag flag) {
initStorage().add(flag);
if (Consts.DEBUG_ATTRIBUTES) {
addDebugComment("Add flag " + flag + " at " + Utils.currentStackTrace(2));
}
}
@Override
public void addAttr(IJadxAttribute attr) {
initStorage().add(attr);
if (Consts.DEBUG_ATTRIBUTES) {
addDebugComment("Add attribute " + attr.getClass().getSimpleName()
+ ": " + attr + " at " + Utils.currentStackTrace(2));
}
}
@Override
@@ -30,6 +41,9 @@ public abstract class AttrNode implements IAttributeNode {
@Override
public <T> void addAttr(IJadxAttrType<AttrList<T>> type, T obj) {
initStorage().add(type, obj);
if (Consts.DEBUG_ATTRIBUTES) {
addDebugComment("Add attribute " + obj + " at " + Utils.currentStackTrace(2));
}
}
public <T> void addAttr(IJadxAttrType<AttrList<T>> type, List<T> list) {
@@ -151,4 +165,13 @@ public abstract class AttrNode implements IAttributeNode {
public boolean isAttrStorageEmpty() {
return storage.isEmpty();
}
private void addDebugComment(String msg) {
JadxCommentsAttr commentsAttr = get(AType.JADX_COMMENTS);
if (commentsAttr == null) {
commentsAttr = new JadxCommentsAttr();
initStorage().add(commentsAttr);
}
commentsAttr.add(CommentsLevel.DEBUG, msg);
}
}
@@ -11,10 +11,25 @@ import jadx.api.CommentsLevel;
import jadx.api.plugins.input.data.attributes.IJadxAttrType;
import jadx.api.plugins.input.data.attributes.IJadxAttribute;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.attributes.IAttributeNode;
import jadx.core.utils.Utils;
public class JadxCommentsAttr implements IJadxAttribute {
public static void add(IAttributeNode node, CommentsLevel level, String comment) {
initFor(node).add(level, comment);
}
private static JadxCommentsAttr initFor(IAttributeNode node) {
JadxCommentsAttr currentAttr = node.get(AType.JADX_COMMENTS);
if (currentAttr != null) {
return currentAttr;
}
JadxCommentsAttr newAttr = new JadxCommentsAttr();
node.addAttr(newAttr);
return newAttr;
}
private final Map<CommentsLevel, List<String>> comments = new EnumMap<>(CommentsLevel.class);
public void add(CommentsLevel level, String comment) {
@@ -20,7 +20,7 @@ public abstract class NotificationAttrNode extends LineAttrNode implements ICode
public void addWarn(String warn) {
ErrorsCounter.warning(this, warn);
initCommentsAttr().add(CommentsLevel.WARN, warn);
JadxCommentsAttr.add(this, CommentsLevel.WARN, warn);
this.add(AFlag.INCONSISTENT_CODE);
}
@@ -29,32 +29,23 @@ public abstract class NotificationAttrNode extends LineAttrNode implements ICode
}
public void addWarnComment(String warn) {
initCommentsAttr().add(CommentsLevel.WARN, warn);
JadxCommentsAttr.add(this, CommentsLevel.WARN, warn);
}
public void addWarnComment(String warn, Throwable exc) {
String commentStr = warn + ICodeWriter.NL + Utils.getStackTrace(exc);
initCommentsAttr().add(CommentsLevel.WARN, commentStr);
JadxCommentsAttr.add(this, CommentsLevel.WARN, commentStr);
}
public void addInfoComment(String commentStr) {
initCommentsAttr().add(CommentsLevel.INFO, commentStr);
JadxCommentsAttr.add(this, CommentsLevel.INFO, commentStr);
}
public void addDebugComment(String commentStr) {
initCommentsAttr().add(CommentsLevel.DEBUG, commentStr);
JadxCommentsAttr.add(this, CommentsLevel.DEBUG, commentStr);
}
public CommentsLevel getCommentsLevel() {
return this.root().getArgs().getCommentsLevel();
}
private JadxCommentsAttr initCommentsAttr() {
JadxCommentsAttr commentsAttr = this.get(AType.JADX_COMMENTS);
if (commentsAttr == null) {
commentsAttr = new JadxCommentsAttr();
this.addAttr(commentsAttr);
}
return commentsAttr;
}
}
@@ -7,6 +7,7 @@ import jadx.api.ICodeWriter;
import jadx.api.plugins.input.data.attributes.IJadxAttribute;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.instructions.PhiInsn;
import jadx.core.dex.instructions.args.RegisterArg;
public class PhiListAttr implements IJadxAttribute {
@@ -24,12 +25,15 @@ public class PhiListAttr implements IJadxAttribute {
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("PHI: ");
sb.append("PHI:");
for (PhiInsn phiInsn : list) {
sb.append('r').append(phiInsn.getResult().getRegNum()).append(' ');
RegisterArg resArg = phiInsn.getResult();
if (resArg != null) {
sb.append(" r").append(resArg.getRegNum());
}
}
for (PhiInsn phiInsn : list) {
sb.append(ICodeWriter.NL).append(" ").append(phiInsn).append(' ').append(phiInsn.getAttributesString());
sb.append(ICodeWriter.NL).append(" ").append(phiInsn);
}
return sb.toString();
}
@@ -117,11 +117,20 @@ public class ArithNode extends InsnNode {
@Override
public String toString() {
return InsnUtils.formatOffset(offset) + ": "
+ InsnUtils.insnTypeToString(insnType)
+ getResult() + " = "
+ getArg(0) + ' '
+ op.getSymbol() + ' '
+ getArg(1);
StringBuilder sb = new StringBuilder();
sb.append(InsnUtils.formatOffset(offset));
sb.append(": ARITH ");
if (contains(AFlag.ARITH_ONEARG)) {
sb.append(getArg(0)).append(' ').append(op.getSymbol()).append("= ").append(getArg(1));
} else {
RegisterArg result = getResult();
if (result != null) {
sb.append(result).append(" = ");
}
sb.append(getArg(0)).append(' ').append(op.getSymbol()).append(' ').append(getArg(1));
}
appendAttributes(sb);
return sb.toString();
}
}
@@ -149,6 +149,7 @@ public class IfNode extends GotoNode {
return InsnUtils.formatOffset(offset) + ": "
+ InsnUtils.insnTypeToString(insnType)
+ getArg(0) + ' ' + op.getSymbol() + ' ' + getArg(1)
+ " -> " + (thenBlock != null ? thenBlock : InsnUtils.formatOffset(target));
+ " -> " + (thenBlock != null ? thenBlock : InsnUtils.formatOffset(target))
+ attributesString();
}
}
@@ -123,6 +123,7 @@ public class InvokeCustomNode extends InvokeNode {
sb.append(getResult()).append(" = ");
}
appendArgs(sb);
appendAttributes(sb);
sb.append("\n handle type: ").append(handleType);
sb.append("\n lambda: ").append(implMthInfo);
sb.append("\n call insn: ").append(callInsn);
@@ -92,6 +92,7 @@ public class InvokeCustomRawNode extends InvokeNode {
if (!appendArgs(sb)) {
sb.append('\n');
}
appendAttributes(sb);
sb.append(" call-site: \n ").append(Utils.listToString(callSiteValues, "\n ")).append('\n');
return sb.toString();
}
@@ -103,6 +103,6 @@ public class InvokeNode extends BaseInvokeNode {
@Override
public String toString() {
return super.toString() + " type: " + type + " call: " + mth;
return baseString() + " type: " + type + " call: " + mth + attributesString();
}
}
@@ -59,6 +59,7 @@ public class InvokePolymorphicNode extends InvokeNode {
if (!appendArgs(sb)) {
sb.append('\n');
}
appendAttributes(sb);
sb.append(" base: ").append(baseCallRef).append('\n');
sb.append(" proto: ").append(proto).append('\n');
return sb.toString();
@@ -14,7 +14,6 @@ import jadx.core.dex.instructions.args.SSAVar;
import jadx.core.dex.nodes.BlockNode;
import jadx.core.dex.nodes.InsnNode;
import jadx.core.utils.InsnRemover;
import jadx.core.utils.Utils;
import jadx.core.utils.exceptions.JadxRuntimeException;
public final class PhiInsn extends InsnNode {
@@ -134,7 +133,6 @@ public final class PhiInsn extends InsnNode {
@Override
public String toString() {
return "PHI: " + getResult() + " = " + Utils.listToString(getArguments())
+ " binds: " + blockBinds;
return baseString() + " binds: " + blockBinds + attributesString();
}
}
@@ -44,6 +44,7 @@ public class SwitchData extends InsnNode {
sb.append(keys[i]).append("->").append(InsnUtils.formatOffset(targets[i])).append(", ");
}
sb.append('}');
appendAttributes(sb);
return sb.toString();
}
}
@@ -103,7 +103,7 @@ public class SwitchInsn extends TargetInsnNode {
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(super.toString());
sb.append(baseString());
if (switchData == null) {
sb.append("no payload");
} else {
@@ -129,6 +129,7 @@ public class SwitchInsn extends TargetInsnNode {
}
}
}
appendAttributes(sb);
return sb.toString();
}
@@ -65,7 +65,13 @@ public class SSAVar {
}
public void setAssign(@NotNull RegisterArg assign) {
this.assign = assign;
RegisterArg oldAssign = this.assign;
if (oldAssign == null) {
this.assign = assign;
} else if (oldAssign != assign) {
oldAssign.resetSSAVar();
this.assign = assign;
}
}
public List<RegisterArg> getUseList() {
@@ -96,7 +96,7 @@ public final class TernaryInsn extends InsnNode {
@Override
public String toString() {
return InsnUtils.formatOffset(offset) + ": TERNARY"
return InsnUtils.formatOffset(offset) + ": TERNARY "
+ getResult() + " = (" + condition + ") ? " + getArg(0) + " : " + getArg(1);
}
}
@@ -472,7 +472,11 @@ public class InsnNode extends LineAttrNode {
public void rebindArgs() {
RegisterArg resArg = getResult();
if (resArg != null) {
resArg.getSVar().setAssign(resArg);
SSAVar ssaVar = resArg.getSVar();
if (ssaVar == null) {
throw new JadxRuntimeException("No SSA var for result arg: " + resArg + " from " + resArg.getParentInsn());
}
ssaVar.setAssign(resArg);
}
for (InsnArg arg : getArguments()) {
if (arg instanceof RegisterArg) {
@@ -560,16 +564,36 @@ public class InsnNode extends LineAttrNode {
return true;
}
@Override
public String toString() {
protected String attributesString() {
StringBuilder sb = new StringBuilder();
sb.append(InsnUtils.formatOffset(offset));
sb.append(": ");
sb.append(InsnUtils.insnTypeToString(insnType));
appendAttributes(sb);
return sb.toString();
}
protected void appendAttributes(StringBuilder sb) {
if (!isAttrStorageEmpty()) {
sb.append(' ').append(getAttributesString());
}
if (getSourceLine() != 0) {
sb.append(" (LINE:").append(getSourceLine()).append(')');
}
}
protected String baseString() {
StringBuilder sb = new StringBuilder();
if (offset != -1) {
sb.append(InsnUtils.formatOffset(offset)).append(": ");
}
sb.append(insnType).append(' ');
if (result != null) {
sb.append(result).append(" = ");
}
appendArgs(sb);
return sb.toString();
}
@Override
public String toString() {
return baseString() + attributesString();
}
}
@@ -37,6 +37,7 @@ import jadx.core.dex.instructions.args.SSAVar;
import jadx.core.dex.nodes.utils.TypeUtils;
import jadx.core.dex.regions.Region;
import jadx.core.dex.trycatch.ExceptionHandler;
import jadx.core.dex.visitors.InitCodeVariables;
import jadx.core.utils.Utils;
import jadx.core.utils.exceptions.DecodeException;
import jadx.core.utils.exceptions.JadxRuntimeException;
@@ -519,6 +520,24 @@ public class MethodNode extends NotificationAttrNode implements IMethodDetails,
return argsStartReg;
}
/**
* Create new fake register arg.
*/
public RegisterArg makeSyntheticRegArg(ArgType type) {
RegisterArg arg = InsnArg.reg(0, type);
arg.add(AFlag.SYNTHETIC);
SSAVar ssaVar = makeNewSVar(arg);
InitCodeVariables.initCodeVar(ssaVar);
ssaVar.setType(type);
return arg;
}
public RegisterArg makeSyntheticRegArg(ArgType type, String name) {
RegisterArg arg = makeSyntheticRegArg(type);
arg.setName(name);
return arg;
}
public SSAVar makeNewSVar(@NotNull RegisterArg assignArg) {
int regNum = assignArg.getRegNum();
return makeNewSVar(regNum, getNextSVarVersion(regNum), assignArg);
@@ -10,10 +10,12 @@ import jadx.core.clsp.ClspMethod;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.attributes.nodes.MethodBridgeAttr;
import jadx.core.dex.attributes.nodes.MethodOverrideAttr;
import jadx.core.dex.attributes.nodes.SkipMethodArgsAttr;
import jadx.core.dex.info.ClassInfo;
import jadx.core.dex.info.MethodInfo;
import jadx.core.dex.instructions.BaseInvokeNode;
import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.instructions.args.InsnArg;
import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.IMethodDetails;
import jadx.core.dex.nodes.MethodNode;
@@ -54,6 +56,19 @@ public class MethodUtils {
return null;
}
public boolean isSkipArg(BaseInvokeNode invokeNode, InsnArg arg) {
MethodNode mth = resolveMethod(invokeNode);
if (mth == null) {
return false;
}
SkipMethodArgsAttr skipArgsAttr = mth.get(AType.SKIP_MTH_ARGS);
if (skipArgsAttr == null) {
return false;
}
int argIndex = invokeNode.getArgIndex(arg);
return skipArgsAttr.isSkip(argIndex);
}
/**
* Search methods with same name and args count in class hierarchy starting from {@code startCls}
* Beware {@code startCls} can be different from {@code mthInfo.getDeclClass()}
@@ -13,7 +13,7 @@ public final class ForEachLoop extends LoopType {
public ForEachLoop(RegisterArg varArg, InsnArg iterableArg) {
// store for-each args in fake instructions to
// save code semantics and allow args manipulations like args inlining
varArgInsn = new InsnNode(InsnType.REGION_ARG, 1);
varArgInsn = new InsnNode(InsnType.REGION_ARG, 0);
varArgInsn.add(AFlag.DONT_INLINE);
varArgInsn.setResult(varArg.duplicate());
@@ -79,8 +79,9 @@ public class ClassModifier extends AbstractVisitor {
boolean inline = cls.isAnonymous();
if (inline || cls.getClassInfo().isInner()) {
for (FieldNode field : cls.getFields()) {
if (field.getAccessFlags().isSynthetic() && field.getType().isObject()) {
ClassInfo clsInfo = ClassInfo.fromType(cls.root(), field.getType());
ArgType fldType = field.getType();
if (field.getAccessFlags().isSynthetic() && fldType.isObject() && !fldType.isGenericType()) {
ClassInfo clsInfo = ClassInfo.fromType(cls.root(), fldType);
ClassNode fieldsCls = cls.root().resolveClass(clsInfo);
ClassInfo parentClass = cls.getClassInfo().getParentClass();
if (fieldsCls != null
@@ -1,7 +1,5 @@
package jadx.core.dex.visitors;
import java.util.ArrayList;
import org.jetbrains.annotations.Nullable;
import jadx.core.codegen.TypeGen;
@@ -71,7 +69,7 @@ public class ConstructorVisitor extends AbstractVisitor {
co.inheritMetadata(inv);
RegisterArg instanceArg = ((RegisterArg) inv.getArg(0));
InsnNode newInstInsn = null;
instanceArg.getSVar().removeUse(instanceArg);
if (co.isNewInstance()) {
InsnNode assignInsn = instanceArg.getAssignInsn();
if (assignInsn != null) {
@@ -79,8 +77,9 @@ public class ConstructorVisitor extends AbstractVisitor {
// arg already used in another constructor instruction
mth.add(AFlag.RERUN_SSA_TRANSFORM);
} else {
newInstInsn = removeAssignChain(mth, assignInsn, remover, InsnType.NEW_INSTANCE);
InsnNode newInstInsn = removeAssignChain(mth, assignInsn, remover, InsnType.NEW_INSTANCE);
if (newInstInsn != null) {
co.inheritMetadata(newInstInsn);
newInstInsn.add(AFlag.REMOVE);
remover.addWithoutUnbind(newInstInsn);
}
@@ -89,23 +88,7 @@ public class ConstructorVisitor extends AbstractVisitor {
// convert instance arg from 'use' to 'assign'
co.setResult(instanceArg.duplicate());
}
instanceArg.getSVar().removeUse(instanceArg);
co.rebindArgs();
if (co.isNewInstance() && 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<>(instArg.getSVar().getUseList())) {
InsnNode parentInsn = useArg.getParentInsn();
if (parentInsn != null) {
parentInsn.replaceArg(useArg, resultArg.duplicate());
}
}
}
co.inheritMetadata(newInstInsn);
}
ConstructorInsn replace = processConstructor(mth, co);
if (replace != null) {
remover.addAndUnbind(co);
@@ -284,15 +284,11 @@ public class DotGraphVisitor extends AbstractVisitor {
private String insertInsns(MethodNode mth, IBlock block) {
if (rawInsn) {
StringBuilder str = new StringBuilder();
StringBuilder sb = new StringBuilder();
for (InsnNode insn : block.getInstructions()) {
str.append(escape(insn + " " + insn.getAttributesString()));
if (insn.getSourceLine() != 0) {
str.append(" (LINE:").append(insn.getSourceLine()).append(')');
}
str.append(NL);
sb.append(escape(insn)).append(NL);
}
return str.toString();
return sb.toString();
} else {
ICodeWriter code = new SimpleCodeWriter();
List<InsnNode> instructions = block.getInstructions();
@@ -15,10 +15,8 @@ import jadx.core.dex.instructions.BaseInvokeNode;
import jadx.core.dex.instructions.IndexInsnNode;
import jadx.core.dex.instructions.InsnType;
import jadx.core.dex.instructions.InvokeNode;
import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.instructions.args.InsnArg;
import jadx.core.dex.instructions.args.RegisterArg;
import jadx.core.dex.instructions.args.SSAVar;
import jadx.core.dex.nodes.BlockNode;
import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.FieldNode;
@@ -84,7 +82,7 @@ public class InlineMethods extends AbstractVisitor {
inlCopy.setResult(resultArg.duplicate());
} else if (isAssignNeeded(mia.getInsn(), insn, callMth)) {
// add fake result to make correct java expression (see test TestGetterInlineNegative)
inlCopy.setResult(makeFakeArg(mth, callMth.getReturnType(), "unused"));
inlCopy.setResult(mth.makeSyntheticRegArg(callMth.getReturnType(), "unused"));
}
if (!callMth.getMethodInfo().getArgumentsTypes().isEmpty()) {
// remap args
@@ -135,15 +133,6 @@ public class InlineMethods extends AbstractVisitor {
return !callMthNode.isVoidReturn();
}
private RegisterArg makeFakeArg(MethodNode mth, ArgType varType, String name) {
RegisterArg fakeArg = RegisterArg.reg(0, varType);
SSAVar ssaVar = mth.makeNewSVar(fakeArg);
InitCodeVariables.initCodeVar(ssaVar);
fakeArg.setName(name);
ssaVar.setType(varType);
return fakeArg;
}
private void updateUsageInfo(MethodNode mth, MethodNode inlinedMth, InsnNode insn) {
inlinedMth.getUseIn().remove(mth);
insn.visitInsns(innerInsn -> {
@@ -531,7 +531,7 @@ public class ModVisitor extends AbstractVisitor {
List<LiteralArg> list = insn.getLiteralArgs(elType);
InsnNode filledArr = new FilledNewArrayNode(elType, list.size());
filledArr.setResult(newArrayNode.getResult());
filledArr.setResult(newArrayNode.getResult().duplicate());
for (LiteralArg arg : list) {
FieldNode f = mth.getParentClass().getConstFieldByLiteralArg(arg);
if (f != null) {
@@ -539,7 +539,7 @@ public class ModVisitor extends AbstractVisitor {
filledArr.addArg(InsnArg.wrapArg(fGet));
f.addUseIn(mth);
} else {
filledArr.addArg(arg);
filledArr.addArg(arg.duplicate());
}
}
return filledArr;
@@ -31,6 +31,7 @@ import jadx.core.dex.nodes.InsnNode;
import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.nodes.RootNode;
import jadx.core.dex.visitors.shrink.CodeShrinkVisitor;
import jadx.core.utils.BlockUtils;
import jadx.core.utils.InsnList;
import jadx.core.utils.InsnRemover;
import jadx.core.utils.InsnUtils;
@@ -165,6 +166,9 @@ public class ReSugarCode extends AbstractVisitor {
// checks complete, apply
InsnNode filledArr = new FilledNewArrayNode(elemType, len);
filledArr.setResult(arrArg.duplicate());
filledArr.copyAttributesFrom(newArrayInsn);
filledArr.inheritMetadata(newArrayInsn);
filledArr.setOffset(newArrayInsn.getOffset());
long prevIndex = -1;
for (Map.Entry<Long, InsnNode> entry : arrPuts.entrySet()) {
@@ -185,6 +189,7 @@ public class ReSugarCode extends AbstractVisitor {
InsnNode lastPut = arrPuts.get(arrPuts.lastKey());
int replaceIndex = InsnList.getIndex(instructions, lastPut);
instructions.set(replaceIndex, filledArr);
BlockUtils.replaceInsn(mth, lastPut, filledArr);
return true;
}
@@ -113,7 +113,7 @@ public class SimplifyVisitor extends AbstractVisitor {
InsnNode wrapInsn = ((InsnWrapArg) arg).getWrapInsn();
InsnNode replaceInsn = simplifyInsn(mth, wrapInsn, insn);
if (replaceInsn != null) {
arg.wrapInstruction(mth, replaceInsn);
arg.wrapInstruction(mth, replaceInsn, false);
InsnRemover.unbindInsn(mth, wrapInsn);
changed = true;
}
@@ -406,17 +406,18 @@ public class SimplifyVisitor extends AbstractVisitor {
}
// all check passed
removeStringBuilderInsns(mth, toStrInsn, chain);
List<InsnArg> dupArgs = Utils.collectionMap(args, InsnArg::duplicate);
List<InsnArg> simplifiedArgs = concatConstArgs(dupArgs);
InsnNode concatInsn = new InsnNode(InsnType.STR_CONCAT, simplifiedArgs);
concatInsn.setResult(toStrInsn.getResult());
concatInsn.add(AFlag.SYNTHETIC);
if (toStrInsn.getResult() == null && !toStrInsn.contains(AFlag.WRAPPED)) {
// string concat without assign to variable will cause compilation error
concatInsn.setResult(mth.makeSyntheticRegArg(ArgType.STRING));
} else {
concatInsn.setResult(toStrInsn.getResult());
}
concatInsn.copyAttributesFrom(toStrInsn);
concatInsn.remove(AFlag.DONT_GENERATE);
concatInsn.remove(AFlag.REMOVE);
checkResult(mth, concatInsn);
removeStringBuilderInsns(mth, toStrInsn, chain);
return concatInsn;
} catch (Exception e) {
mth.addWarnComment("String concatenation convert failed", e);
@@ -485,17 +486,6 @@ public class SimplifyVisitor extends AbstractVisitor {
return null;
}
/* String concat without assign to variable will cause compilation error */
private static void checkResult(MethodNode mth, InsnNode concatInsn) {
if (concatInsn.getResult() == null) {
RegisterArg resArg = InsnArg.reg(0, ArgType.STRING);
SSAVar ssaVar = mth.makeNewSVar(resArg);
InitCodeVariables.initCodeVar(ssaVar);
ssaVar.setType(ArgType.STRING);
concatInsn.setResult(resArg);
}
}
/**
* Remove and unbind all instructions with StringBuilder
*/
@@ -210,11 +210,24 @@ public class LoopRegionVisitor extends AbstractVisitor implements IRegionVisitor
return null;
}
RegisterArg iterVar = arrGetInsn.getResult();
if (iterVar == null) {
return null;
}
if (!usedOnlyInLoop(mth, loopRegion, iterVar)) {
return null;
if (iterVar != null) {
if (!usedOnlyInLoop(mth, loopRegion, iterVar)) {
return null;
}
} else {
if (!arrGetInsn.contains(AFlag.WRAPPED)) {
return null;
}
// create new variable and replace wrapped insn
InsnArg wrapArg = BlockUtils.searchWrappedInsnParent(mth, arrGetInsn);
if (wrapArg == null || wrapArg.getParentInsn() == null) {
mth.addWarnComment("checkArrayForEach: Wrapped insn not found: " + arrGetInsn);
return null;
}
iterVar = mth.makeSyntheticRegArg(wrapArg.getType());
InsnNode parentInsn = wrapArg.getParentInsn();
parentInsn.replaceArg(wrapArg, iterVar.duplicate());
parentInsn.rebindArgs();
}
// array for each loop confirmed
@@ -224,16 +237,6 @@ public class LoopRegionVisitor extends AbstractVisitor implements IRegionVisitor
arrGetInsn.add(AFlag.DONT_GENERATE);
compare.getInsn().add(AFlag.DONT_GENERATE);
if (arrGetInsn.contains(AFlag.WRAPPED)) {
InsnArg wrapArg = BlockUtils.searchWrappedInsnParent(mth, arrGetInsn);
if (wrapArg != null && wrapArg.getParentInsn() != null) {
InsnNode parentInsn = wrapArg.getParentInsn();
parentInsn.replaceArg(wrapArg, iterVar.duplicate());
parentInsn.rebindArgs();
} else {
LOG.debug(" checkArrayForEach: Wrapped insn not found: {}, mth: {}", arrGetInsn, mth);
}
}
ForEachLoop forEachLoop = new ForEachLoop(iterVar, len.getArg(0));
forEachLoop.injectFakeInsns(loopRegion);
if (InsnUtils.dontGenerateIfNotUsed(len)) {
@@ -327,6 +330,7 @@ public class LoopRegionVisitor extends AbstractVisitor implements IRegionVisitor
assignInsn.getResult().add(AFlag.DONT_GENERATE);
for (InsnNode insnNode : toSkip) {
insnNode.setResult(null);
insnNode.add(AFlag.DONT_GENERATE);
}
for (RegisterArg itArg : itUseList) {
@@ -287,7 +287,7 @@ public class TernaryMod extends AbstractRegionVisitor implements IRegionIterativ
}
RegisterArg otherArg = null;
for (InsnArg arg : phiInsn.getArguments()) {
if (arg != resArg && arg instanceof RegisterArg) {
if (!resArg.sameRegAndSVar(arg)) {
otherArg = (RegisterArg) arg;
break;
}
@@ -326,15 +326,16 @@ public class TernaryMod extends AbstractRegionVisitor implements IRegionIterativ
}
elseArg = InsnArg.wrapInsnIntoArg(elseAssign);
} else {
elseArg = otherArg;
elseArg = otherArg.duplicate();
}
TernaryInsn ternInsn = new TernaryInsn(ifRegion.getCondition(),
phiInsn.getResult(), InsnArg.wrapInsnIntoArg(insn), elseArg);
InsnArg thenArg = InsnArg.wrapInsnIntoArg(insn);
RegisterArg resultArg = phiInsn.getResult().duplicate();
TernaryInsn ternInsn = new TernaryInsn(ifRegion.getCondition(), resultArg, thenArg, elseArg);
ternInsn.simplifyCondition();
InsnRemover.unbindAllArgs(mth, phiInsn);
InsnRemover.unbindResult(mth, insn);
InsnList.remove(block, insn);
InsnRemover.unbindAllArgs(mth, phiInsn);
header.getInstructions().clear();
ternInsn.rebindArgs();
header.getInstructions().add(ternInsn);
@@ -8,12 +8,14 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.attributes.nodes.DeclareVariablesAttr;
import jadx.core.dex.instructions.BaseInvokeNode;
import jadx.core.dex.instructions.InsnType;
import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.instructions.args.CodeVar;
@@ -30,6 +32,7 @@ import jadx.core.dex.visitors.regions.AbstractRegionVisitor;
import jadx.core.dex.visitors.regions.DepthRegionTraversal;
import jadx.core.dex.visitors.typeinference.TypeCompare;
import jadx.core.dex.visitors.typeinference.TypeCompareEnum;
import jadx.core.utils.ListUtils;
import jadx.core.utils.RegionUtils;
import jadx.core.utils.Utils;
import jadx.core.utils.exceptions.JadxException;
@@ -72,15 +75,59 @@ public class ProcessVariables extends AbstractVisitor {
public void processBlock(MethodNode mth, IBlock container) {
for (InsnNode insn : container.getInstructions()) {
RegisterArg resultArg = insn.getResult();
if (resultArg != null) {
SSAVar ssaVar = resultArg.getSVar();
if (ssaVar.getUseList().isEmpty() && insn.canRemoveResult()) {
if (resultArg == null) {
continue;
}
SSAVar ssaVar = resultArg.getSVar();
if (isVarUnused(mth, ssaVar)) {
boolean remove = false;
if (insn.canRemoveResult()) {
// remove unused result
remove = true;
} else if (insn.isConstInsn()) {
// remove whole insn
insn.add(AFlag.REMOVE);
insn.add(AFlag.DONT_GENERATE);
remove = true;
}
if (remove) {
insn.setResult(null);
mth.removeSVar(ssaVar);
for (RegisterArg arg : ssaVar.getUseList()) {
arg.resetSSAVar();
}
}
}
}
}
private boolean isVarUnused(MethodNode mth, @Nullable SSAVar ssaVar) {
if (ssaVar == null) {
return true;
}
List<RegisterArg> useList = ssaVar.getUseList();
if (useList.isEmpty()) {
return true;
}
if (ssaVar.isUsedInPhi()) {
return false;
}
return ListUtils.allMatch(useList, arg -> isArgUnused(mth, arg));
}
private boolean isArgUnused(MethodNode mth, RegisterArg arg) {
if (arg.contains(AFlag.REMOVE) || arg.contains(AFlag.SKIP_ARG)) {
return true;
}
InsnNode parentInsn = arg.getParentInsn();
if (parentInsn instanceof BaseInvokeNode
&& mth.root().getMethodUtils().isSkipArg(((BaseInvokeNode) parentInsn), arg)) {
arg.add(AFlag.DONT_GENERATE);
arg.add(AFlag.REMOVE);
return true;
}
return false;
}
});
}
@@ -18,9 +18,12 @@ import jadx.core.dex.instructions.args.SSAVar;
import jadx.core.dex.nodes.BlockNode;
import jadx.core.dex.nodes.InsnNode;
import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.trycatch.CatchAttr;
import jadx.core.dex.trycatch.ExcHandlerAttr;
import jadx.core.dex.visitors.AbstractVisitor;
import jadx.core.dex.visitors.JadxVisitor;
import jadx.core.dex.visitors.blocks.BlockProcessor;
import jadx.core.utils.BlockUtils;
import jadx.core.utils.InsnList;
import jadx.core.utils.InsnRemover;
import jadx.core.utils.exceptions.JadxException;
@@ -58,20 +61,10 @@ public class SSATransform extends AbstractVisitor {
placePhi(mth, i, la);
}
renameVariables(mth);
fixLastAssignInTry(mth);
removeBlockerInsns(mth);
markThisArgs(mth.getThisArg());
boolean repeatFix;
int k = 0;
do {
repeatFix = fixUselessPhi(mth);
if (k++ > 50) {
throw new JadxRuntimeException("Phi nodes fix limit reached!");
}
} while (repeatFix);
tryToFixUselessPhi(mth);
hidePhiInsns(mth);
removeUnusedInvokeResults(mth);
}
@@ -208,29 +201,42 @@ public class SSATransform extends AbstractVisitor {
private static void fixLastAssignInTry(MethodNode mth) {
for (BlockNode block : mth.getBasicBlocks()) {
PhiListAttr phiList = block.get(AType.PHI_LIST);
if (phiList != null && block.contains(AType.EXC_HANDLER)) {
for (PhiInsn phi : phiList.getList()) {
fixPhiInTryCatch(phi);
if (phiList != null) {
ExcHandlerAttr handlerAttr = block.get(AType.EXC_HANDLER);
if (handlerAttr != null) {
for (PhiInsn phi : phiList.getList()) {
fixPhiInTryCatch(mth, phi, handlerAttr);
}
}
}
}
}
private static void fixPhiInTryCatch(PhiInsn phi) {
private static void fixPhiInTryCatch(MethodNode mth, PhiInsn phi, ExcHandlerAttr handlerAttr) {
int argsCount = phi.getArgsCount();
int k = 0;
while (k < argsCount) {
RegisterArg arg = phi.getArg(k);
InsnNode parentInsn = arg.getAssignInsn();
if (parentInsn != null
&& parentInsn.getResult() != null
&& parentInsn.contains(AFlag.TRY_LEAVE)
&& phi.removeArg(arg) /* TODO: fix registers removing */) {
if (shouldSkipInsnResult(mth, arg.getAssignInsn(), handlerAttr)) {
phi.removeArg(arg);
argsCount--;
continue;
} else {
k++;
}
k++;
}
if (phi.getArgsCount() == 0) {
throw new JadxRuntimeException("PHI empty after try-catch fix!");
}
}
private static boolean shouldSkipInsnResult(MethodNode mth, InsnNode insn, ExcHandlerAttr handlerAttr) {
if (insn != null
&& insn.getResult() != null
&& insn.contains(AFlag.TRY_LEAVE)) {
CatchAttr catchAttr = BlockUtils.getCatchAttrForInsn(mth, insn);
return catchAttr != null && catchAttr.getHandlers().contains(handlerAttr.getHandler());
}
return false;
}
private static boolean removeBlockerInsns(MethodNode mth) {
@@ -256,6 +262,16 @@ public class SSATransform extends AbstractVisitor {
return removed;
}
private static void tryToFixUselessPhi(MethodNode mth) {
int k = 0;
int maxTries = mth.getSVars().size() * 2;
while (fixUselessPhi(mth)) {
if (k++ > maxTries) {
throw new JadxRuntimeException("Phi nodes fix limit reached!");
}
}
}
private static boolean fixUselessPhi(MethodNode mth) {
boolean changed = false;
List<PhiInsn> insnToRemove = new ArrayList<>();
@@ -299,10 +315,10 @@ public class SSATransform extends AbstractVisitor {
return true;
}
boolean allSame = phi.getArgsCount() == 1 || isSameArgs(phi);
if (!allSame) {
return false;
if (allSame) {
return replacePhiWithMove(mth, block, phi, phi.getArg(0));
}
return replacePhiWithMove(mth, block, phi, phi.getArg(0));
return false;
}
private static boolean isSameArgs(PhiInsn phi) {
@@ -638,9 +638,8 @@ public final class TypeInferenceVisitor extends AbstractVisitor {
if (assignBlock == null) {
return false;
}
RegisterArg newAssignArg = assignArg.duplicateWithNewSSAVar(mth);
assignInsn.setResult(newAssignArg);
IndexInsnNode castInsn = makeSoftCastInsn(assignArg, newAssignArg, castType);
assignInsn.setResult(assignArg.duplicateWithNewSSAVar(mth));
IndexInsnNode castInsn = makeSoftCastInsn(assignArg.duplicate(), assignInsn.getResult().duplicate(), castType);
return BlockUtils.insertAfterInsn(assignBlock, assignInsn, castInsn);
}
@@ -657,18 +656,19 @@ public final class TypeInferenceVisitor extends AbstractVisitor {
if (useBlock == null) {
return false;
}
RegisterArg newUseArg = useArg.duplicateWithNewSSAVar(mth);
useInsn.replaceArg(useArg, newUseArg);
IndexInsnNode castInsn = makeSoftCastInsn(newUseArg, useArg, useArg.getInitType());
IndexInsnNode castInsn = makeSoftCastInsn(
useArg.duplicateWithNewSSAVar(mth),
useArg.duplicate(),
useArg.getInitType());
useInsn.replaceArg(useArg, castInsn.getResult().duplicate());
return BlockUtils.insertBeforeInsn(useBlock, useInsn, castInsn);
}
@NotNull
private IndexInsnNode makeSoftCastInsn(RegisterArg result, RegisterArg arg, ArgType castType) {
IndexInsnNode castInsn = new IndexInsnNode(InsnType.CHECK_CAST, castType, 1);
castInsn.setResult(result.duplicate());
castInsn.addArg(arg.duplicate());
castInsn.setResult(result);
castInsn.addArg(arg);
castInsn.add(AFlag.SOFT_CAST);
castInsn.add(AFlag.SYNTHETIC);
return castInsn;
@@ -35,6 +35,7 @@ import jadx.core.dex.nodes.IBlock;
import jadx.core.dex.nodes.InsnNode;
import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.regions.conditions.IfCondition;
import jadx.core.dex.trycatch.CatchAttr;
import jadx.core.dex.trycatch.ExceptionHandler;
import jadx.core.utils.exceptions.JadxRuntimeException;
@@ -410,6 +411,12 @@ public class BlockUtils {
return s.isEmpty() ? null : s.get(0);
}
@Nullable
public static BlockNode getPrevBlock(BlockNode block) {
List<BlockNode> preds = block.getPredecessors();
return preds.size() == 1 ? preds.get(0) : null;
}
/**
* Return successor on path to 'pathEnd' block
*/
@@ -1245,4 +1252,16 @@ public class BlockUtils {
}
return null;
}
public static @Nullable CatchAttr getCatchAttrForInsn(MethodNode mth, InsnNode insn) {
CatchAttr catchAttr = insn.get(AType.EXC_CATCH);
if (catchAttr != null) {
return catchAttr;
}
BlockNode block = getBlockByInsn(mth, insn);
if (block == null) {
return null;
}
return block.get(AType.EXC_CATCH);
}
}
@@ -80,16 +80,30 @@ public class DebugChecks {
SSAVar sVar = reg.getSVar();
if (sVar == null) {
if (reg.contains(AFlag.DONT_GENERATE) || insn.contains(AFlag.DONT_GENERATE)) {
return;
}
if (Utils.notEmpty(mth.getSVars())) {
throw new JadxRuntimeException("Null SSA var in " + insn + ", mth: " + mth);
throw new JadxRuntimeException("Null SSA var in " + reg + " at " + insn);
}
return;
}
if (Utils.indexInListByRef(mth.getSVars(), sVar) == -1) {
throw new JadxRuntimeException("SSA var not present in method vars list, var: " + sVar + " from insn: " + insn);
}
RegisterArg resArg = insn.getResult();
List<RegisterArg> useList = sVar.getUseList();
boolean assignReg = insn.getResult() == reg;
if (!assignReg && !Utils.containsInListByRef(useList, reg)) {
throw new JadxRuntimeException("Incorrect use list in ssa var: " + sVar + ", register not listed."
+ ICodeWriter.NL + " insn: " + insn);
if (resArg == reg) {
if (sVar.getAssignInsn() != insn) {
throw new JadxRuntimeException("Incorrect assign in ssa var: " + sVar
+ ICodeWriter.NL + " expected: " + sVar.getAssignInsn()
+ ICodeWriter.NL + " got: " + insn);
}
} else {
if (!Utils.containsInListByRef(useList, reg)) {
throw new JadxRuntimeException("Incorrect use list in ssa var: " + sVar + ", register not listed."
+ ICodeWriter.NL + " insn: " + insn);
}
}
for (RegisterArg useArg : useList) {
checkRegisterArg(mth, useArg);
@@ -22,6 +22,9 @@ import jadx.core.dex.nodes.InsnNode;
import jadx.core.dex.nodes.MethodNode;
import jadx.core.utils.exceptions.JadxRuntimeException;
import static jadx.core.utils.InsnUtils.isInsnType;
import static jadx.core.utils.ListUtils.allMatch;
/**
* Helper class for correct instructions removing,
* can be used while iterating over instructions list
@@ -108,14 +111,13 @@ public class InsnRemover {
if (r == null) {
return;
}
r.add(AFlag.REMOVE); // don't unset result arg, can be used to restore variable
if (mth == null) {
return;
}
SSAVar ssaVar = r.getSVar();
if (ssaVar != null && ssaVar.getAssign() == insn.getResult()) {
removeSsaVar(mth, ssaVar);
if (mth != null) {
SSAVar ssaVar = r.getSVar();
if (ssaVar != null && ssaVar.getAssignInsn() == insn /* can be already reassigned */) {
removeSsaVar(mth, ssaVar);
}
}
insn.setResult(null);
}
private static void removeSsaVar(MethodNode mth, SSAVar ssaVar) {
@@ -125,15 +127,7 @@ public class InsnRemover {
return;
}
// check if all usage only in PHI insns
boolean allPhis = true;
for (RegisterArg arg : ssaVar.getUseList()) {
InsnNode parentInsn = arg.getParentInsn();
if (parentInsn == null || parentInsn.getType() != InsnType.PHI) {
allPhis = false;
break;
}
}
if (allPhis) {
if (allMatch(ssaVar.getUseList(), arg -> isInsnType(arg.getParentInsn(), InsnType.PHI))) {
for (RegisterArg arg : new ArrayList<>(ssaVar.getUseList())) {
InsnNode parentInsn = arg.getParentInsn();
if (parentInsn != null) {
@@ -143,12 +137,19 @@ public class InsnRemover {
mth.removeSVar(ssaVar);
return;
}
if (Consts.DEBUG_WITH_ERRORS) {
throw new JadxRuntimeException("Can't remove SSA var, still in use, count: " + useCount + ", list:"
+ ICodeWriter.NL + " " + ssaVar.getUseList().stream()
.map(arg -> arg + " from " + arg.getParentInsn())
.collect(Collectors.joining(ICodeWriter.NL + " ")));
// check if all usage only in not generated instructions
if (allMatch(ssaVar.getUseList(),
arg -> arg.contains(AFlag.DONT_GENERATE) || (InsnUtils.contains(arg.getParentInsn(), AFlag.DONT_GENERATE)))) {
for (RegisterArg arg : ssaVar.getUseList()) {
arg.resetSSAVar();
}
mth.removeSVar(ssaVar);
return;
}
throw new JadxRuntimeException("Can't remove SSA var: " + ssaVar + ", still in use, count: " + useCount
+ ", list:" + ICodeWriter.NL + " " + ssaVar.getUseList().stream()
.map(arg -> arg + " from " + arg.getParentInsn())
.collect(Collectors.joining(ICodeWriter.NL + " ")));
}
public static void unbindArgUsage(@Nullable MethodNode mth, InsnArg arg) {
@@ -215,6 +215,10 @@ public class InsnUtils {
return null;
}
public static boolean isInsnType(@Nullable InsnNode insn, InsnType insnType) {
return insn != null && insn.getType() == insnType;
}
@Nullable
public static InsnNode getWrappedInsn(InsnArg arg) {
if (arg != null && arg.isInsnWrap()) {
@@ -250,4 +254,8 @@ public class InsnUtils {
}
return false;
}
public static boolean contains(InsnNode insn, AFlag flag) {
return insn != null && insn.contains(flag);
}
}
@@ -119,6 +119,20 @@ public class Utils {
return sb.toString();
}
public static String currentStackTrace() {
return getStackTrace(new Exception());
}
public static String currentStackTrace(int skipFrames) {
Exception e = new Exception();
StackTraceElement[] stackTrace = e.getStackTrace();
int len = stackTrace.length;
if (skipFrames < len) {
e.setStackTrace(Arrays.copyOfRange(stackTrace, skipFrames, len));
}
return getStackTrace(e);
}
public static String getFullStackTrace(Throwable throwable) {
return getStackTrace(throwable, false);
}