package jadx.core.dex.nodes; import jadx.core.dex.attributes.nodes.LineAttrNode; import jadx.core.dex.instructions.InsnType; import jadx.core.dex.instructions.args.ArgType; import jadx.core.dex.instructions.args.InsnArg; import jadx.core.dex.instructions.args.InsnWrapArg; import jadx.core.dex.instructions.args.LiteralArg; import jadx.core.dex.instructions.args.NamedArg; import jadx.core.dex.instructions.args.RegisterArg; import jadx.core.dex.instructions.args.SSAVar; import jadx.core.utils.InsnUtils; import jadx.core.utils.Utils; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import com.android.dx.io.instructions.DecodedInstruction; import com.rits.cloning.Cloner; public class InsnNode extends LineAttrNode { private static final Cloner INSN_CLONER = new Cloner(); static { INSN_CLONER.dontClone(ArgType.class, SSAVar.class, LiteralArg.class, NamedArg.class); INSN_CLONER.dontCloneInstanceOf(RegisterArg.class); } protected final InsnType insnType; private RegisterArg result; private final List<InsnArg> arguments; protected int offset; public InsnNode(InsnType type, int argsCount) { this.insnType = type; this.offset = -1; if (argsCount == 0) { this.arguments = Collections.emptyList(); } else { this.arguments = new ArrayList<InsnArg>(argsCount); } } public static InsnNode wrapArg(InsnArg arg) { InsnNode insn = new InsnNode(InsnType.ONE_ARG, 1); insn.addArg(arg); return insn; } public void setResult(RegisterArg res) { if (res != null) { res.setParentInsn(this); } this.result = res; } public void addArg(InsnArg arg) { arg.setParentInsn(this); arguments.add(arg); } public InsnType getType() { return insnType; } public RegisterArg getResult() { return result; } public Iterable<InsnArg> getArguments() { return arguments; } public int getArgsCount() { return arguments.size(); } public InsnArg getArg(int n) { return arguments.get(n); } public boolean containsArg(RegisterArg arg) { for (InsnArg a : arguments) { if (a == arg || a.isRegister() && ((RegisterArg) a).getRegNum() == arg.getRegNum()) { return true; } } return false; } public void setArg(int n, InsnArg arg) { arg.setParentInsn(this); arguments.set(n, arg); } /** * Replace instruction arg with another using recursive search. * <br> * <b>Caution:</b> this method don't change usage information for replaced argument. */ public boolean replaceArg(InsnArg from, InsnArg to) { int count = getArgsCount(); for (int i = 0; i < count; i++) { InsnArg arg = arguments.get(i); if (arg == from) { setArg(i, to); return true; } if (arg.isInsnWrap() && ((InsnWrapArg) arg).getWrapInsn().replaceArg(from, to)) { return true; } } return false; } protected boolean removeArg(InsnArg arg) { int count = getArgsCount(); for (int i = 0; i < count; i++) { if (arg == arguments.get(i)) { arguments.remove(i); if (arg instanceof RegisterArg) { RegisterArg reg = (RegisterArg) arg; reg.getSVar().removeUse(reg); } return true; } } return false; } protected void addReg(DecodedInstruction insn, int i, ArgType type) { addArg(InsnArg.reg(insn, i, type)); } protected void addReg(int regNum, ArgType type) { addArg(InsnArg.reg(regNum, type)); } protected void addLit(long literal, ArgType type) { addArg(InsnArg.lit(literal, type)); } protected void addLit(DecodedInstruction insn, ArgType type) { addArg(InsnArg.lit(insn, type)); } public int getOffset() { return offset; } public void setOffset(int offset) { this.offset = offset; } public void getRegisterArgs(Collection<RegisterArg> collection) { for (InsnArg arg : this.getArguments()) { if (arg.isRegister()) { collection.add((RegisterArg) arg); } else if (arg.isInsnWrap()) { ((InsnWrapArg) arg).getWrapInsn().getRegisterArgs(collection); } } } public boolean isConstInsn() { switch (getType()) { case CONST: case CONST_STR: case CONST_CLASS: return true; default: return false; } } public boolean canReorder() { switch (getType()) { case CONST: case CONST_STR: case CONST_CLASS: case CAST: case MOVE: case ARITH: case NEG: case CMP_L: case CMP_G: case CHECK_CAST: case INSTANCE_OF: case FILL_ARRAY: case FILLED_NEW_ARRAY: case NEW_ARRAY: case NEW_MULTIDIM_ARRAY: case STR_CONCAT: return true; default: return false; } } public boolean canReorderRecursive() { if (!canReorder()) { return false; } for (InsnArg arg : this.getArguments()) { if (arg.isInsnWrap()) { InsnNode wrapInsn = ((InsnWrapArg) arg).getWrapInsn(); if (!wrapInsn.canReorderRecursive()) { return false; } } } return true; } @Override public String toString() { return InsnUtils.formatOffset(offset) + ": " + InsnUtils.insnTypeToString(insnType) + (result == null ? "" : result + " = ") + Utils.listToString(arguments); } /** * Compare instruction only by identity. */ @Override public final int hashCode() { return super.hashCode(); } /** * Compare instruction only by identity. */ @Override public final boolean equals(Object obj) { return super.equals(obj); } /** * 'Soft' equals, don't compare arguments, only instruction specific parameters. */ public boolean isSame(InsnNode other) { if (this == other) { return true; } if (insnType != other.insnType || arguments.size() != other.arguments.size()) { return false; } // check wrapped instructions int size = arguments.size(); for (int i = 0; i < size; i++) { InsnArg arg = arguments.get(i); InsnArg otherArg = other.arguments.get(i); if (arg.isInsnWrap()) { if (!otherArg.isInsnWrap()) { return false; } InsnNode wrapInsn = ((InsnWrapArg) arg).getWrapInsn(); InsnNode otherWrapInsn = ((InsnWrapArg) otherArg).getWrapInsn(); if (!wrapInsn.isSame(otherWrapInsn)) { return false; } } } return true; } protected <T extends InsnNode> T copyCommonParams(T copy) { copy.setResult(result); if (copy.getArgsCount() == 0) { for (InsnArg arg : this.getArguments()) { if (arg.isInsnWrap()) { InsnNode wrapInsn = ((InsnWrapArg) arg).getWrapInsn(); copy.addArg(InsnArg.wrapArg(wrapInsn.copy())); } else { copy.addArg(arg); } } } copy.copyAttributesFrom(this); copy.copyLines(this); copy.setOffset(this.getOffset()); return copy; } /** * Make copy of InsnNode object. */ public InsnNode copy() { if (this.getClass() == InsnNode.class) { return copyCommonParams(new InsnNode(insnType, getArgsCount())); } return INSN_CLONER.deepClone(this); } }