001/* 002 * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. 008 * 009 * This code is distributed in the hope that it will be useful, but WITHOUT 010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 011 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 012 * version 2 for more details (a copy is included in the LICENSE file that 013 * accompanied this code). 014 * 015 * You should have received a copy of the GNU General Public License version 016 * 2 along with this work; if not, write to the Free Software Foundation, 017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 018 * 019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 020 * or visit www.oracle.com if you need additional information or have any 021 * questions. 022 */ 023package com.oracle.graal.java; 024 025import static com.oracle.graal.bytecode.Bytecodes.*; 026import jdk.internal.jvmci.meta.*; 027 028import com.oracle.graal.bytecode.*; 029 030/** 031 * Utility for producing a {@code javap}-like disassembly of bytecode. 032 */ 033public class BytecodeDisassembler { 034 035 /** 036 * Specifies if the disassembly for a single instruction can span multiple lines. 037 */ 038 private final boolean multiline; 039 040 public BytecodeDisassembler(boolean multiline) { 041 this.multiline = multiline; 042 } 043 044 public BytecodeDisassembler() { 045 this(true); 046 } 047 048 /** 049 * Disassembles the bytecode of a given method in a {@code javap}-like format. 050 * 051 * @return {@code null} if {@code method} has no bytecode (e.g., it is native or abstract) 052 */ 053 public String disassemble(ResolvedJavaMethod method) { 054 return disassemble(method, 0, Integer.MAX_VALUE); 055 } 056 057 /** 058 * Disassembles the bytecode of a given method in a {@code javap}-like format. 059 * 060 * @return {@code null} if {@code method} has no bytecode (e.g., it is native or abstract) 061 */ 062 public String disassemble(ResolvedJavaMethod method, int startBci, int endBci) { 063 if (method.getCode() == null) { 064 return null; 065 } 066 ConstantPool cp = method.getConstantPool(); 067 BytecodeStream stream = new BytecodeStream(method.getCode()); 068 StringBuilder buf = new StringBuilder(); 069 int opcode = stream.currentBC(); 070 while (opcode != Bytecodes.END) { 071 int bci = stream.currentBCI(); 072 if (bci >= startBci && bci <= endBci) { 073 String mnemonic = Bytecodes.nameOf(opcode); 074 buf.append(String.format("%4d: %-14s", bci, mnemonic)); 075 if (stream.nextBCI() > bci + 1) { 076 // @formatter:off 077 switch (opcode) { 078 case BIPUSH : buf.append(stream.readByte()); break; 079 case SIPUSH : buf.append(stream.readShort()); break; 080 case NEW : 081 case CHECKCAST : 082 case INSTANCEOF : 083 case ANEWARRAY : { 084 int cpi = stream.readCPI(); 085 JavaType type = cp.lookupType(cpi, opcode); 086 buf.append(String.format("#%-10d // %s", cpi, type.toJavaName())); 087 break; 088 } 089 case GETSTATIC : 090 case PUTSTATIC : 091 case GETFIELD : 092 case PUTFIELD : { 093 int cpi = stream.readCPI(); 094 JavaField field = cp.lookupField(cpi, opcode); 095 String fieldDesc = field.getDeclaringClass().getName().equals(method.getDeclaringClass().getName()) ? field.format("%n:%T") : field.format("%H.%n:%T"); 096 buf.append(String.format("#%-10d // %s", cpi, fieldDesc)); 097 break; 098 } 099 case INVOKEVIRTUAL : 100 case INVOKESPECIAL : 101 case INVOKESTATIC : { 102 int cpi = stream.readCPI(); 103 JavaMethod callee = cp.lookupMethod(cpi, opcode); 104 String calleeDesc = callee.getDeclaringClass().getName().equals(method.getDeclaringClass().getName()) ? callee.format("%n:(%P)%R") : callee.format("%H.%n:(%P)%R"); 105 buf.append(String.format("#%-10d // %s", cpi, calleeDesc)); 106 break; 107 } 108 case INVOKEINTERFACE: { 109 int cpi = stream.readCPI(); 110 JavaMethod callee = cp.lookupMethod(cpi, opcode); 111 String calleeDesc = callee.getDeclaringClass().getName().equals(method.getDeclaringClass().getName()) ? callee.format("%n:(%P)%R") : callee.format("%H.%n:(%P)%R"); 112 buf.append(String.format("#%-10s // %s", cpi + ", " + stream.readUByte(bci + 3), calleeDesc)); 113 break; 114 } 115 case INVOKEDYNAMIC: { 116 int cpi = stream.readCPI4(); 117 JavaMethod callee = cp.lookupMethod(cpi, opcode); 118 String calleeDesc = callee.getDeclaringClass().getName().equals(method.getDeclaringClass().getName()) ? callee.format("%n:(%P)%R") : callee.format("%H.%n:(%P)%R"); 119 buf.append(String.format("#%-10d // %s", cpi, calleeDesc)); 120 break; 121 } 122 case LDC : 123 case LDC_W : 124 case LDC2_W : { 125 int cpi = stream.readCPI(); 126 Object constant = cp.lookupConstant(cpi); 127 String desc = null; 128 if (constant instanceof JavaConstant) { 129 JavaConstant c = ((JavaConstant) constant); 130 desc = c.toValueString(); 131 } else { 132 desc = constant.toString(); 133 } 134 if (!multiline) { 135 desc = desc.replaceAll("\\n", ""); 136 } 137 buf.append(String.format("#%-10d // %s", cpi, desc)); 138 break; 139 } 140 case RET : 141 case ILOAD : 142 case LLOAD : 143 case FLOAD : 144 case DLOAD : 145 case ALOAD : 146 case ISTORE : 147 case LSTORE : 148 case FSTORE : 149 case DSTORE : 150 case ASTORE : { 151 buf.append(String.format("%d", stream.readLocalIndex())); 152 break; 153 } 154 case IFEQ : 155 case IFNE : 156 case IFLT : 157 case IFGE : 158 case IFGT : 159 case IFLE : 160 case IF_ICMPEQ : 161 case IF_ICMPNE : 162 case IF_ICMPLT : 163 case IF_ICMPGE : 164 case IF_ICMPGT : 165 case IF_ICMPLE : 166 case IF_ACMPEQ : 167 case IF_ACMPNE : 168 case GOTO : 169 case JSR : 170 case IFNULL : 171 case IFNONNULL : 172 case GOTO_W : 173 case JSR_W : { 174 buf.append(String.format("%d", stream.readBranchDest())); 175 break; 176 } 177 case LOOKUPSWITCH : 178 case TABLESWITCH : { 179 BytecodeSwitch bswitch = opcode == LOOKUPSWITCH ? new BytecodeLookupSwitch(stream, bci) : new BytecodeTableSwitch(stream, bci); 180 if (multiline) { 181 buf.append("{ // " + bswitch.numberOfCases()); 182 for (int i = 0; i < bswitch.numberOfCases(); i++) { 183 buf.append(String.format("%n %7d: %d", bswitch.keyAt(i), bswitch.targetAt(i))); 184 } 185 buf.append(String.format("%n default: %d", bswitch.defaultTarget())); 186 buf.append(String.format("%n }")); 187 } else { 188 buf.append("[" + bswitch.numberOfCases()).append("] {"); 189 for (int i = 0; i < bswitch.numberOfCases(); i++) { 190 buf.append(String.format("%d: %d", bswitch.keyAt(i), bswitch.targetAt(i))); 191 if (i != bswitch.numberOfCases() - 1) { 192 buf.append(", "); 193 } 194 } 195 buf.append(String.format("} default: %d", bswitch.defaultTarget())); 196 } 197 break; 198 } 199 case NEWARRAY : { 200 int code = stream.readLocalIndex(); 201 // Checkstyle: stop 202 switch (code) { 203 case 4: buf.append("boolean"); break; 204 case 5: buf.append("char"); break; 205 case 6: buf.append("float"); break; 206 case 7: buf.append("double"); break; 207 case 8: buf.append("byte"); break; 208 case 9: buf.append("short"); break; 209 case 10: buf.append("int"); break; 210 case 11: buf.append("long"); break; 211 } 212 // Checkstyle: resume 213 214 break; 215 } 216 case MULTIANEWARRAY : { 217 int cpi = stream.readCPI(); 218 JavaType type = cp.lookupType(cpi, opcode); 219 buf.append(String.format("#%-10s // %s", cpi + ", " + stream.readUByte(bci + 3), type.toJavaName())); 220 break; 221 } 222 } 223 // @formatter:on 224 } 225 buf.append(String.format("%n")); 226 } 227 stream.next(); 228 opcode = stream.currentBC(); 229 } 230 return buf.toString(); 231 } 232}