package nf.co.haxter.util;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Map.Entry;
import nf.co.haxter.customized.CustomLabel;
import nf.co.haxter.exception.InvalidBytecodeException;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Label;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.FrameNode;
import org.objectweb.asm.tree.IincInsnNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.IntInsnNode;
import org.objectweb.asm.tree.InvokeDynamicInsnNode;
import org.objectweb.asm.tree.JumpInsnNode;
import org.objectweb.asm.tree.LabelNode;
import org.objectweb.asm.tree.LdcInsnNode;
import org.objectweb.asm.tree.LineNumberNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MultiANewArrayInsnNode;
import org.objectweb.asm.tree.TableSwitchInsnNode;
import org.objectweb.asm.tree.TypeInsnNode;
import org.objectweb.asm.tree.VarInsnNode;
import org.objectweb.asm.util.Printer;
import org.objectweb.asm.util.Textifier;
/**
* A utility class for bytecode-related tasks.
*
* @author blootooby
*/
public final class BytecodeUtils implements Opcodes {
/**
* Turns a textual representation of bytecode into actual bytecode.
*
* @param scriptLines A script of bytecode instruction representations.
* @return A list of instruction nodes created from the <code>scriptLines</code>.
* @throws InvalidBytecodeException If the script was invalid.
*/
public static InsnList generateInsnListFromScript(String[] scriptLines)
throws InvalidBytecodeException {
InsnList ret = new InsnList();
for (String line : scriptLines) {
String[] args = MiscUtils.translateCommandline(line);
/*
* for (int i = 0; i < args.length; i++) { // FIXME if (args[i].contains(" ")) { args[i] = '"'
* + args[i]; } }
*/
ret.add(getNodeFromArgs(args));
}
// Generate actual labels.
Map<Integer, LabelNode> labelz = new HashMap<Integer, LabelNode>();
ListIterator<AbstractInsnNode> li = ret.iterator();
AbstractInsnNode ain = li.hasNext() ? null : li.next();
for (int i = 0; li.hasNext(); ain = li.next(), i++) {
if (ain instanceof LineNumberNode) {
LineNumberNode lnn = (LineNumberNode) ain;
LabelNode cl = labelz.get(lnn.start.getLabel().getOffset());
if (cl == null) {
labelz.put(lnn.start.getLabel().getOffset(), lnn.start);
} else {
lnn.start = cl;
}
} else if (ain instanceof JumpInsnNode) {
JumpInsnNode jin = (JumpInsnNode) ain;
LabelNode cl = labelz.get(jin.label.getLabel().getOffset());
if (cl == null) {
labelz.put(jin.label.getLabel().getOffset(), jin.label);
} else {
jin.label = cl;
}
} else if (ain instanceof TableSwitchInsnNode) {
TableSwitchInsnNode tsin = (TableSwitchInsnNode) ain;
LabelNode cl = labelz.get(tsin.dflt.getLabel().getOffset());
if (cl == null) {
labelz.put(tsin.dflt.getLabel().getOffset(), tsin.dflt);
} else {
tsin.dflt = cl;
}
}
}
Map<AbstractInsnNode, LabelNode> nodeMap = new HashMap<AbstractInsnNode, LabelNode>();
for (Entry<Integer, LabelNode> e : labelz.entrySet()) {
nodeMap.put(ret.get(e.getKey()), e.getValue());
}
System.out.println("Adding labels!");
for (Entry<AbstractInsnNode, LabelNode> e : nodeMap.entrySet()) {
System.out.println(String.format("%s -> %s", e.getKey(), e.getValue()));
ret.insertBefore(e.getKey(), e.getValue());
}
System.out.println("Added labels.");
return ret;
}
/**
* Translates the string representation of an opcode to the integer representation.
*
* @param opcode the string representation of the opcode.
* @return the integer representation of the opcode, or -1 if the opcode was not found.
*/
public static int getOpcodeFromString(String opcode) {
if (opcode == null || opcode.length() == 0) {
return -1;
}
for (int i = 0; i < Printer.OPCODES.length; i++) {
String s = Printer.OPCODES[i];
if (s != null && s.equals(opcode)) {
return i;
}
}
return -1;
}
/**
* Translates the string representation of an operand type to the integer representation. Used
* only for BIPUSH and SIPUSH.
*
* @param type the string representation of the operand type.
* @return the integer representation of the operand type, or -1 if the operand type was not
* found.
*/
public static int getOperandTypeFromString(String type) {
if (type == null || type.length() == 0) {
return -1;
}
for (int i = 0; i < Printer.TYPES.length; i++) {
String s = Printer.TYPES[i];
if (s != null && s.equals(type)) {
return i;
}
}
return -1;
}
/**
* Turns a string representation of a bytecode instruction into an instruction node.
*
* @param args strings representing the instruction.
* @return an instruction node made from the <code>args</code>.
* @throws InvalidBytecodeException If the string representation was invalid.
*/
public static AbstractInsnNode getNodeFromArgs(String[] args) throws InvalidBytecodeException {
int opcode = getOpcodeFromString(args[0].toUpperCase());
switch (opcode) {
case NOP:
case ACONST_NULL:
case ICONST_M1:
case ICONST_0:
case ICONST_1:
case ICONST_2:
case ICONST_3:
case ICONST_4:
case ICONST_5:
case LCONST_0:
case LCONST_1:
case FCONST_0:
case FCONST_1:
case FCONST_2:
case DCONST_0:
case DCONST_1:
case IALOAD:
case LALOAD:
case FALOAD:
case DALOAD:
case AALOAD:
case BALOAD:
case CALOAD:
case SALOAD:
case IASTORE:
case LASTORE:
case FASTORE:
case DASTORE:
case AASTORE:
case BASTORE:
case CASTORE:
case SASTORE:
case POP:
case POP2:
case DUP:
case DUP_X1:
case DUP_X2:
case DUP2:
case DUP2_X1:
case DUP2_X2:
case SWAP:
case IADD:
case LADD:
case DADD:
case FADD:
case ISUB:
case LSUB:
case DSUB:
case FSUB:
case IMUL:
case LMUL:
case DMUL:
case FMUL:
case IDIV:
case LDIV:
case DDIV:
case FDIV:
case IREM:
case LREM:
case DREM:
case FREM:
case INEG:
case LNEG:
case DNEG:
case FNEG:
case ISHL:
case LSHL:
case ISHR:
case LSHR:
case IUSHR:
case LUSHR:
case IAND:
case LAND:
case IXOR:
case LXOR:
case I2L:
case I2F:
case I2D:
case L2I:
case L2F:
case L2D:
case F2L:
case F2D:
case F2I:
case D2I:
case D2L:
case D2F:
case I2B:
case I2C:
case I2S:
case LCMP:
case FCMPL:
case FCMPG:
case DCMPL:
case DCMPG:
case IRETURN:
case FRETURN:
case DRETURN:
case LRETURN:
case ARETURN:
case RETURN:
case ARRAYLENGTH:
case ATHROW:
case MONITORENTER:
case MONITOREXIT:
return new InsnNode(opcode);
case ILOAD:
case FLOAD:
case DLOAD:
case LLOAD:
case ALOAD:
case ISTORE:
case LSTORE:
case FSTORE:
case DSTORE:
case ASTORE:
case RET:
return new VarInsnNode(opcode, toint(args[1]));
case GETSTATIC:
case PUTSTATIC:
case GETFIELD:
case PUTFIELD:
return new FieldInsnNode(opcode, args[1], args[2], args[3]);
case INVOKESTATIC:
case INVOKEVIRTUAL:
case INVOKESPECIAL:
case INVOKEINTERFACE:
return new MethodInsnNode(opcode, args[1], args[2], args[3], tobool(args[4]));
case INVOKEDYNAMIC:
if (args.length < 7) {
return new InvokeDynamicInsnNode(args[1], args[2], new Handle(toint(args[3]), args[4],
args[5], args[6]));
}// else {
//
// for (int i = 7; i < args.length; i++) {
//
// }
// }
// TODO fuck it, I'll do the bsmArgs version another day. >:(
case IFEQ:
case IFNE:
case IFLT:
case IFGT:
case IFLE:
case IFGE:
case IF_ICMPEQ:
case IF_ICMPNE:
case IF_ICMPLT:
case IF_ICMPGE:
case IF_ICMPGT:
case IF_ICMPLE:
case IF_ACMPEQ:
case IF_ACMPNE:
case GOTO:
case JSR:
case IFNULL:
case IFNONNULL:
return new JumpInsnNode(opcode, getLabelNode(toint(args[1]))); // this was annoying to do.
case BIPUSH:
case SIPUSH:
return new IntInsnNode(opcode, toint(args[1]));
case NEWARRAY:
return new IntInsnNode(opcode, getOperandTypeFromString(args[1]));
case LDC:
if (args[1].charAt(0) == '"') {
return new LdcInsnNode(args[1].replace("\\n", "\n").substring(1));
} else if (args[1].charAt(0) == 'L' && args[1].charAt(args[1].length()) == ';') {
return new LdcInsnNode(Type.getType(args[1]));
} else {
System.out.println(args[1]);
char c = args[1].charAt(args[1].length());
switch (c) {
case 'D':
case 'd':
return new LdcInsnNode(todouble(args[1]));
case 'F':
case 'f':
return new LdcInsnNode(tofloat(args[1]));
case 'L':
case 'l':
return new LdcInsnNode(tolong(args[1]));
default:
return new LdcInsnNode(toint(args[1]));
}
}
case NEW:
case ANEWARRAY:
case CHECKCAST:
case INSTANCEOF:
return new TypeInsnNode(opcode, args[1]);
case -1:
switch (args[0].toUpperCase()) {
case "LINE":
case "LINENUMBER":
return new LineNumberNode(toint(args[1]), getLabelNode(toint(args[2])));// FIXME
case "MULTIANEWARRAY":
return new MultiANewArrayInsnNode(args[1], toint(args[2]));
case "TABLESWITCH":
case "SWITCH":
return new TableSwitchInsnNode(toint(args[1]), toint(args[2]),
getLabelNode(toint(args[3])), getLabelNode(toint(args[4]))); // FIXME arg4 is a
// varargs
case "IINC":
return new IincInsnNode(toint(args[1]), toint(args[2]));
case "FRAME":
switch (args[1].toUpperCase()) {
case "SAME":
return new FrameNode(F_SAME, 0, null, 0, null);
case "SAME1":
switch (Character.toUpperCase(args[2].charAt(0))) {
case 'T':
return new FrameNode(F_SAME1, 0, null, 1, new Object[] {0});
case 'I':
return new FrameNode(F_SAME1, 0, null, 1, new Object[] {1});
case 'F':
return new FrameNode(F_SAME1, 0, null, 1, new Object[] {2});
case 'D':
return new FrameNode(F_SAME1, 0, null, 1, new Object[] {3});
case 'J':
return new FrameNode(F_SAME1, 0, null, 1, new Object[] {4});
case 'N':
return new FrameNode(F_SAME1, 0, null, 1, new Object[] {5});
case 'U':
return new FrameNode(F_SAME1, 0, null, 1, new Object[] {6});
case 'L':
return new FrameNode(F_SAME1, 0, null, 1, new Object[] {new Label()});// FIXME
case '[':
default:
return new FrameNode(F_SAME1, 0, null, 1, new Object[] {args[2]});
}
case "CHOP":
return new FrameNode(F_CHOP, toint(args[2]), null, 0, null);
case "FULL":
case "NEW": {// DAMN THIS IS GONNA SUCK DDD:
boolean doingLocals = true;
boolean done = false;
List<Object> locals = new ArrayList<Object>();
List<Object> stack = new ArrayList<Object>();
for (int i = 2; i < args.length; i++) {
if (args[i].length() > 0) {
String part = args[i];
if (part.charAt(0) == '[') {
part = part.substring(1);
} else if (part.endsWith("]")) {
if (!doingLocals) {
done = true;
} else {
locals.add(part.substring(0, part.length() - 1));
doingLocals = false;
}
part = part.substring(0, part.length() - 1);
switch (Character.toUpperCase(args[i].charAt(0))) {
case 'T':
(doingLocals ? locals : stack).add(0);
case 'I':
(doingLocals ? locals : stack).add(1);
case 'F':
(doingLocals ? locals : stack).add(2);
case 'D':
(doingLocals ? locals : stack).add(3);
case 'J':
(doingLocals ? locals : stack).add(4);
case 'N':
(doingLocals ? locals : stack).add(5);
case 'U':
(doingLocals ? locals : stack).add(6);
case 'L':
(doingLocals ? locals : stack).add(new CustomLabel(toint(part
.substring(1))));
case '[':
default:
(doingLocals ? locals : stack).add(args[i]);
}
}
if (done) {
break;
}
}
}
return new FrameNode(args[1].equalsIgnoreCase("new") ? F_NEW : F_FULL,
locals.size(), locals.toArray(), stack.size(), stack.toArray());
}
case "APPEND": {
boolean done = false;
List<Object> locals = new ArrayList<Object>();
for (int i = 2; i < args.length; i++) {
String part = args[i];
if (part.length() > 0) {
if (part.charAt(0) == '[') {
part = part.substring(1);
} else if (part.endsWith("]")) {
done = true;
part = part.substring(0, part.length() - 1);
}
if (!part.contains("/")) {
switch (Character.toUpperCase(part.charAt(0))) {
case 'T':
locals.add(0);
case 'I':
locals.add(1);
case 'F':
locals.add(2);
case 'D':
locals.add(3);
case 'J':
locals.add(4);
case 'N':
locals.add(5);
case 'U':
locals.add(6);
case 'L':
locals.add(new CustomLabel(toint(part.substring(1))));
case '[':
default:
locals.add(part);
}
} else {
locals.add(part);
}
}
if (done) {
break;
}
}
return new FrameNode(F_APPEND, locals.size(), locals.toArray(), 0, null);
}
}
}
throw new InvalidBytecodeException(args);
default:
throw new InvalidBytecodeException(args);
}
}
public static List<String> bytecodeToText(InsnList instructions) {
List<String> ret = new ArrayList<String>();
ListIterator<AbstractInsnNode> li = instructions.iterator();
while (li.hasNext()) {
AbstractInsnNode ain = li.next();
String textOpcode = "";
if (ain.getOpcode() != -1) {
textOpcode = Printer.OPCODES[ain.getOpcode()];
} else {
// oh boy, special case. yaaay :(
}
String fullInsn = textOpcode;
switch (ain.getType()) {
case AbstractInsnNode.FIELD_INSN:
FieldInsnNode fin = (FieldInsnNode) ain;
fullInsn += ' ' + fin.owner;
fullInsn += ' ' + fin.name;
fullInsn += ' ' + fin.desc;
break;
case AbstractInsnNode.JUMP_INSN:
JumpInsnNode jin = (JumpInsnNode) ain;
try {
int offset = getLabelOffset(jin.label.getLabel(), instructions); // YAY FIXED
fullInsn += " " + offset;
} catch (Exception e) {
e.printStackTrace();
fullInsn += " [offset not resolved! :(]";
}
break;
case AbstractInsnNode.IINC_INSN:
IincInsnNode iin = (IincInsnNode) ain;
fullInsn += " " + iin.var;
fullInsn += " " + iin.incr;
break;
case AbstractInsnNode.LOOKUPSWITCH_INSN:
case AbstractInsnNode.INSN:
break;
case AbstractInsnNode.LDC_INSN:
LdcInsnNode lin = (LdcInsnNode) ain;
if (lin.cst instanceof String) {
fullInsn += " \"" + ((String) lin.cst).replace("\n", "\\n") + "\"";
} else if (lin.cst instanceof Type) {
fullInsn += ' ' + ((Type) lin.cst).getDescriptor();
} else {
fullInsn += " " + lin.cst;
}
break;
case AbstractInsnNode.INT_INSN:
IntInsnNode iin1 = (IntInsnNode) ain;
fullInsn += " " + iin1.operand;
break;
case AbstractInsnNode.LINE:
fullInsn =
"LINENUMBER " + ((LineNumberNode) ain).line + " "
+ getLabelOffset(((LineNumberNode) ain).start.getLabel(), instructions);
break;
case AbstractInsnNode.METHOD_INSN:
MethodInsnNode min = (MethodInsnNode) ain;
fullInsn += ' ' + min.owner;
fullInsn += ' ' + min.name;
fullInsn += ' ' + min.desc;
fullInsn += " " + min.itf;
break;
case AbstractInsnNode.MULTIANEWARRAY_INSN:
MultiANewArrayInsnNode manin = (MultiANewArrayInsnNode) ain;
fullInsn += ' ' + manin.desc;
fullInsn += " " + manin.dims;
break;
case AbstractInsnNode.TABLESWITCH_INSN:
TableSwitchInsnNode tsin = (TableSwitchInsnNode) ain;
fullInsn += " " + tsin.min;
fullInsn += " " + tsin.max;
fullInsn += ' ' + tsin.dflt.getLabel().getOffset();
for (LabelNode ln : (List<LabelNode>) tsin.labels) {
fullInsn += ' ' + ln.getLabel().getOffset();
}
break;
case AbstractInsnNode.TYPE_INSN:
TypeInsnNode tin = (TypeInsnNode) ain;
fullInsn += ' ' + tin.desc;
break;
case AbstractInsnNode.VAR_INSN:
VarInsnNode vin = (VarInsnNode) ain;
fullInsn += " " + vin.var;
break;
case AbstractInsnNode.LABEL:
continue;
case AbstractInsnNode.INVOKE_DYNAMIC_INSN:
InvokeDynamicInsnNode idin = (InvokeDynamicInsnNode) ain;
// idin.
// TODO all of invokedynamic.
break;
case AbstractInsnNode.FRAME:
FrameNode fn = (FrameNode) ain;
fullInsn = translateFrameToText(fn, instructions);
break;
// TODO the rest.
}
ret.add(fullInsn);
}
return ret;
}
public static String translateFrameToText(FrameNode fn, InsnList instructions) {
String ret = "FRAME ";
switch (fn.type) {
case F_NEW:
case F_FULL:
ret += fn.type == F_NEW ? "NEW [" : "FULL [";
ret = appendFrameTypes(ret, instructions, fn.local.size(), fn.local.toArray());
ret += "] [";
ret = appendFrameTypes(ret, instructions, fn.stack.size(), fn.stack.toArray());
return ret + ']';
case F_APPEND:
ret += "APPEND [";
ret = appendFrameTypes(ret, instructions, fn.local.size(), fn.local.toArray());
return ret + ']';
case F_CHOP:
return "FRAME CHOP " + fn.local.size();
case F_SAME:
return "FRAME SAME";
case F_SAME1:
ret += "SAME1 ";
ret = appendFrameTypes(ret, instructions, 1, fn.stack.toArray());
return ret;
default:
return "FRAME";
}
}
public static String appendFrameTypes(String buf, InsnList instructions, final int n,
final Object[] o) {
for (int i = 0; i < n; ++i) {
if (i > 0) {
buf += ' ';
}
if (o[i] instanceof String) {
String desc = (String) o[i];
if (desc.startsWith("[")) {
buf = appendDescriptor(buf, Textifier.FIELD_DESCRIPTOR, desc);
} else {
buf = appendDescriptor(buf, Textifier.INTERNAL_NAME, desc);
}
} else if (o[i] instanceof Integer) {
switch (((Integer) o[i]).intValue()) {
case 0:
buf = appendDescriptor(buf, Textifier.FIELD_DESCRIPTOR, "T");
break;
case 1:
buf = appendDescriptor(buf, Textifier.FIELD_DESCRIPTOR, "I");
break;
case 2:
buf = appendDescriptor(buf, Textifier.FIELD_DESCRIPTOR, "F");
break;
case 3:
buf = appendDescriptor(buf, Textifier.FIELD_DESCRIPTOR, "D");
break;
case 4:
buf = appendDescriptor(buf, Textifier.FIELD_DESCRIPTOR, "J");
break;
case 5:
buf = appendDescriptor(buf, Textifier.FIELD_DESCRIPTOR, "N");
break;
case 6:
buf = appendDescriptor(buf, Textifier.FIELD_DESCRIPTOR, "U");
break;
}
} else if (o[i] instanceof Label) {
buf += "L" + getLabelOffset((Label) o[i], instructions);
} else {
throw new Error("Invalid frame parts!");
}
}
return buf;
}
private static String appendDescriptor(String buf, final int type, final String desc) {
if (type == Textifier.CLASS_SIGNATURE || type == Textifier.FIELD_SIGNATURE
|| type == Textifier.METHOD_SIGNATURE) {
if (desc != null) {
buf += desc + '\n'; // TODO look into this later. (signatures)
}
} else {
buf += desc;
}
return buf;
}
/**
* A convenience method for converting a {@link String} to an <code>int</code>.
*
* @param s {@link String} to parse.
* @return the converted <code>int</code>.
* @throws NumberFormatException If the {@link String} failed to parse.
* @see Integer#parseInt(String)
*/
private static int toint(String s) throws NumberFormatException {
return Integer.parseInt(s);
}
/**
* A convenience method for converting a {@link String} to an <code>float</code>.
*
* @param s {@link String} to parse.
* @return the converted <code>float</code>.
* @throws NumberFormatException If the {@link String} failed to parse.
* @see Float#parseFloat(String)
*/
private static float tofloat(String s) throws NumberFormatException {
return Float.parseFloat(s);
}
/**
* A convenience method for converting a {@link String} to an <code>long</code>.
*
* @param s {@link String} to parse.
* @return the converted <code>long</code>.
* @throws NumberFormatException If the {@link String} failed to parse.
* @see Long#parseLong(String)
*/
private static long tolong(String s) throws NumberFormatException {
return Long.parseLong(s);
}
/**
* A convenience method for converting a {@link String} to an <code>double</code>.
*
* @param s {@link String} to parse.
* @return the converted <code>double</code>.
* @throws NumberFormatException If the {@link String} failed to parse.
* @see Double#parseDouble(String)
*/
private static double todouble(String s) throws NumberFormatException {
return Double.parseDouble(s);
}
/**
* A convenience method for converting a {@link String} to an <code>boolean</code>.
*
* @param s {@link String} to parse.
* @return the converted <code>boolean</code>.
* @see Boolean#parseBoolean(String)
*/
private static boolean tobool(String s) {
return Boolean.parseBoolean(s);
}
/**
* Creates a {@link LabelNode} with a custom offset.
*
* @param offset the offset for the {@link Label}.
* @return a custom-offset {@link LabelNode}
* @see CustomLabel
* @see CustomLabel#getOffset()
* @see org.objectweb.asm.Label#getOffset()
*/
public static LabelNode getLabelNode(int offset) {
return new LabelNode(new CustomLabel(offset));
}
// TODO javadocs for these *shudder*
public static boolean isAbstract(int access) {
return (access & ACC_ABSTRACT) != 0;
}
public static boolean isFinal(int access) {
return (access & ACC_FINAL) != 0;
}
public static boolean isStatic(int access) {
return (access & ACC_STATIC) != 0;
}
public static boolean isNative(int access) {
return (access & ACC_NATIVE) != 0;
}
public static boolean isSynthetic(int access) {
return (access & ACC_SYNTHETIC) != 0;
}
public static boolean isSynchronized(int access) {
return (access & ACC_SYNCHRONIZED) != 0;
}
public static boolean isEnum(int access) {
return (access & ACC_ENUM) != 0;
}
public static boolean isBridge(int access) {
return (access & ACC_BRIDGE) != 0;
}
public static boolean isVolatile(int access) {
return (access & ACC_VOLATILE) != 0;
}
public static boolean isDeprecated(int access) {
return (access & ACC_DEPRECATED) != 0;
}
public static boolean isMandated(int access) {
return (access & ACC_MANDATED) != 0;
}
public static boolean isInterface(int access) {
return (access & ACC_INTERFACE) != 0;
}
public static boolean isAnnotated(int access) {
return (access & ACC_ANNOTATION) != 0;
}
public static boolean isPrivate(int access) {
return (access & ACC_PRIVATE) != 0;
}
public static boolean isPublic(int access) {
return (access & ACC_PUBLIC) != 0;
}
public static boolean isProtected(int access) {
return (access & ACC_PROTECTED) != 0;
}
public static boolean isStrictFP(int access) {
return (access & ACC_STRICT) != 0;
}
public static boolean isSuper(int access) { // archaic, no longer used as of some Java 1.7 update
return (access & ACC_SUPER) != 0;
}
public static boolean isVarArgs(int access) {
return (access & ACC_VARARGS) != 0;
}
public static boolean isTransient(int access) {
return (access & ACC_TRANSIENT) != 0;
}
public static int removeFlag(int flag, int flags) {
return flags & ~flag;
}
public static int addFlag(int flag, int flags) {
return flags | flag;
}
public static int getLabelOffset(Label label, InsnList instructions) {
try {
return label.getOffset();
} catch (Exception e) {
ListIterator<AbstractInsnNode> li = instructions.iterator();
AbstractInsnNode ain = li.next();
for (int i = 0; li.hasNext(); ain = li.next(), i++) {
if (ain instanceof LabelNode) {
if (((LabelNode) ain).getLabel() == label) {
return i;
} else {
i--; // this fixes the issue where it counted labels as instructions.
continue;
}
}
}
return -1;
}
}
}