package codechicken.lib.asm; import org.objectweb.asm.tree.*; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.HashMap; import java.util.Map; import static org.objectweb.asm.Opcodes.*; import static org.objectweb.asm.tree.AbstractInsnNode.*; public class ASMReader { public static Map<String, Integer> opCodes = new HashMap<String, Integer>(); public static byte[] TYPE; static { opCodes.put("NOP", NOP); opCodes.put("ACONST_NULL", ACONST_NULL); opCodes.put("ICONST_M1", ICONST_M1); opCodes.put("ICONST_0", ICONST_0); opCodes.put("ICONST_1", ICONST_1); opCodes.put("ICONST_2", ICONST_2); opCodes.put("ICONST_3", ICONST_3); opCodes.put("ICONST_4", ICONST_4); opCodes.put("ICONST_5", ICONST_5); opCodes.put("LCONST_0", LCONST_0); opCodes.put("LCONST_1", LCONST_1); opCodes.put("FCONST_0", FCONST_0); opCodes.put("FCONST_1", FCONST_1); opCodes.put("FCONST_2", FCONST_2); opCodes.put("DCONST_0", DCONST_0); opCodes.put("DCONST_1", DCONST_1); opCodes.put("BIPUSH", BIPUSH); opCodes.put("SIPUSH", SIPUSH); opCodes.put("LDC", LDC); opCodes.put("ILOAD", ILOAD); opCodes.put("LLOAD", LLOAD); opCodes.put("FLOAD", FLOAD); opCodes.put("DLOAD", DLOAD); opCodes.put("ALOAD", ALOAD); opCodes.put("IALOAD", IALOAD); opCodes.put("LALOAD", LALOAD); opCodes.put("FALOAD", FALOAD); opCodes.put("DALOAD", DALOAD); opCodes.put("AALOAD", AALOAD); opCodes.put("BALOAD", BALOAD); opCodes.put("CALOAD", CALOAD); opCodes.put("SALOAD", SALOAD); opCodes.put("ISTORE", ISTORE); opCodes.put("LSTORE", LSTORE); opCodes.put("FSTORE", FSTORE); opCodes.put("DSTORE", DSTORE); opCodes.put("ASTORE", ASTORE); opCodes.put("IASTORE", IASTORE); opCodes.put("LASTORE", LASTORE); opCodes.put("FASTORE", FASTORE); opCodes.put("DASTORE", DASTORE); opCodes.put("AASTORE", AASTORE); opCodes.put("BASTORE", BASTORE); opCodes.put("CASTORE", CASTORE); opCodes.put("SASTORE", SASTORE); opCodes.put("POP", POP); opCodes.put("POP2", POP2); opCodes.put("DUP", DUP); opCodes.put("DUP_X1", DUP_X1); opCodes.put("DUP_X2", DUP_X2); opCodes.put("DUP2", DUP2); opCodes.put("DUP2_X1", DUP2_X1); opCodes.put("DUP2_X2", DUP2_X2); opCodes.put("SWAP", SWAP); opCodes.put("IADD", IADD); opCodes.put("LADD", LADD); opCodes.put("FADD", FADD); opCodes.put("DADD", DADD); opCodes.put("ISUB", ISUB); opCodes.put("LSUB", LSUB); opCodes.put("FSUB", FSUB); opCodes.put("DSUB", DSUB); opCodes.put("IMUL", IMUL); opCodes.put("LMUL", LMUL); opCodes.put("FMUL", FMUL); opCodes.put("DMUL", DMUL); opCodes.put("IDIV", IDIV); opCodes.put("LDIV", LDIV); opCodes.put("FDIV", FDIV); opCodes.put("DDIV", DDIV); opCodes.put("IREM", IREM); opCodes.put("LREM", LREM); opCodes.put("FREM", FREM); opCodes.put("DREM", DREM); opCodes.put("INEG", INEG); opCodes.put("LNEG", LNEG); opCodes.put("FNEG", FNEG); opCodes.put("DNEG", DNEG); opCodes.put("ISHL", ISHL); opCodes.put("LSHL", LSHL); opCodes.put("ISHR", ISHR); opCodes.put("LSHR", LSHR); opCodes.put("IUSHR", IUSHR); opCodes.put("LUSHR", LUSHR); opCodes.put("IAND", IAND); opCodes.put("LAND", LAND); opCodes.put("IOR", IOR); opCodes.put("LOR", LOR); opCodes.put("IXOR", IXOR); opCodes.put("LXOR", LXOR); opCodes.put("IINC", IINC); opCodes.put("I2L", I2L); opCodes.put("I2F", I2F); opCodes.put("I2D", I2D); opCodes.put("L2I", L2I); opCodes.put("L2F", L2F); opCodes.put("L2D", L2D); opCodes.put("F2I", F2I); opCodes.put("F2L", F2L); opCodes.put("F2D", F2D); opCodes.put("D2I", D2I); opCodes.put("D2L", D2L); opCodes.put("D2F", D2F); opCodes.put("I2B", I2B); opCodes.put("I2C", I2C); opCodes.put("I2S", I2S); opCodes.put("LCMP", LCMP); opCodes.put("FCMPL", FCMPL); opCodes.put("FCMPG", FCMPG); opCodes.put("DCMPL", DCMPL); opCodes.put("DCMPG", DCMPG); opCodes.put("IFEQ", IFEQ); opCodes.put("IFNE", IFNE); opCodes.put("IFLT", IFLT); opCodes.put("IFGE", IFGE); opCodes.put("IFGT", IFGT); opCodes.put("IFLE", IFLE); opCodes.put("IF_ICMPEQ", IF_ICMPEQ); opCodes.put("IF_ICMPNE", IF_ICMPNE); opCodes.put("IF_ICMPLT", IF_ICMPLT); opCodes.put("IF_ICMPGE", IF_ICMPGE); opCodes.put("IF_ICMPGT", IF_ICMPGT); opCodes.put("IF_ICMPLE", IF_ICMPLE); opCodes.put("IF_ACMPEQ", IF_ACMPEQ); opCodes.put("IF_ACMPNE", IF_ACMPNE); opCodes.put("GOTO", GOTO); opCodes.put("JSR", JSR); opCodes.put("RET", RET); opCodes.put("TABLESWITCH", TABLESWITCH); opCodes.put("LOOKUPSWITCH", LOOKUPSWITCH); opCodes.put("IRETURN", IRETURN); opCodes.put("LRETURN", LRETURN); opCodes.put("FRETURN", FRETURN); opCodes.put("DRETURN", DRETURN); opCodes.put("ARETURN", ARETURN); opCodes.put("RETURN", RETURN); opCodes.put("GETSTATIC", GETSTATIC); opCodes.put("PUTSTATIC", PUTSTATIC); opCodes.put("GETFIELD", GETFIELD); opCodes.put("PUTFIELD", PUTFIELD); opCodes.put("INVOKEVIRTUAL", INVOKEVIRTUAL); opCodes.put("INVOKESPECIAL", INVOKESPECIAL); opCodes.put("INVOKESTATIC", INVOKESTATIC); opCodes.put("INVOKEINTERFACE", INVOKEINTERFACE); opCodes.put("INVOKEDYNAMIC", INVOKEDYNAMIC); opCodes.put("NEW", NEW); opCodes.put("NEWARRAY", NEWARRAY); opCodes.put("ANEWARRAY", ANEWARRAY); opCodes.put("ARRAYLENGTH", ARRAYLENGTH); opCodes.put("ATHROW", ATHROW); opCodes.put("CHECKCAST", CHECKCAST); opCodes.put("INSTANCEOF", INSTANCEOF); opCodes.put("MONITORENTER", MONITORENTER); opCodes.put("MONITOREXIT", MONITOREXIT); opCodes.put("MULTIANEWARRAY", MULTIANEWARRAY); opCodes.put("IFNULL", IFNULL); opCodes.put("IFNONNULL", IFNONNULL); //derived from classWriter, mapped to AbstractInsnNode TYPE = new byte[200]; String s = "AAAAAAAAAAAAAAAABBJ__CCCCC____________________AAAAAAAACC" + "CCC____________________AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAKAAAAAAAAAAAAAAAAAAAAHHHHHHHHHHHHHHHHCLMAA" + "AAAAEEEEFFFFGDBDAADDAA_NHH"; for (int i = 0; i < s.length(); i++) { TYPE[i] = (byte) (s.charAt(i) - 'A'); } } public static Map<String, ASMBlock> loadResource(String res) { return loadResource(ASMHelper.class.getResourceAsStream(res), res); } public static Map<String, ASMBlock> loadResource(InputStream in, String res) { HashMap<String, ASMBlock> blocks = new HashMap<String, ASMBlock>(); String current = "unnamed"; ASMBlock block = new ASMBlock(); try { BufferedReader r = new BufferedReader(new InputStreamReader(in)); String line; while ((line = r.readLine()) != null) { { int hpos = line.indexOf('#'); if (hpos >= 0) { line = line.substring(0, hpos); } } line = line.trim(); if (line.length() == 0) { continue; } if (line.startsWith("list ")) { if (block.list.size() > 0) { blocks.put(current, block); } current = line.substring(5); block = new ASMBlock(); continue; } try { AbstractInsnNode insn = null; String[] split = line.replace(" : ", ":").split(" "); Integer i_opcode = opCodes.get(split[0]); if (i_opcode == null) { if (split[0].equals("LINENUMBER")) { insn = new LineNumberNode(Integer.parseInt(split[1]), block.getOrAdd(split[2])); } else if (split[0].startsWith("L")) { insn = block.getOrAdd(split[0]); } else { throw new Exception("Unknown opcode " + split[0]); } } else { int opcode = i_opcode; switch (TYPE[opcode]) { case INSN: insn = new InsnNode(opcode); break; case INT_INSN: insn = new IntInsnNode(opcode, Integer.parseInt(split[1])); break; case VAR_INSN: insn = new VarInsnNode(opcode, Integer.parseInt(split[1])); break; case TYPE_INSN: insn = new ObfMapping(split[1]).toClassloading().toInsn(opcode); break; case FIELD_INSN: case METHOD_INSN: StringBuilder sb = new StringBuilder(); for (int i = 1; i < split.length; i++) { sb.append(split[i]); } insn = ObfMapping.fromDesc(sb.toString()).toClassloading().toInsn(opcode); break; case INVOKE_DYNAMIC_INSN: throw new Exception("Found INVOKEDYNAMIC while reading"); case JUMP_INSN: insn = new JumpInsnNode(opcode, block.getOrAdd(split[1])); break; case LDC_INSN: String cst = split[1]; if (cst.equals("*")) { insn = new LdcInsnNode(null); } else if (cst.endsWith("\"")) { insn = new LdcInsnNode(cst.substring(1, cst.length() - 1)); } else if (cst.endsWith("L")) { insn = new LdcInsnNode(Long.valueOf(cst.substring(0, cst.length() - 1))); } else if (cst.endsWith("F")) { insn = new LdcInsnNode(Float.valueOf(cst.substring(0, cst.length() - 1))); } else if (cst.endsWith("D")) { insn = new LdcInsnNode(Double.valueOf(cst.substring(0, cst.length() - 1))); } else if (cst.contains(".")) { insn = new LdcInsnNode(Double.valueOf(cst)); } else { insn = new LdcInsnNode(Integer.valueOf(cst)); } break; case IINC_INSN: insn = new IincInsnNode(opcode, Integer.parseInt(split[1])); break; case LABEL: throw new Exception("Use L# for labels"); case TABLESWITCH_INSN: case LOOKUPSWITCH_INSN: throw new Exception("I don't know how to deal with this insn type"); case MULTIANEWARRAY_INSN: insn = new MultiANewArrayInsnNode(split[1], Integer.parseInt(split[2])); break; case FRAME: throw new Exception("Use ClassWriter.COMPUTE_FRAMES"); } } if (insn != null) { block.list.add(insn); } } catch (Exception e) { System.err.println("Error while reading ASM Block " + current + " from " + res + ", line: " + line); e.printStackTrace(); } } r.close(); if (block.list.size() > 0) { blocks.put(current, block); } } catch (IOException e) { throw new RuntimeException("Failed to read ASM resource: " + res, e); } return blocks; } }