fix: don't duplicate result arg with instruction copy (breaks SSA variable assign)
This commit is contained in:
@@ -71,6 +71,7 @@ public class Jadx {
|
||||
passes.add(new DebugInfoApplyVisitor());
|
||||
}
|
||||
|
||||
passes.add(new GenericTypesVisitor());
|
||||
passes.add(new DeboxingVisitor());
|
||||
passes.add(new ModVisitor());
|
||||
passes.add(new CodeShrinkVisitor());
|
||||
|
||||
@@ -15,6 +15,7 @@ import jadx.core.deobf.NameMapper;
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.nodes.FieldReplaceAttr;
|
||||
import jadx.core.dex.attributes.nodes.GenericInfoAttr;
|
||||
import jadx.core.dex.attributes.nodes.LoopLabelAttr;
|
||||
import jadx.core.dex.attributes.nodes.MethodInlineAttr;
|
||||
import jadx.core.dex.attributes.nodes.SkipMethodArgsAttr;
|
||||
@@ -603,8 +604,7 @@ public class InsnGen {
|
||||
code.add('}');
|
||||
}
|
||||
|
||||
private void makeConstructor(ConstructorInsn insn, CodeWriter code)
|
||||
throws CodegenException {
|
||||
private void makeConstructor(ConstructorInsn insn, CodeWriter code) throws CodegenException {
|
||||
ClassNode cls = mth.dex().resolveClass(insn.getClassType());
|
||||
if (cls != null && cls.isAnonymous() && !fallback) {
|
||||
cls.ensureProcessed();
|
||||
@@ -622,20 +622,18 @@ public class InsnGen {
|
||||
} else {
|
||||
code.add("new ");
|
||||
useClass(code, insn.getClassType());
|
||||
ArgType argType = insn.getResult().getSVar().getCodeVar().getType();
|
||||
boolean genericCls = cls == null || !cls.getGenericTypeParameters().isEmpty();
|
||||
if (argType != null
|
||||
&& argType.getGenericTypes() != null
|
||||
&& genericCls) {
|
||||
GenericInfoAttr genericInfoAttr = insn.get(AType.GENERIC_INFO);
|
||||
if (genericInfoAttr != null) {
|
||||
code.add('<');
|
||||
if (insn.contains(AFlag.EXPLICIT_GENERICS)) {
|
||||
if (genericInfoAttr.isExplicit()) {
|
||||
boolean first = true;
|
||||
for (ArgType type : argType.getGenericTypes()) {
|
||||
for (ArgType type : genericInfoAttr.getGenericTypes()) {
|
||||
if (!first) {
|
||||
code.add(',');
|
||||
} else {
|
||||
first = false;
|
||||
}
|
||||
mgen.getClassGen().useType(code, type);
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
code.add('>');
|
||||
@@ -849,7 +847,7 @@ public class InsnGen {
|
||||
regs[regNums[i]] = arg;
|
||||
}
|
||||
// replace args
|
||||
InsnNode inlCopy = inl.copy();
|
||||
InsnNode inlCopy = inl.copyWithoutResult();
|
||||
List<RegisterArg> inlArgs = new ArrayList<>();
|
||||
inlCopy.getRegisterArgs(inlArgs);
|
||||
for (RegisterArg r : inlArgs) {
|
||||
|
||||
@@ -60,7 +60,6 @@ public enum AFlag {
|
||||
|
||||
FALL_THROUGH,
|
||||
|
||||
EXPLICIT_GENERICS,
|
||||
VARARG_CALL,
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,6 +12,7 @@ import jadx.core.dex.attributes.nodes.EnumClassAttr;
|
||||
import jadx.core.dex.attributes.nodes.EnumMapAttr;
|
||||
import jadx.core.dex.attributes.nodes.FieldReplaceAttr;
|
||||
import jadx.core.dex.attributes.nodes.ForceReturnAttr;
|
||||
import jadx.core.dex.attributes.nodes.GenericInfoAttr;
|
||||
import jadx.core.dex.attributes.nodes.IgnoreEdgeAttr;
|
||||
import jadx.core.dex.attributes.nodes.JadxError;
|
||||
import jadx.core.dex.attributes.nodes.JumpInfo;
|
||||
@@ -36,6 +37,7 @@ import jadx.core.dex.trycatch.SplitterBlockAttr;
|
||||
*
|
||||
* @param <T> attribute class implementation
|
||||
*/
|
||||
@SuppressWarnings("InstantiationOfUtilityClass")
|
||||
public class AType<T extends IAttribute> {
|
||||
|
||||
// class, method, field
|
||||
@@ -81,6 +83,7 @@ public class AType<T extends IAttribute> {
|
||||
public static final AType<LoopLabelAttr> LOOP_LABEL = new AType<>();
|
||||
public static final AType<AttrList<JumpInfo>> JUMP = new AType<>();
|
||||
public static final AType<IMethodDetails> METHOD_DETAILS = new AType<>();
|
||||
public static final AType<GenericInfoAttr> GENERIC_INFO = new AType<>();
|
||||
|
||||
// register
|
||||
public static final AType<RegDebugInfoAttr> REG_DEBUG_INFO = new AType<>();
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package jadx.core.dex.attributes.nodes;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import jadx.core.dex.attributes.AType;
|
||||
@@ -53,8 +52,8 @@ public class EnumClassAttr implements IAttribute {
|
||||
private final List<EnumField> fields;
|
||||
private MethodNode staticMethod;
|
||||
|
||||
public EnumClassAttr(int fieldsCount) {
|
||||
this.fields = new ArrayList<>(fieldsCount);
|
||||
public EnumClassAttr(List<EnumField> fields) {
|
||||
this.fields = fields;
|
||||
}
|
||||
|
||||
public List<EnumField> getFields() {
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package jadx.core.dex.attributes.nodes;
|
||||
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.IAttribute;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
|
||||
public class GenericInfoAttr implements IAttribute {
|
||||
private final ArgType[] genericTypes;
|
||||
private boolean explicit;
|
||||
|
||||
public GenericInfoAttr(ArgType[] genericTypes) {
|
||||
this.genericTypes = genericTypes;
|
||||
}
|
||||
|
||||
public ArgType[] getGenericTypes() {
|
||||
return genericTypes;
|
||||
}
|
||||
|
||||
public boolean isExplicit() {
|
||||
return explicit;
|
||||
}
|
||||
|
||||
public void setExplicit(boolean explicit) {
|
||||
this.explicit = explicit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AType<GenericInfoAttr> getType() {
|
||||
return AType.GENERIC_INFO;
|
||||
}
|
||||
}
|
||||
@@ -37,4 +37,9 @@ public abstract class NotificationAttrNode extends LineAttrNode implements ICode
|
||||
addAttr(AType.COMMENTS, commentStr);
|
||||
LOG.info("{} in {}", commentStr, this);
|
||||
}
|
||||
|
||||
public void addDebugComment(String commentStr) {
|
||||
addAttr(AType.COMMENTS, "JADX DEBUG: " + commentStr);
|
||||
LOG.debug("{} in {}", commentStr, this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,10 @@ public class ArithNode extends InsnNode {
|
||||
addArg(b);
|
||||
}
|
||||
|
||||
public ArithNode(ArithOp op, InsnArg a, InsnArg b) {
|
||||
this(op, null, a, b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create one argument arithmetic instructions (a+=2).
|
||||
* Result is not set (null).
|
||||
@@ -58,7 +62,7 @@ public class ArithNode extends InsnNode {
|
||||
* @param res argument to change
|
||||
*/
|
||||
public static ArithNode oneArgOp(ArithOp op, InsnArg res, InsnArg a) {
|
||||
ArithNode insn = new ArithNode(op, null, res, a);
|
||||
ArithNode insn = new ArithNode(op, res, a);
|
||||
insn.add(AFlag.ARITH_ONEARG);
|
||||
return insn;
|
||||
}
|
||||
@@ -97,10 +101,7 @@ public class ArithNode extends InsnNode {
|
||||
|
||||
@Override
|
||||
public InsnNode copy() {
|
||||
ArithNode copy = new ArithNode(op,
|
||||
getResult().duplicate(),
|
||||
getArg(0).duplicate(),
|
||||
getArg(1).duplicate());
|
||||
ArithNode copy = new ArithNode(op, getArg(0).duplicate(), getArg(1).duplicate());
|
||||
return copyCommonParams(copy);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package jadx.core.dex.instructions.args;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -96,6 +97,11 @@ public abstract class InsnArg extends Typed {
|
||||
|
||||
@Nullable("if wrap failed")
|
||||
public InsnArg wrapInstruction(MethodNode mth, InsnNode insn) {
|
||||
return wrapInstruction(mth, insn, true);
|
||||
}
|
||||
|
||||
@Nullable("if wrap failed")
|
||||
public InsnArg wrapInstruction(MethodNode mth, InsnNode insn, boolean unbind) {
|
||||
InsnNode parent = parentInsn;
|
||||
if (parent == null) {
|
||||
return null;
|
||||
@@ -116,14 +122,24 @@ public abstract class InsnArg extends Typed {
|
||||
if (arg.isRegister()) {
|
||||
((RegisterArg) arg).setNameIfUnknown(name);
|
||||
} else if (arg.isInsnWrap()) {
|
||||
((InsnWrapArg) arg).getWrapInsn().getResult().setNameIfUnknown(name);
|
||||
InsnNode wrapInsn = ((InsnWrapArg) arg).getWrapInsn();
|
||||
RegisterArg registerArg = wrapInsn.getResult();
|
||||
if (registerArg != null) {
|
||||
registerArg.setNameIfUnknown(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
InsnArg arg = wrapInsnIntoArg(insn);
|
||||
InsnArg oldArg = parent.getArg(i);
|
||||
parent.setArg(i, arg);
|
||||
InsnRemover.unbindArgUsage(mth, this);
|
||||
InsnRemover.unbindResult(mth, insn);
|
||||
InsnRemover.unbindArgUsage(mth, oldArg);
|
||||
if (unbind) {
|
||||
InsnRemover.unbindArgUsage(mth, this);
|
||||
// result not needed in wrapped insn
|
||||
InsnRemover.unbindResult(mth, insn);
|
||||
insn.setResult(null);
|
||||
}
|
||||
return arg;
|
||||
}
|
||||
|
||||
@@ -137,29 +153,29 @@ public abstract class InsnArg extends Typed {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static InsnArg wrapInsnIntoArg(InsnNode insn) {
|
||||
InsnArg arg;
|
||||
InsnType type = insn.getType();
|
||||
if (type == InsnType.CONST || type == InsnType.MOVE) {
|
||||
if (insn.contains(AFlag.FORCE_ASSIGN_INLINE)) {
|
||||
RegisterArg resArg = insn.getResult();
|
||||
arg = wrap(insn);
|
||||
InsnArg arg = wrap(insn);
|
||||
if (resArg != null) {
|
||||
arg.setType(resArg.getType());
|
||||
}
|
||||
return arg;
|
||||
} else {
|
||||
arg = insn.getArg(0);
|
||||
insn.add(AFlag.REMOVE);
|
||||
InsnArg arg = insn.getArg(0);
|
||||
insn.add(AFlag.DONT_GENERATE);
|
||||
return arg;
|
||||
}
|
||||
} else {
|
||||
arg = wrapArg(insn);
|
||||
}
|
||||
return arg;
|
||||
return wrapArg(insn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer {@link InsnArg#wrapInsnIntoArg}.
|
||||
* Prefer {@link InsnArg#wrapInsnIntoArg(InsnNode)}.
|
||||
* <p>
|
||||
* This method don't support MOVE and CONST insns!
|
||||
*/
|
||||
public static InsnArg wrapArg(InsnNode insn) {
|
||||
|
||||
@@ -36,7 +36,7 @@ public final class InsnWrapArg extends InsnArg {
|
||||
|
||||
@Override
|
||||
public InsnArg duplicate() {
|
||||
InsnWrapArg copy = new InsnWrapArg(wrappedInsn.copy());
|
||||
InsnWrapArg copy = new InsnWrapArg(wrappedInsn.copyWithoutResult());
|
||||
copy.setType(type);
|
||||
return copyCommonParams(copy);
|
||||
}
|
||||
|
||||
@@ -130,6 +130,10 @@ public class RegisterArg extends InsnArg implements Named {
|
||||
return duplicate(getRegNum(), sVar);
|
||||
}
|
||||
|
||||
public RegisterArg duplicate(@Nullable SSAVar ssaVar) {
|
||||
return duplicate(getRegNum(), ssaVar);
|
||||
}
|
||||
|
||||
public RegisterArg duplicate(int regNum, @Nullable SSAVar sVar) {
|
||||
RegisterArg dup = new RegisterArg(regNum, getInitType());
|
||||
if (sVar != null) {
|
||||
|
||||
@@ -270,6 +270,7 @@ public class SSAVar {
|
||||
if (!types.isEmpty()) {
|
||||
sb.append(", types: ").append(types);
|
||||
}
|
||||
sb.append(", assign insn: ").append(getAssign().getParentInsn());
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,7 @@ public class InsnNode extends LineAttrNode {
|
||||
}
|
||||
|
||||
public void setResult(@Nullable RegisterArg res) {
|
||||
this.result = res;
|
||||
if (res != null) {
|
||||
res.setParentInsn(this);
|
||||
SSAVar ssaVar = res.getSVar();
|
||||
@@ -58,7 +59,6 @@ public class InsnNode extends LineAttrNode {
|
||||
ssaVar.setAssign(res);
|
||||
}
|
||||
}
|
||||
this.result = res;
|
||||
}
|
||||
|
||||
public void addArg(InsnArg arg) {
|
||||
@@ -326,17 +326,9 @@ public class InsnNode extends LineAttrNode {
|
||||
}
|
||||
|
||||
protected final <T extends InsnNode> T copyCommonParams(T copy) {
|
||||
if (copy.getResult() == null && result != null) {
|
||||
copy.setResult(result.duplicate());
|
||||
}
|
||||
if (copy.getArgsCount() == 0) {
|
||||
for (InsnArg arg : this.getArguments()) {
|
||||
if (arg.isInsnWrap()) {
|
||||
InsnNode wrapInsn = ((InsnWrapArg) arg).getWrapInsn();
|
||||
copy.addArg(InsnArg.wrapInsnIntoArg(wrapInsn.copy()));
|
||||
} else {
|
||||
copy.addArg(arg.duplicate());
|
||||
}
|
||||
copy.addArg(arg.duplicate());
|
||||
}
|
||||
}
|
||||
copy.copyAttributesFrom(this);
|
||||
@@ -347,6 +339,17 @@ public class InsnNode extends LineAttrNode {
|
||||
|
||||
/**
|
||||
* Make copy of InsnNode object.
|
||||
* <p>
|
||||
* NOTE: can't copy instruction with result argument
|
||||
* (SSA variable can't be used in two different assigns).
|
||||
* <p>
|
||||
* Prefer use next methods:
|
||||
* <ul>
|
||||
* <li>{@link #copyWithoutResult()} to explicitly state that result not needed
|
||||
* <li>{@link #copy(RegisterArg)} to provide new result arg
|
||||
* <li>{@link #copyWithNewSsaVar(MethodNode)} to make new SSA variable for result arg
|
||||
* </ul>
|
||||
* <p>
|
||||
*/
|
||||
public InsnNode copy() {
|
||||
if (this.getClass() != InsnNode.class) {
|
||||
@@ -355,6 +358,49 @@ public class InsnNode extends LineAttrNode {
|
||||
return copyCommonParams(new InsnNode(insnType, getArgsCount()));
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link #copy()}
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends InsnNode> T copyWithoutResult() {
|
||||
return (T) copy();
|
||||
}
|
||||
|
||||
public InsnNode copyWithoutSsa() {
|
||||
InsnNode copy = copyWithoutResult();
|
||||
if (result != null) {
|
||||
if (result.getSVar() == null) {
|
||||
copy.setResult(result.duplicate());
|
||||
} else {
|
||||
throw new JadxRuntimeException("Can't copy if SSA var is set");
|
||||
}
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link #copy()}
|
||||
*/
|
||||
public InsnNode copy(RegisterArg newReturnArg) {
|
||||
InsnNode copy = copy();
|
||||
copy.setResult(newReturnArg);
|
||||
return copy;
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link #copy()}
|
||||
*/
|
||||
public InsnNode copyWithNewSsaVar(MethodNode mth) {
|
||||
RegisterArg result = getResult();
|
||||
if (result == null) {
|
||||
throw new JadxRuntimeException("Result in null");
|
||||
}
|
||||
int regNum = result.getRegNum();
|
||||
RegisterArg resDupArg = result.duplicate(regNum, null);
|
||||
mth.makeNewSVar(resDupArg);
|
||||
return copy(resDupArg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix SSAVar info in register arguments.
|
||||
* Must be used after altering instructions.
|
||||
|
||||
@@ -647,7 +647,8 @@ public class MethodNode extends NotificationAttrNode implements IMethodDetails,
|
||||
return debugInfoOffset;
|
||||
}
|
||||
|
||||
public SSAVar makeNewSVar(int regNum, @NotNull RegisterArg assignArg) {
|
||||
public SSAVar makeNewSVar(@NotNull RegisterArg assignArg) {
|
||||
int regNum = assignArg.getRegNum();
|
||||
return makeNewSVar(regNum, getNextSVarVersion(regNum), assignArg);
|
||||
}
|
||||
|
||||
|
||||
@@ -93,8 +93,7 @@ public class ConstInlineVisitor extends AbstractVisitor {
|
||||
String s = ((ConstStringNode) insn).getString();
|
||||
FieldNode f = mth.getParentClass().getConstField(s);
|
||||
if (f == null) {
|
||||
InsnNode copy = insn.copy();
|
||||
copy.setResult(null);
|
||||
InsnNode copy = insn.copyWithoutResult();
|
||||
constArg = InsnArg.wrapArg(copy);
|
||||
} else {
|
||||
InsnNode constGet = new IndexInsnNode(InsnType.SGET, f.getFieldInfo(), 0);
|
||||
|
||||
@@ -44,6 +44,7 @@ import jadx.core.dex.visitors.shrink.CodeShrinkVisitor;
|
||||
import jadx.core.utils.BlockInsnPair;
|
||||
import jadx.core.utils.InsnRemover;
|
||||
import jadx.core.utils.InsnUtils;
|
||||
import jadx.core.utils.Utils;
|
||||
import jadx.core.utils.exceptions.JadxException;
|
||||
|
||||
import static jadx.core.utils.InsnUtils.checkInsnType;
|
||||
@@ -152,14 +153,13 @@ public class EnumVisitor extends AbstractVisitor {
|
||||
toRemove.add(valuesInitInsn);
|
||||
|
||||
// all checks complete, perform transform
|
||||
EnumClassAttr attr = new EnumClassAttr(enumFields.size());
|
||||
EnumClassAttr attr = new EnumClassAttr(enumFields);
|
||||
attr.setStaticMethod(classInitMth);
|
||||
attr.getFields().addAll(enumFields);
|
||||
cls.addAttr(attr);
|
||||
|
||||
for (EnumField field : attr.getFields()) {
|
||||
ConstructorInsn co = field.getConstrInsn();
|
||||
FieldNode fieldNode = field.getField();
|
||||
for (EnumField enumField : attr.getFields()) {
|
||||
ConstructorInsn co = enumField.getConstrInsn();
|
||||
FieldNode fieldNode = enumField.getField();
|
||||
|
||||
// use string arg from the constructor as enum field name
|
||||
String name = getConstString(cls.dex(), co.getArg(0));
|
||||
@@ -172,13 +172,16 @@ public class EnumVisitor extends AbstractVisitor {
|
||||
if (!co.getClassType().equals(cls.getClassInfo())) {
|
||||
// enum contains additional methods
|
||||
for (ClassNode innerCls : cls.getInnerClasses()) {
|
||||
processEnumInnerCls(co, field, innerCls);
|
||||
processEnumInnerCls(co, enumField, innerCls);
|
||||
}
|
||||
}
|
||||
fieldNode.add(AFlag.DONT_GENERATE);
|
||||
}
|
||||
|
||||
List<InsnNode> constrInsns = Utils.collectionMap(attr.getFields(), EnumField::getConstrInsn);
|
||||
InsnRemover.removeAllWithoutUnbind(staticBlock, constrInsns);
|
||||
|
||||
valuesField.add(AFlag.DONT_GENERATE);
|
||||
enumFields.forEach(f -> f.getField().add(AFlag.DONT_GENERATE));
|
||||
InsnRemover.removeAllAndUnbind(classInitMth, staticBlock, toRemove);
|
||||
if (classInitMth.countInsns() == 0) {
|
||||
classInitMth.add(AFlag.DONT_GENERATE);
|
||||
@@ -242,7 +245,6 @@ public class EnumVisitor extends AbstractVisitor {
|
||||
}
|
||||
toRemove.add(sgetInsn);
|
||||
toRemove.add(sputInsn);
|
||||
toRemove.add(co);
|
||||
return createEnumFieldByConstructor(cls, enumFieldNode, co);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
package jadx.core.dex.visitors;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.core.dex.attributes.nodes.GenericInfoAttr;
|
||||
import jadx.core.dex.instructions.InsnType;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.instructions.args.RegisterArg;
|
||||
import jadx.core.dex.instructions.mods.ConstructorInsn;
|
||||
import jadx.core.dex.nodes.BlockNode;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.dex.visitors.shrink.CodeShrinkVisitor;
|
||||
import jadx.core.dex.visitors.typeinference.TypeInferenceVisitor;
|
||||
import jadx.core.utils.exceptions.JadxException;
|
||||
|
||||
@JadxVisitor(
|
||||
name = "GenericTypesVisitor",
|
||||
desc = "Fix and apply generic type info",
|
||||
runAfter = TypeInferenceVisitor.class,
|
||||
runBefore = { CodeShrinkVisitor.class, MethodInvokeVisitor.class }
|
||||
)
|
||||
public class GenericTypesVisitor extends AbstractVisitor {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(GenericTypesVisitor.class);
|
||||
|
||||
@Override
|
||||
public void visit(MethodNode mth) throws JadxException {
|
||||
if (mth.isNoCode()) {
|
||||
return;
|
||||
}
|
||||
for (BlockNode block : mth.getBasicBlocks()) {
|
||||
for (InsnNode insn : block.getInstructions()) {
|
||||
if (insn.getType() == InsnType.CONSTRUCTOR) {
|
||||
attachGenericTypesInfo(mth, (ConstructorInsn) insn);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void attachGenericTypesInfo(MethodNode mth, ConstructorInsn insn) {
|
||||
try {
|
||||
RegisterArg resultArg = insn.getResult();
|
||||
if (resultArg == null) {
|
||||
return;
|
||||
}
|
||||
ArgType argType = resultArg.getSVar().getCodeVar().getType();
|
||||
if (argType == null || argType.getGenericTypes() == null) {
|
||||
return;
|
||||
}
|
||||
ClassNode cls = mth.dex().resolveClass(insn.getClassType());
|
||||
if (cls != null && cls.getGenericTypeParameters().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
insn.addAttr(new GenericInfoAttr(argType.getGenericTypes()));
|
||||
} catch (Exception e) {
|
||||
LOG.error("Failed to attach constructor generic info", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -73,7 +73,7 @@ public class MethodInlineVisitor extends AbstractVisitor {
|
||||
if (Consts.DEBUG) {
|
||||
mth.addAttr(AType.COMMENTS, "Removed for inline");
|
||||
} else {
|
||||
InsnNode copy = insn.copy();
|
||||
InsnNode copy = insn.copyWithoutResult();
|
||||
// unbind SSA variables from copy instruction
|
||||
List<RegisterArg> regArgs = new ArrayList<>();
|
||||
copy.getRegisterArgs(regArgs);
|
||||
|
||||
@@ -39,6 +39,7 @@ 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.Utils;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
public class SimplifyVisitor extends AbstractVisitor {
|
||||
@@ -393,7 +394,8 @@ public class SimplifyVisitor extends AbstractVisitor {
|
||||
// all check passed
|
||||
removeStringBuilderInsns(mth, toStrInsn, chain);
|
||||
|
||||
InsnNode concatInsn = new InsnNode(InsnType.STR_CONCAT, args);
|
||||
List<InsnArg> dupArgs = Utils.collectionMap(args, InsnArg::duplicate);
|
||||
InsnNode concatInsn = new InsnNode(InsnType.STR_CONCAT, dupArgs);
|
||||
concatInsn.setResult(toStrInsn.getResult());
|
||||
concatInsn.add(AFlag.SYNTHETIC);
|
||||
concatInsn.copyAttributesFrom(toStrInsn);
|
||||
@@ -499,7 +501,8 @@ public class SimplifyVisitor extends AbstractVisitor {
|
||||
|| !wrap.getArg(0).isInsnWrap()) {
|
||||
return null;
|
||||
}
|
||||
InsnNode get = ((InsnWrapArg) wrap.getArg(0)).getWrapInsn();
|
||||
InsnArg getWrap = wrap.getArg(0);
|
||||
InsnNode get = ((InsnWrapArg) getWrap).getWrapInsn();
|
||||
InsnType getType = get.getType();
|
||||
if (getType != InsnType.IGET && getType != InsnType.SGET) {
|
||||
return null;
|
||||
@@ -517,7 +520,8 @@ public class SimplifyVisitor extends AbstractVisitor {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
InsnArg fArg = InsnArg.wrapArg(get);
|
||||
InsnArg fArg = getWrap.duplicate();
|
||||
InsnRemover.unbindInsn(mth, get);
|
||||
if (insn.getType() == InsnType.IPUT) {
|
||||
InsnRemover.unbindArgUsage(mth, insn.getArg(1));
|
||||
}
|
||||
|
||||
@@ -750,7 +750,7 @@ public class BlockProcessor extends AbstractVisitor {
|
||||
first = false;
|
||||
} else {
|
||||
for (InsnNode oldInsn : exitBlock.getInstructions()) {
|
||||
newRetBlock.getInstructions().add(oldInsn.copy());
|
||||
newRetBlock.getInstructions().add(oldInsn.copyWithoutSsa());
|
||||
}
|
||||
}
|
||||
BlockSplitter.replaceConnection(pred, exitBlock, newRetBlock);
|
||||
|
||||
@@ -255,27 +255,19 @@ public class LoopRegionVisitor extends AbstractVisitor implements IRegionVisitor
|
||||
return false;
|
||||
}
|
||||
List<InsnNode> toSkip = new LinkedList<>();
|
||||
RegisterArg iterVar = nextCall.getResult();
|
||||
if (iterVar == null) {
|
||||
return false;
|
||||
}
|
||||
if (!usedOnlyInLoop(mth, loopRegion, iterVar)) {
|
||||
return false;
|
||||
}
|
||||
if (!assignOnlyInLoop(mth, loopRegion, iterVar)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RegisterArg iterVar;
|
||||
if (nextCall.contains(AFlag.WRAPPED)) {
|
||||
InsnArg wrapArg = BlockUtils.searchWrappedInsnParent(mth, nextCall);
|
||||
if (wrapArg != null && wrapArg.getParentInsn() != null) {
|
||||
InsnNode parentInsn = wrapArg.getParentInsn();
|
||||
if (parentInsn.getType() != InsnType.CHECK_CAST) {
|
||||
if (!fixIterableType(mth, iterableArg, iterVar)) {
|
||||
return false;
|
||||
}
|
||||
parentInsn.replaceArg(wrapArg, iterVar);
|
||||
} else {
|
||||
BlockNode block = BlockUtils.getBlockByInsn(mth, parentInsn);
|
||||
if (block == null) {
|
||||
return false;
|
||||
}
|
||||
if (!RegionUtils.isRegionContainsBlock(loopRegion, block)) {
|
||||
return false;
|
||||
}
|
||||
if (parentInsn.getType() == InsnType.CHECK_CAST) {
|
||||
iterVar = parentInsn.getResult();
|
||||
if (iterVar == null || !fixIterableType(mth, iterableArg, iterVar)) {
|
||||
return false;
|
||||
@@ -287,12 +279,32 @@ public class LoopRegionVisitor extends AbstractVisitor implements IRegionVisitor
|
||||
// cast not inlined
|
||||
toSkip.add(parentInsn);
|
||||
}
|
||||
} else {
|
||||
iterVar = nextCall.getResult();
|
||||
if (iterVar == null) {
|
||||
return false;
|
||||
}
|
||||
nextCall.add(AFlag.DONT_GENERATE);
|
||||
if (!fixIterableType(mth, iterableArg, iterVar)) {
|
||||
return false;
|
||||
}
|
||||
parentInsn.replaceArg(wrapArg, iterVar);
|
||||
}
|
||||
} else {
|
||||
LOG.warn(" checkIterableForEach: Wrapped insn not found: {}, mth: {}", nextCall, mth);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
iterVar = nextCall.getResult();
|
||||
if (iterVar == null) {
|
||||
return false;
|
||||
}
|
||||
if (!usedOnlyInLoop(mth, loopRegion, iterVar)) {
|
||||
return false;
|
||||
}
|
||||
if (!assignOnlyInLoop(mth, loopRegion, iterVar)) {
|
||||
return false;
|
||||
}
|
||||
toSkip.add(nextCall);
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
@JadxVisitor(
|
||||
name = "CodeShrinkVisitor",
|
||||
desc = "Inline variables for make code smaller",
|
||||
desc = "Inline variables to make code smaller",
|
||||
runAfter = { ModVisitor.class }
|
||||
)
|
||||
public class CodeShrinkVisitor extends AbstractVisitor {
|
||||
@@ -125,12 +125,11 @@ public class CodeShrinkVisitor extends AbstractVisitor {
|
||||
if (useInsn == null || useInsn.contains(AFlag.DONT_GENERATE)) {
|
||||
return false;
|
||||
}
|
||||
InsnArg replaceArg = InsnArg.wrapInsnIntoArg(assignInsn.copy());
|
||||
if (!InsnRemover.removeWithoutUnbind(mth, assignBlock, assignInsn)) {
|
||||
return false;
|
||||
}
|
||||
InsnArg replaceArg = InsnArg.wrapInsnIntoArg(assignInsn);
|
||||
useInsn.replaceArg(useArg, replaceArg);
|
||||
|
||||
assignInsn.add(AFlag.REMOVE);
|
||||
assignInsn.add(AFlag.DONT_GENERATE);
|
||||
InsnRemover.remove(mth, assignBlock, assignInsn);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -142,9 +141,11 @@ public class CodeShrinkVisitor extends AbstractVisitor {
|
||||
if (insn.contains(AFlag.FORCE_ASSIGN_INLINE)) {
|
||||
return assignInline(mth, arg, insn, block);
|
||||
}
|
||||
boolean replaced = arg.wrapInstruction(mth, insn) != null;
|
||||
// just move instruction into arg, don't unbind/copy/duplicate
|
||||
InsnArg wrappedArg = arg.wrapInstruction(mth, insn, false);
|
||||
boolean replaced = wrappedArg != null;
|
||||
if (replaced) {
|
||||
InsnList.remove(block, insn);
|
||||
InsnRemover.removeWithoutUnbind(mth, block, insn);
|
||||
}
|
||||
return replaced;
|
||||
}
|
||||
|
||||
@@ -422,7 +422,6 @@ public class SSATransform extends AbstractVisitor {
|
||||
if (resArg.getRegNum() != arg.getRegNum()
|
||||
&& !resArg.getSVar().isUsedInPhi()) {
|
||||
markThisArgs(resArg);
|
||||
parentInsn.add(AFlag.REMOVE);
|
||||
parentInsn.add(AFlag.DONT_GENERATE);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -404,7 +404,7 @@ public final class TypeInferenceVisitor extends AbstractVisitor {
|
||||
if (apply) {
|
||||
int regNum = reg.getRegNum();
|
||||
RegisterArg resultArg = reg.duplicate(regNum, null);
|
||||
SSAVar newSsaVar = mth.makeNewSVar(regNum, resultArg);
|
||||
SSAVar newSsaVar = mth.makeNewSVar(resultArg);
|
||||
RegisterArg arg = reg.duplicate(regNum, var);
|
||||
|
||||
InsnNode moveInsn = new InsnNode(InsnType.MOVE, 1);
|
||||
@@ -476,7 +476,7 @@ public final class TypeInferenceVisitor extends AbstractVisitor {
|
||||
castNode.addArg(bound.getArg());
|
||||
castNode.setResult(InsnArg.reg(bound.getArg().getRegNum(), bound.getType()));
|
||||
|
||||
SSAVar newVar = mth.makeNewSVar(castNode.getResult().getRegNum(), castNode.getResult());
|
||||
SSAVar newVar = mth.makeNewSVar(castNode.getResult());
|
||||
CodeVar codeVar = new CodeVar();
|
||||
codeVar.setType(bound.getType());
|
||||
newVar.setCodeVar(codeVar);
|
||||
|
||||
@@ -56,8 +56,6 @@ public class InsnRemover {
|
||||
|
||||
public void addWithoutUnbind(InsnNode insn) {
|
||||
toRemove.add(insn);
|
||||
insn.add(AFlag.REMOVE);
|
||||
insn.add(AFlag.DONT_GENERATE);
|
||||
}
|
||||
|
||||
public void perform() {
|
||||
@@ -69,7 +67,8 @@ public class InsnRemover {
|
||||
remove(mth, remInsn);
|
||||
}
|
||||
} else {
|
||||
removeAll(mth, instrList, toRemove);
|
||||
unbindInsns(mth, toRemove);
|
||||
removeAll(instrList, toRemove);
|
||||
}
|
||||
toRemove.clear();
|
||||
}
|
||||
@@ -77,10 +76,15 @@ public class InsnRemover {
|
||||
public static void unbindInsn(@Nullable MethodNode mth, InsnNode insn) {
|
||||
unbindAllArgs(mth, insn);
|
||||
unbindResult(mth, insn);
|
||||
insn.add(AFlag.REMOVE);
|
||||
insn.add(AFlag.DONT_GENERATE);
|
||||
}
|
||||
|
||||
public static void unbindInsns(@Nullable MethodNode mth, List<InsnNode> insns) {
|
||||
for (InsnNode insn : insns) {
|
||||
unbindInsn(mth, insn);
|
||||
}
|
||||
}
|
||||
|
||||
public static void unbindAllArgs(@Nullable MethodNode mth, InsnNode insn) {
|
||||
for (InsnArg arg : insn.getArguments()) {
|
||||
unbindArgUsage(mth, arg);
|
||||
@@ -154,7 +158,7 @@ public class InsnRemover {
|
||||
|
||||
// Don't use 'instrList.removeAll(toRemove)' because it will remove instructions by content
|
||||
// and here can be several instructions with same content
|
||||
private static void removeAll(MethodNode mth, List<InsnNode> insns, List<InsnNode> toRemove) {
|
||||
private static void removeAll(List<InsnNode> insns, List<InsnNode> toRemove) {
|
||||
if (toRemove == null || toRemove.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
@@ -164,7 +168,6 @@ public class InsnRemover {
|
||||
for (int i = 0; i < insnsCount; i++) {
|
||||
if (insns.get(i) == rem) {
|
||||
insns.remove(i);
|
||||
unbindInsn(mth, rem);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
@@ -179,32 +182,58 @@ public class InsnRemover {
|
||||
}
|
||||
|
||||
public static void remove(MethodNode mth, InsnNode insn) {
|
||||
if (insn.contains(AFlag.WRAPPED)) {
|
||||
unbindInsn(mth, insn);
|
||||
return;
|
||||
}
|
||||
BlockNode block = BlockUtils.getBlockByInsn(mth, insn);
|
||||
if (block != null) {
|
||||
remove(mth, block, insn);
|
||||
} else {
|
||||
insn.add(AFlag.DONT_GENERATE);
|
||||
mth.addWarnComment("Not found block with instruction: " + insn);
|
||||
}
|
||||
}
|
||||
|
||||
public static void remove(MethodNode mth, BlockNode block, InsnNode insn) {
|
||||
unbindInsn(mth, insn);
|
||||
removeWithoutUnbind(mth, block, insn);
|
||||
}
|
||||
|
||||
public static boolean removeWithoutUnbind(MethodNode mth, BlockNode block, InsnNode insn) {
|
||||
// remove by pointer (don't use equals)
|
||||
Iterator<InsnNode> it = block.getInstructions().iterator();
|
||||
while (it.hasNext()) {
|
||||
InsnNode ir = it.next();
|
||||
if (ir == insn) {
|
||||
it.remove();
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
mth.addWarnComment("Failed to remove instruction: " + insn + " from block: " + block);
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void removeAllAndUnbind(MethodNode mth, BlockNode block, List<InsnNode> insns) {
|
||||
for (InsnNode insn : insns) {
|
||||
unbindInsn(mth, insn);
|
||||
unbindInsns(mth, insns);
|
||||
removeAll(block.getInstructions(), insns);
|
||||
}
|
||||
|
||||
public static void removeAllWithoutUnbind(BlockNode block, List<InsnNode> insns) {
|
||||
removeAll(block.getInstructions(), insns);
|
||||
}
|
||||
|
||||
public static void removeAllMarked(MethodNode mth) {
|
||||
InsnRemover insnRemover = new InsnRemover(mth);
|
||||
for (BlockNode blockNode : mth.getBasicBlocks()) {
|
||||
for (InsnNode insn : blockNode.getInstructions()) {
|
||||
if (insn.contains(AFlag.REMOVE)) {
|
||||
insnRemover.addWithoutUnbind(insn);
|
||||
}
|
||||
}
|
||||
insnRemover.setBlock(blockNode);
|
||||
insnRemover.perform();
|
||||
}
|
||||
removeAll(mth, block.getInstructions(), insns);
|
||||
}
|
||||
|
||||
public static void remove(MethodNode mth, BlockNode block, int index) {
|
||||
|
||||
@@ -181,7 +181,7 @@ public class InsnUtils {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static InsnNode checkInsnType(InsnNode insn, InsnType insnType) {
|
||||
public static InsnNode checkInsnType(@Nullable InsnNode insn, InsnType insnType) {
|
||||
if (insn != null && insn.getType() == insnType) {
|
||||
return insn;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
package jadx.core.utils.exceptions;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import jadx.core.utils.Utils;
|
||||
|
||||
import static jadx.core.codegen.CodeWriter.NL;
|
||||
|
||||
public class JadxRuntimeException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = -7410848445429898248L;
|
||||
@@ -8,6 +14,10 @@ public class JadxRuntimeException extends RuntimeException {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public JadxRuntimeException(String... lines) {
|
||||
super(Utils.listToString(Arrays.asList(lines), NL + " "));
|
||||
}
|
||||
|
||||
public JadxRuntimeException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user