fix: improve internal state checks
This commit is contained in:
@@ -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);
|
||||
|
||||
+50
-3
@@ -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) {
|
||||
|
||||
+9
-9
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user