Home | History | Annotate | Download | only in bytecode
      1 /*
      2  * Javassist, a Java-bytecode translator toolkit.
      3  * Copyright (C) 1999-2007 Shigeru Chiba, and others. All Rights Reserved.
      4  *
      5  * The contents of this file are subject to the Mozilla Public License Version
      6  * 1.1 (the "License"); you may not use this file except in compliance with
      7  * the License.  Alternatively, the contents of this file may be used under
      8  * the terms of the GNU Lesser General Public License Version 2.1 or later.
      9  *
     10  * Software distributed under the License is distributed on an "AS IS" basis,
     11  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
     12  * for the specific language governing rights and limitations under the
     13  * License.
     14  */
     15 package javassist.bytecode;
     16 
     17 import java.io.PrintStream;
     18 
     19 import javassist.CtMethod;
     20 
     21 /**
     22  * Simple utility class for printing the instructions of a method.
     23  *
     24  * @author Jason T. Greene
     25  */
     26 public class InstructionPrinter implements Opcode {
     27 
     28     private final static String opcodes[] = Mnemonic.OPCODE;
     29     private final PrintStream stream;
     30 
     31     public InstructionPrinter(PrintStream stream) {
     32         this.stream = stream;
     33     }
     34 
     35     public static void print(CtMethod method, PrintStream stream) {
     36         (new InstructionPrinter(stream)).print(method);
     37     }
     38 
     39     public void print(CtMethod method) {
     40         MethodInfo info = method.getMethodInfo2();
     41         ConstPool pool = info.getConstPool();
     42         CodeAttribute code = info.getCodeAttribute();
     43         if (code == null)
     44             return;
     45 
     46         CodeIterator iterator = code.iterator();
     47         while (iterator.hasNext()) {
     48             int pos;
     49             try {
     50                 pos = iterator.next();
     51             } catch (BadBytecode e) {
     52                 throw new RuntimeException(e);
     53             }
     54 
     55             stream.println(pos + ": " + instructionString(iterator, pos, pool));
     56         }
     57     }
     58 
     59     public static String instructionString(CodeIterator iter, int pos, ConstPool pool) {
     60         int opcode = iter.byteAt(pos);
     61 
     62         if (opcode > opcodes.length || opcode < 0)
     63             throw new IllegalArgumentException("Invalid opcode, opcode: " + opcode + " pos: "+ pos);
     64 
     65         String opstring = opcodes[opcode];
     66         switch (opcode) {
     67             case BIPUSH:
     68                 return opstring + " " + iter.byteAt(pos + 1);
     69             case SIPUSH:
     70                 return opstring + " " + iter.s16bitAt(pos + 1);
     71             case LDC:
     72                 return opstring + " " + ldc(pool, iter.byteAt(pos + 1));
     73             case LDC_W :
     74             case LDC2_W :
     75                 return opstring + " " + ldc(pool, iter.u16bitAt(pos + 1));
     76             case ILOAD:
     77             case LLOAD:
     78             case FLOAD:
     79             case DLOAD:
     80             case ALOAD:
     81             case ISTORE:
     82             case LSTORE:
     83             case FSTORE:
     84             case DSTORE:
     85             case ASTORE:
     86                 return opstring + " " + iter.byteAt(pos + 1);
     87             case IFEQ:
     88             case IFGE:
     89             case IFGT:
     90             case IFLE:
     91             case IFLT:
     92             case IFNE:
     93             case IFNONNULL:
     94             case IFNULL:
     95             case IF_ACMPEQ:
     96             case IF_ACMPNE:
     97             case IF_ICMPEQ:
     98             case IF_ICMPGE:
     99             case IF_ICMPGT:
    100             case IF_ICMPLE:
    101             case IF_ICMPLT:
    102             case IF_ICMPNE:
    103                 return opstring + " " + (iter.s16bitAt(pos + 1) + pos);
    104             case IINC:
    105                 return opstring + " " + iter.byteAt(pos + 1);
    106             case GOTO:
    107             case JSR:
    108                 return opstring + " " + (iter.s16bitAt(pos + 1) + pos);
    109             case RET:
    110                 return opstring + " " + iter.byteAt(pos + 1);
    111             case TABLESWITCH:
    112                 return tableSwitch(iter, pos);
    113             case LOOKUPSWITCH:
    114                 return lookupSwitch(iter, pos);
    115             case GETSTATIC:
    116             case PUTSTATIC:
    117             case GETFIELD:
    118             case PUTFIELD:
    119                 return opstring + " " + fieldInfo(pool, iter.u16bitAt(pos + 1));
    120             case INVOKEVIRTUAL:
    121             case INVOKESPECIAL:
    122             case INVOKESTATIC:
    123                 return opstring + " " + methodInfo(pool, iter.u16bitAt(pos + 1));
    124             case INVOKEINTERFACE:
    125                 return opstring + " " + interfaceMethodInfo(pool, iter.u16bitAt(pos + 1));
    126             case 186:
    127                 throw new RuntimeException("Bad opcode 186");
    128             case NEW:
    129                 return opstring + " " + classInfo(pool, iter.u16bitAt(pos + 1));
    130             case NEWARRAY:
    131                 return opstring + " " + arrayInfo(iter.byteAt(pos + 1));
    132             case ANEWARRAY:
    133             case CHECKCAST:
    134                 return opstring + " " + classInfo(pool, iter.u16bitAt(pos + 1));
    135             case WIDE:
    136                 return wide(iter, pos);
    137             case MULTIANEWARRAY:
    138                 return opstring + " " + classInfo(pool, iter.u16bitAt(pos + 1));
    139             case GOTO_W:
    140             case JSR_W:
    141                 return opstring + " " + (iter.s32bitAt(pos + 1)+ pos);
    142             default:
    143                 return opstring;
    144         }
    145     }
    146 
    147 
    148     private static String wide(CodeIterator iter, int pos) {
    149         int opcode = iter.byteAt(pos + 1);
    150         int index = iter.u16bitAt(pos + 2);
    151         switch (opcode) {
    152             case ILOAD:
    153             case LLOAD:
    154             case FLOAD:
    155             case DLOAD:
    156             case ALOAD:
    157             case ISTORE:
    158             case LSTORE:
    159             case FSTORE:
    160             case DSTORE:
    161             case ASTORE:
    162             case IINC:
    163             case RET:
    164                 return opcodes[opcode] + " " + index;
    165             default:
    166                 throw new RuntimeException("Invalid WIDE operand");
    167         }
    168     }
    169 
    170 
    171     private static String arrayInfo(int type) {
    172         switch (type) {
    173             case T_BOOLEAN:
    174                 return "boolean";
    175             case T_CHAR:
    176                 return "char";
    177             case T_BYTE:
    178                 return "byte";
    179             case T_SHORT:
    180                 return "short";
    181             case T_INT:
    182                 return "int";
    183             case T_LONG:
    184                 return "long";
    185             case T_FLOAT:
    186                 return "float";
    187             case T_DOUBLE:
    188                 return "double";
    189             default:
    190                 throw new RuntimeException("Invalid array type");
    191         }
    192     }
    193 
    194 
    195     private static String classInfo(ConstPool pool, int index) {
    196         return "#" + index + " = Class " + pool.getClassInfo(index);
    197     }
    198 
    199 
    200     private static String interfaceMethodInfo(ConstPool pool, int index) {
    201         return "#" + index + " = Method "
    202                 + pool.getInterfaceMethodrefClassName(index) + "."
    203                 + pool.getInterfaceMethodrefName(index) + "("
    204                 + pool.getInterfaceMethodrefType(index) + ")";
    205     }
    206 
    207     private static String methodInfo(ConstPool pool, int index) {
    208         return "#" + index + " = Method "
    209                 + pool.getMethodrefClassName(index) + "."
    210                 + pool.getMethodrefName(index) + "("
    211                 + pool.getMethodrefType(index) + ")";
    212     }
    213 
    214 
    215     private static String fieldInfo(ConstPool pool, int index) {
    216         return "#" + index + " = Field "
    217             + pool.getFieldrefClassName(index) + "."
    218             + pool.getFieldrefName(index) + "("
    219             + pool.getFieldrefType(index) + ")";
    220     }
    221 
    222 
    223     private static String lookupSwitch(CodeIterator iter, int pos) {
    224         StringBuffer buffer = new StringBuffer("lookupswitch {\n");
    225         int index = (pos & ~3) + 4;
    226         // default
    227         buffer.append("\t\tdefault: ").append(pos + iter.s32bitAt(index)).append("\n");
    228         int npairs = iter.s32bitAt(index += 4);
    229         int end = npairs * 8 + (index += 4);
    230 
    231         for (; index < end; index += 8) {
    232             int match = iter.s32bitAt(index);
    233             int target = iter.s32bitAt(index + 4) + pos;
    234             buffer.append("\t\t").append(match).append(": ").append(target).append("\n");
    235         }
    236 
    237         buffer.setCharAt(buffer.length() - 1, '}');
    238         return buffer.toString();
    239     }
    240 
    241 
    242     private static String tableSwitch(CodeIterator iter, int pos) {
    243         StringBuffer buffer = new StringBuffer("tableswitch {\n");
    244         int index = (pos & ~3) + 4;
    245         // default
    246         buffer.append("\t\tdefault: ").append(pos + iter.s32bitAt(index)).append("\n");
    247         int low = iter.s32bitAt(index += 4);
    248         int high = iter.s32bitAt(index += 4);
    249         int end = (high - low + 1) * 4 + (index += 4);
    250 
    251         // Offset table
    252         for (int key = low; index < end; index += 4, key++) {
    253             int target = iter.s32bitAt(index) + pos;
    254             buffer.append("\t\t").append(key).append(": ").append(target).append("\n");
    255         }
    256 
    257         buffer.setCharAt(buffer.length() - 1, '}');
    258         return buffer.toString();
    259     }
    260 
    261 
    262     private static String ldc(ConstPool pool, int index) {
    263         int tag = pool.getTag(index);
    264         switch (tag) {
    265             case ConstPool.CONST_String:
    266                 return "#" + index + " = \"" + pool.getStringInfo(index) + "\"";
    267             case ConstPool.CONST_Integer:
    268                 return "#" + index + " = int " + pool.getIntegerInfo(index);
    269             case ConstPool.CONST_Float:
    270                 return "#" + index + " = float " + pool.getFloatInfo(index);
    271             case ConstPool.CONST_Long:
    272                 return "#" + index + " = long " + pool.getLongInfo(index);
    273             case ConstPool.CONST_Double:
    274                 return "#" + index + " = int " + pool.getDoubleInfo(index);
    275             case ConstPool.CONST_Class:
    276                 return classInfo(pool, index);
    277             default:
    278                 throw new RuntimeException("bad LDC: " + tag);
    279         }
    280     }
    281 }
    282