1 /* 2 * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 package org.graalvm.compiler.bytecode; 24 25 import static org.graalvm.compiler.bytecode.Bytecodes.ALOAD; 26 import static org.graalvm.compiler.bytecode.Bytecodes.ANEWARRAY; 27 import static org.graalvm.compiler.bytecode.Bytecodes.ASTORE; 28 import static org.graalvm.compiler.bytecode.Bytecodes.BIPUSH; 29 import static org.graalvm.compiler.bytecode.Bytecodes.CHECKCAST; 30 import static org.graalvm.compiler.bytecode.Bytecodes.DLOAD; 31 import static org.graalvm.compiler.bytecode.Bytecodes.DSTORE; 32 import static org.graalvm.compiler.bytecode.Bytecodes.FLOAD; 33 import static org.graalvm.compiler.bytecode.Bytecodes.FSTORE; 34 import static org.graalvm.compiler.bytecode.Bytecodes.GETFIELD; 35 import static org.graalvm.compiler.bytecode.Bytecodes.GETSTATIC; 36 import static org.graalvm.compiler.bytecode.Bytecodes.GOTO; 37 import static org.graalvm.compiler.bytecode.Bytecodes.GOTO_W; 38 import static org.graalvm.compiler.bytecode.Bytecodes.IFEQ; 39 import static org.graalvm.compiler.bytecode.Bytecodes.IFGE; 40 import static org.graalvm.compiler.bytecode.Bytecodes.IFGT; 41 import static org.graalvm.compiler.bytecode.Bytecodes.IFLE; 42 import static org.graalvm.compiler.bytecode.Bytecodes.IFLT; 43 import static org.graalvm.compiler.bytecode.Bytecodes.IFNE; 44 import static org.graalvm.compiler.bytecode.Bytecodes.IFNONNULL; 45 import static org.graalvm.compiler.bytecode.Bytecodes.IFNULL; 46 import static org.graalvm.compiler.bytecode.Bytecodes.IF_ACMPEQ; 47 import static org.graalvm.compiler.bytecode.Bytecodes.IF_ACMPNE; 48 import static org.graalvm.compiler.bytecode.Bytecodes.IF_ICMPEQ; 49 import static org.graalvm.compiler.bytecode.Bytecodes.IF_ICMPGE; 50 import static org.graalvm.compiler.bytecode.Bytecodes.IF_ICMPGT; 51 import static org.graalvm.compiler.bytecode.Bytecodes.IF_ICMPLE; 52 import static org.graalvm.compiler.bytecode.Bytecodes.IF_ICMPLT; 53 import static org.graalvm.compiler.bytecode.Bytecodes.IF_ICMPNE; 54 import static org.graalvm.compiler.bytecode.Bytecodes.ILOAD; 55 import static org.graalvm.compiler.bytecode.Bytecodes.INSTANCEOF; 56 import static org.graalvm.compiler.bytecode.Bytecodes.INVOKEDYNAMIC; 57 import static org.graalvm.compiler.bytecode.Bytecodes.INVOKEINTERFACE; 58 import static org.graalvm.compiler.bytecode.Bytecodes.INVOKESPECIAL; 59 import static org.graalvm.compiler.bytecode.Bytecodes.INVOKESTATIC; 60 import static org.graalvm.compiler.bytecode.Bytecodes.INVOKEVIRTUAL; 61 import static org.graalvm.compiler.bytecode.Bytecodes.ISTORE; 62 import static org.graalvm.compiler.bytecode.Bytecodes.JSR; 63 import static org.graalvm.compiler.bytecode.Bytecodes.JSR_W; 64 import static org.graalvm.compiler.bytecode.Bytecodes.LDC; 65 import static org.graalvm.compiler.bytecode.Bytecodes.LDC2_W; 66 import static org.graalvm.compiler.bytecode.Bytecodes.LDC_W; 67 import static org.graalvm.compiler.bytecode.Bytecodes.LLOAD; 68 import static org.graalvm.compiler.bytecode.Bytecodes.LOOKUPSWITCH; 69 import static org.graalvm.compiler.bytecode.Bytecodes.LSTORE; 70 import static org.graalvm.compiler.bytecode.Bytecodes.MULTIANEWARRAY; 71 import static org.graalvm.compiler.bytecode.Bytecodes.NEW; 72 import static org.graalvm.compiler.bytecode.Bytecodes.NEWARRAY; 73 import static org.graalvm.compiler.bytecode.Bytecodes.PUTFIELD; 74 import static org.graalvm.compiler.bytecode.Bytecodes.PUTSTATIC; 75 import static org.graalvm.compiler.bytecode.Bytecodes.RET; 76 import static org.graalvm.compiler.bytecode.Bytecodes.SIPUSH; 77 import static org.graalvm.compiler.bytecode.Bytecodes.TABLESWITCH; 78 79 import jdk.vm.ci.meta.ConstantPool; 80 import jdk.vm.ci.meta.JavaConstant; 81 import jdk.vm.ci.meta.JavaField; 82 import jdk.vm.ci.meta.JavaMethod; 83 import jdk.vm.ci.meta.JavaType; 84 import jdk.vm.ci.meta.ResolvedJavaMethod; 85 86 /** 87 * Utility for producing a {@code javap}-like disassembly of bytecode. 88 */ 89 public class BytecodeDisassembler { 90 91 /** 92 * Specifies if the disassembly for a single instruction can span multiple lines. 93 */ 94 private final boolean multiline; 95 96 private final boolean newLine; 97 98 public BytecodeDisassembler(boolean multiline, boolean newLine) { 99 this.multiline = multiline; 100 this.newLine = newLine; 101 } 102 103 public BytecodeDisassembler(boolean multiline) { 104 this(multiline, true); 105 } 106 107 public BytecodeDisassembler() { 108 this(true, true); 109 } 110 111 public static String disassembleOne(ResolvedJavaMethod method, int bci) { 112 return new BytecodeDisassembler(false, false).disassemble(method, bci, bci); 113 } 114 115 /** 116 * Disassembles the bytecode of a given method in a {@code javap}-like format. 117 * 118 * @return {@code null} if {@code method} has no bytecode (e.g., it is native or abstract) 119 */ 120 public String disassemble(ResolvedJavaMethod method) { 121 return disassemble(method, 0, Integer.MAX_VALUE); 122 } 123 124 /** 125 * Disassembles the bytecode of a given method in a {@code javap}-like format. 126 * 127 * @return {@code null} if {@code method} has no bytecode (e.g., it is native or abstract) 128 */ 129 public String disassemble(ResolvedJavaMethod method, int startBci, int endBci) { 130 return disassemble(new ResolvedJavaMethodBytecode(method), startBci, endBci); 131 } 132 133 /** 134 * Disassembles {@code code} in a {@code javap}-like format. 135 */ 136 public String disassemble(Bytecode code) { 137 return disassemble(code, 0, Integer.MAX_VALUE); 138 } 139 140 /** 141 * Disassembles {@code code} in a {@code javap}-like format. 142 */ 143 public String disassemble(Bytecode code, int startBci, int endBci) { 144 if (code.getCode() == null) { 145 return null; 146 } 147 ResolvedJavaMethod method = code.getMethod(); 148 ConstantPool cp = code.getConstantPool(); 149 BytecodeStream stream = new BytecodeStream(code.getCode()); 150 StringBuilder buf = new StringBuilder(); 151 int opcode = stream.currentBC(); 152 try { 153 while (opcode != Bytecodes.END) { 154 int bci = stream.currentBCI(); 155 if (bci >= startBci && bci <= endBci) { 156 String mnemonic = Bytecodes.nameOf(opcode); 157 buf.append(String.format("%4d: %-14s", bci, mnemonic)); 158 if (stream.nextBCI() > bci + 1) { 159 decodeOperand(buf, stream, cp, method, bci, opcode); 160 } 161 if (newLine) { 162 buf.append(String.format("%n")); 163 } 164 } 165 stream.next(); 166 opcode = stream.currentBC(); 167 } 168 } catch (RuntimeException e) { 169 throw new RuntimeException(String.format("Error disassembling %s%nPartial disassembly:%n%s", method.format("%H.%n(%p)"), buf.toString()), e); 170 } 171 return buf.toString(); 172 } 173 174 private void decodeOperand(StringBuilder buf, BytecodeStream stream, ConstantPool cp, ResolvedJavaMethod method, int bci, int opcode) { 175 // @formatter:off 176 switch (opcode) { 177 case BIPUSH : buf.append(stream.readByte()); break; 178 case SIPUSH : buf.append(stream.readShort()); break; 179 case NEW : 180 case CHECKCAST : 181 case INSTANCEOF : 182 case ANEWARRAY : { 183 int cpi = stream.readCPI(); 184 JavaType type = cp.lookupType(cpi, opcode); 185 buf.append(String.format("#%-10d // %s", cpi, type.toJavaName())); 186 break; 187 } 188 case GETSTATIC : 189 case PUTSTATIC : 190 case GETFIELD : 191 case PUTFIELD : { 192 int cpi = stream.readCPI(); 193 JavaField field = cp.lookupField(cpi, method, opcode); 194 String fieldDesc = field.getDeclaringClass().getName().equals(method.getDeclaringClass().getName()) ? field.format("%n:%T") : field.format("%H.%n:%T"); 195 buf.append(String.format("#%-10d // %s", cpi, fieldDesc)); 196 break; 197 } 198 case INVOKEVIRTUAL : 199 case INVOKESPECIAL : 200 case INVOKESTATIC : { 201 int cpi = stream.readCPI(); 202 JavaMethod callee = cp.lookupMethod(cpi, opcode); 203 String calleeDesc = callee.getDeclaringClass().getName().equals(method.getDeclaringClass().getName()) ? callee.format("%n:(%P)%R") : callee.format("%H.%n:(%P)%R"); 204 buf.append(String.format("#%-10d // %s", cpi, calleeDesc)); 205 break; 206 } 207 case INVOKEINTERFACE: { 208 int cpi = stream.readCPI(); 209 JavaMethod callee = cp.lookupMethod(cpi, opcode); 210 String calleeDesc = callee.getDeclaringClass().getName().equals(method.getDeclaringClass().getName()) ? callee.format("%n:(%P)%R") : callee.format("%H.%n:(%P)%R"); 211 buf.append(String.format("#%-10s // %s", cpi + ", " + stream.readUByte(bci + 3), calleeDesc)); 212 break; 213 } 214 case INVOKEDYNAMIC: { 215 int cpi = stream.readCPI4(); 216 JavaMethod callee = cp.lookupMethod(cpi, opcode); 217 String calleeDesc = callee.getDeclaringClass().getName().equals(method.getDeclaringClass().getName()) ? callee.format("%n:(%P)%R") : callee.format("%H.%n:(%P)%R"); 218 buf.append(String.format("#%-10d // %s", cpi, calleeDesc)); 219 break; 220 } 221 case LDC : 222 case LDC_W : 223 case LDC2_W : { 224 int cpi = stream.readCPI(); 225 Object constant = cp.lookupConstant(cpi); 226 String desc = null; 227 if (constant instanceof JavaConstant) { 228 JavaConstant c = ((JavaConstant) constant); 229 desc = c.toValueString(); 230 } else { 231 desc = constant.toString(); 232 } 233 if (!multiline) { 234 desc = desc.replaceAll("\\n", ""); 235 } 236 buf.append(String.format("#%-10d // %s", cpi, desc)); 237 break; 238 } 239 case RET : 240 case ILOAD : 241 case LLOAD : 242 case FLOAD : 243 case DLOAD : 244 case ALOAD : 245 case ISTORE : 246 case LSTORE : 247 case FSTORE : 248 case DSTORE : 249 case ASTORE : { 250 buf.append(String.format("%d", stream.readLocalIndex())); 251 break; 252 } 253 case IFEQ : 254 case IFNE : 255 case IFLT : 256 case IFGE : 257 case IFGT : 258 case IFLE : 259 case IF_ICMPEQ : 260 case IF_ICMPNE : 261 case IF_ICMPLT : 262 case IF_ICMPGE : 263 case IF_ICMPGT : 264 case IF_ICMPLE : 265 case IF_ACMPEQ : 266 case IF_ACMPNE : 267 case GOTO : 268 case JSR : 269 case IFNULL : 270 case IFNONNULL : 271 case GOTO_W : 272 case JSR_W : { 273 buf.append(String.format("%d", stream.readBranchDest())); 274 break; 275 } 276 case LOOKUPSWITCH : 277 case TABLESWITCH : { 278 BytecodeSwitch bswitch = opcode == LOOKUPSWITCH ? new BytecodeLookupSwitch(stream, bci) : new BytecodeTableSwitch(stream, bci); 279 if (multiline) { 280 buf.append("{ // " + bswitch.numberOfCases()); 281 for (int i = 0; i < bswitch.numberOfCases(); i++) { 282 buf.append(String.format("%n %7d: %d", bswitch.keyAt(i), bswitch.targetAt(i))); 283 } 284 buf.append(String.format("%n default: %d", bswitch.defaultTarget())); 285 buf.append(String.format("%n }")); 286 } else { 287 buf.append("[" + bswitch.numberOfCases()).append("] {"); 288 for (int i = 0; i < bswitch.numberOfCases(); i++) { 289 buf.append(String.format("%d: %d", bswitch.keyAt(i), bswitch.targetAt(i))); 290 if (i != bswitch.numberOfCases() - 1) { 291 buf.append(", "); 292 } 293 } 294 buf.append(String.format("} default: %d", bswitch.defaultTarget())); 295 } 296 break; 297 } 298 case NEWARRAY : { 299 int typecode = stream.readLocalIndex(); 300 // Checkstyle: stop 301 switch (typecode) { 302 case 4: buf.append("boolean"); break; 303 case 5: buf.append("char"); break; 304 case 6: buf.append("float"); break; 305 case 7: buf.append("double"); break; 306 case 8: buf.append("byte"); break; 307 case 9: buf.append("short"); break; 308 case 10: buf.append("int"); break; 309 case 11: buf.append("long"); break; 310 } 311 // Checkstyle: resume 312 313 break; 314 } 315 case MULTIANEWARRAY : { 316 int cpi = stream.readCPI(); 317 JavaType type = cp.lookupType(cpi, opcode); 318 buf.append(String.format("#%-10s // %s", cpi + ", " + stream.readUByte(bci + 3), type.toJavaName())); 319 break; 320 } 321 } 322 // @formatter:on 323 } 324 }