package org.objectweb.asm.commons.util;
import org.objectweb.asm.Label;
import org.objectweb.asm.tree.*;
import java.util.Arrays;
import java.util.Collection;
import static org.objectweb.asm.tree.AbstractInsnNode.*;
/**
* @author Tyler Sedlar
* @author Bibl
*/
public class Assembly {
public static final String[] OPCODES = {"NOP", "ACONST_NULL", "ICONST_M1", "ICONST_0", "ICONST_1", "ICONST_2", "ICONST_3", "ICONST_4", "ICONST_5", "LCONST_0", "LCONST_1", "FCONST_0", "FCONST_1", "FCONST_2", "DCONST_0", "DCONST_1", "BIPUSH", "SIPUSH", "LDC", "", "", "ILOAD", "LLOAD", "FLOAD", "DLOAD", "ALOAD", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "IALOAD", "LALOAD", "FALOAD", "DALOAD", "AALOAD", "BALOAD", "CALOAD", "SALOAD", "ISTORE", "LSTORE", "FSTORE", "DSTORE", "ASTORE", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "IASTORE", "LASTORE", "FASTORE", "DASTORE", "AASTORE", "BASTORE", "CASTORE", "SASTORE", "POP", "POP2", "DUP", "DUP_X1", "DUP_X2", "DUP2", "DUP2_X1", "DUP2_X2", "SWAP", "IADD", "LADD", "FADD", "DADD", "ISUB", "LSUB", "FSUB", "DSUB", "IMUL", "LMUL", "FMUL", "DMUL", "IDIV", "LDIV", "FDIV", "DDIV", "IREM", "LREM", "FREM", "DREM", "INEG", "LNEG", "FNEG", "DNEG", "ISHL", "LSHL", "ISHR", "LSHR", "IUSHR", "LUSHR", "IAND", "LAND", "IOR", "LOR", "IXOR", "LXOR", "IINC", "I2L", "I2F", "I2D", "L2I", "L2F", "L2D", "F2I", "F2L", "F2D", "D2I", "D2L", "D2F", "I2B", "I2C", "I2S", "LCMP", "FCMPL", "FCMPG", "DCMPL", "DCMPG", "IFEQ", "IFNE", "IFLT", "IFGE", "IFGT", "IFLE", "IF_ICMPEQ", "IF_ICMPNE", "IF_ICMPLT", "IF_ICMPGE", "IF_ICMPGT", "IF_ICMPLE", "IF_ACMPEQ", "IF_ACMPNE", "GOTO", "JSR", "RET", "TABLESWITCH", "LOOKUPSWITCH", "IRETURN", "LRETURN", "FRETURN", "DRETURN", "ARETURN", "RETURN", "GETSTATIC", "PUTSTATIC", "GETFIELD", "PUTFIELD", "INVOKEVIRTUAL", "INVOKESPECIAL", "INVOKESTATIC", "INVOKEINTERFACE", "INVOKEDYNAMIC", "NEW", "NEWARRAY", "ANEWARRAY", "ARRAYLENGTH", "ATHROW", "CHECKCAST", "INSTANCEOF", "MONITORENTER", "MONITOREXIT", "", "MULTIANEWARRAY", "IFNULL", "IFNONNULL"};
public static final int LONGEST_OPCODE_NAME = getLongest(OPCODES);
public static int getLongest(String[] strings) {
String longest = "";
for(String s : strings) {
if(s.length() > longest.length())
longest = s;
}
return longest.length();
}
public static String pad(String s, int size) {
if(s.length() >= size)
return s;
StringBuilder sb = new StringBuilder(s);
int diff = size - s.length();
for(int i=0; i < diff; i++) {
sb.append(" ");
}
return sb.toString();
}
public static boolean instructionsEqual(AbstractInsnNode insn1, AbstractInsnNode insn2) {
if (insn1 == insn2) {
return true;
}
if (insn1 == null || insn2 == null || insn1.type() != insn2.type() ||
insn1.opcode() != insn2.opcode()) {
return false;
}
int size;
switch (insn1.type()) {
case INSN:
return true;
case INT_INSN:
IntInsnNode iin1 = (IntInsnNode) insn1, iin2 = (IntInsnNode) insn2;
return iin1.operand == iin2.operand;
case VAR_INSN:
VarInsnNode vin1 = (VarInsnNode) insn1, vin2 = (VarInsnNode) insn2;
return vin1.var == vin2.var;
case TYPE_INSN:
TypeInsnNode tin1 = (TypeInsnNode) insn1, tin2 = (TypeInsnNode) insn2;
return tin1.desc.equals(tin2.desc);
case FIELD_INSN:
FieldInsnNode fin1 = (FieldInsnNode) insn1, fin2 = (FieldInsnNode) insn2;
return fin1.desc.equals(fin2.desc) && fin1.name.equals(fin2.name) && fin1.owner.equals(fin2.owner);
case METHOD_INSN:
MethodInsnNode min1 = (MethodInsnNode) insn1, min2 = (MethodInsnNode) insn2;
return min1.desc.equals(min2.desc) && min1.name.equals(min2.name) && min1.owner.equals(min2.owner);
case INVOKE_DYNAMIC_INSN:
InvokeDynamicInsnNode idin1 = (InvokeDynamicInsnNode) insn1, idin2 = (InvokeDynamicInsnNode) insn2;
return idin1.bsm.equals(idin2.bsm) && Arrays.equals(idin1.bsmArgs, idin2.bsmArgs) &&
idin1.desc.equals(idin2.desc) && idin1.name.equals(idin2.name);
case JUMP_INSN:
JumpInsnNode jin1 = (JumpInsnNode) insn1, jin2 = (JumpInsnNode) insn2;
return instructionsEqual(jin1.label, jin2.label);
case LABEL:
Label label1 = ((LabelNode) insn1).getLabel(), label2 = ((LabelNode) insn2).getLabel();
return label1 == null ? label2 == null : label1.info == null ? label2.info == null :
label1.info.equals(label2.info);
case LDC_INSN:
LdcInsnNode lin1 = (LdcInsnNode) insn1, lin2 = (LdcInsnNode) insn2;
return lin1.cst.equals(lin2.cst);
case IINC_INSN:
IincInsnNode iiin1 = (IincInsnNode) insn1, iiin2 = (IincInsnNode) insn2;
return iiin1.incr == iiin2.incr && iiin1.var == iiin2.var;
case TABLESWITCH_INSN:
TableSwitchInsnNode tsin1 = (TableSwitchInsnNode) insn1, tsin2 = (TableSwitchInsnNode) insn2;
size = tsin1.labels.size();
if (size != tsin2.labels.size()) {
return false;
}
for (int i = 0; i < size; i++) {
if (!instructionsEqual(tsin1.labels.get(i), tsin2.labels.get(i))) {
return false;
}
}
return instructionsEqual(tsin1.dflt, tsin2.dflt) && tsin1.max == tsin2.max && tsin1.min == tsin2.min;
case LOOKUPSWITCH_INSN:
LookupSwitchInsnNode lsin1 = (LookupSwitchInsnNode) insn1, lsin2 = (LookupSwitchInsnNode) insn2;
size = lsin1.labels.size();
if (size != lsin2.labels.size()) {
return false;
}
for (int i = 0; i < size; i++) {
if (!instructionsEqual(lsin1.labels.get(i), lsin2.labels.get(i))) {
return false;
}
}
return instructionsEqual(lsin1.dflt, lsin2.dflt) && lsin1.keys.equals(lsin2.keys);
case MULTIANEWARRAY_INSN:
MultiANewArrayInsnNode manain1 = (MultiANewArrayInsnNode) insn1, manain2 = (MultiANewArrayInsnNode) insn2;
return manain1.desc.equals(manain2.desc) && manain1.dims == manain2.dims;
case FRAME:
FrameNode fn1 = (FrameNode) insn1, fn2 = (FrameNode) insn2;
return fn1.local.equals(fn2.local) && fn1.stack.equals(fn2.stack);
case LINE:
LineNumberNode lnn1 = (LineNumberNode) insn1, lnn2 = (LineNumberNode) insn2;
return lnn1.line == lnn2.line && instructionsEqual(lnn1.start, lnn2.start);
}
return false;
}
public static boolean instructionsEqual(AbstractInsnNode[] insns, AbstractInsnNode[] insns2) {
if (insns == insns2) {
return true;
}
if (insns == null || insns2 == null) {
return false;
}
int length = insns.length;
if (insns2.length != length) {
return false;
}
for (int i = 0; i < length; i++) {
AbstractInsnNode insn1 = insns[i], insn2 = insns2[i];
if (!(insn1 == null ? insn2 == null : instructionsEqual(insn1, insn2))) {
return false;
}
}
return true;
}
public static String toString(AbstractInsnNode insn) {
if (insn == null) {
return "null";
}
int op = insn.opcode();
if (op == -1) {
return insn.toString();
}
StringBuilder sb = new StringBuilder();
/* pad the opcode name so that all the extra information for the instructions is aligned on the column.
* TODO: maybe change the column length to the longest opcode name in the instruction set rather than
* out of all the possible ones(statically, the longest opcode name is invokedynamic).*/
sb.append(pad(OPCODES[op].toLowerCase(), LONGEST_OPCODE_NAME));
switch (insn.type()) {
case INT_INSN:
sb.append(((IntInsnNode) insn).operand);
break;
case VAR_INSN:
sb.append('#').append(((VarInsnNode) insn).var);
break;
case TYPE_INSN:
sb.append(((TypeInsnNode) insn).desc);
break;
case FIELD_INSN:
FieldInsnNode fin = (FieldInsnNode) insn;
sb.append(fin.owner).append('.').append(fin.name).append(' ').append(fin.desc);
break;
case METHOD_INSN:
MethodInsnNode min = (MethodInsnNode) insn;
sb.append(min.owner).append('.').append(min.name).append(' ').append(min.desc);
break;
case JUMP_INSN:
break;
case LDC_INSN:
Object cst = ((LdcInsnNode) insn).cst;
sb.append(cst).append("(").append(cst.getClass().getName()).append(")");
break;
case IINC_INSN:
IincInsnNode iin = (IincInsnNode) insn;
sb.append('#').append(iin.var).append(' ').append(iin.incr);
break;
case TABLESWITCH_INSN:
break;
case LOOKUPSWITCH_INSN:
break;
case MULTIANEWARRAY_INSN:
MultiANewArrayInsnNode m = (MultiANewArrayInsnNode) insn;
sb.append(m.desc).append(' ').append(m.dims);
break;
}
return sb.toString();
}
public static void rename(Collection<ClassNode> classes, FieldNode fn, String newName) {
for (ClassNode node : classes) {
for (MethodNode mn : node.methods) {
for (AbstractInsnNode ain : mn.instructions.toArray()) {
if (ain instanceof FieldInsnNode) {
FieldInsnNode fin = (FieldInsnNode) ain;
if (fin.owner.equals(fn.owner.name) && fin.name.equals(fn.name))
fin.name = newName;
}
}
}
}
fn.name = newName;
}
public static void rename(Collection<ClassNode> classes, ClassNode cn, String newName) {
for (ClassNode node : classes) {
if (node.superName.equals(cn.name))
node.superName = newName;
if (node.interfaces.contains(cn.name)) {
node.interfaces.remove(cn.name);
node.interfaces.add(newName);
}
for (FieldNode fn : node.fields) {
if (fn.desc.endsWith("L" + cn.name + ";"))
fn.desc = fn.desc.replace("L" + cn.name + ";", "L" + newName + ";");
}
for (MethodNode mn : node.methods) {
if (mn.desc.contains("L" + cn.name + ";"))
mn.desc = mn.desc.replaceAll("L" + cn.name + ";", "L" + newName + ";");
for (AbstractInsnNode ain : mn.instructions.toArray()) {
if (ain instanceof FieldInsnNode) {
FieldInsnNode fin = (FieldInsnNode) ain;
if (fin.owner.equals(cn.name))
fin.owner = newName;
} else if (ain instanceof MethodInsnNode) {
MethodInsnNode min = (MethodInsnNode) ain;
if (min.owner.equals(cn.name))
min.owner = newName;
}
}
}
}
cn.name = newName;
}
}