4 */ 5 /* 6 * Licensed to the Apache Software Foundation (ASF) under one or more 7 * contributor license agreements. See the NOTICE file distributed with 8 * this work for additional information regarding copyright ownership. 9 * The ASF licenses this file to You under the Apache License, Version 2.0 10 * (the "License"); you may not use this file except in compliance with 11 * the License. You may obtain a copy of the License at 12 * 13 * http://www.apache.org/licenses/LICENSE-2.0 14 * 15 * Unless required by applicable law or agreed to in writing, software 16 * distributed under the License is distributed on an "AS IS" BASIS, 17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 * See the License for the specific language governing permissions and 19 * limitations under the License. 20 */ 21 22 package com.sun.org.apache.bcel.internal.util; 23 24 25 import com.sun.org.apache.bcel.internal.classfile.*; 26 import java.io.*; 27 import java.util.BitSet; 28 29 /** 30 * Convert code into HTML file. 31 * 32 * @author <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A> 33 * 34 */ 35 final class CodeHTML implements com.sun.org.apache.bcel.internal.Constants { 36 private String class_name; // name of current class 37 private Method[] methods; // Methods to print 38 private PrintWriter file; // file to write to 39 private BitSet goto_set; 40 private ConstantPool constant_pool; 41 private ConstantHTML constant_html; 42 private static boolean wide=false; 43 44 CodeHTML(String dir, String class_name, 45 Method[] methods, ConstantPool constant_pool, 46 ConstantHTML constant_html) throws IOException 47 { 48 this.class_name = class_name; 49 this.methods = methods; 50 this.constant_pool = constant_pool; 51 this.constant_html = constant_html; 52 53 file = new PrintWriter(new FileOutputStream(dir + class_name + "_code.html")); 54 file.println("<HTML><BODY BGCOLOR=\"#C0C0C0\">"); 55 56 for(int i=0; i < methods.length; i++) 57 writeMethod(methods[i], i); 58 59 file.println("</BODY></HTML>"); 60 file.close(); 61 } 62 63 /** 64 * Disassemble a stream of byte codes and return the 65 * string representation. 66 * 67 * @param stream data input stream 68 * @return String representation of byte code 69 */ 70 private final String codeToHTML(ByteSequence bytes, int method_number) 71 throws IOException 72 { 73 short opcode = (short)bytes.readUnsignedByte(); 74 StringBuffer buf; 75 String name, signature; 76 int default_offset=0, low, high; 77 int index, class_index, vindex, constant; 78 int[] jump_table; 79 int no_pad_bytes=0, offset; 80 81 buf = new StringBuffer("<TT>" + OPCODE_NAMES[opcode] + "</TT></TD><TD>"); 82 83 /* Special case: Skip (0-3) padding bytes, i.e., the 84 * following bytes are 4-byte-aligned 85 */ 86 if((opcode == TABLESWITCH) || (opcode == LOOKUPSWITCH)) { 87 int remainder = bytes.getIndex() % 4; 88 no_pad_bytes = (remainder == 0)? 0 : 4 - remainder; 89 90 for(int i=0; i < no_pad_bytes; i++) 91 bytes.readByte(); 92 93 // Both cases have a field default_offset in common 94 default_offset = bytes.readInt(); 95 } 96 97 switch(opcode) { 98 case TABLESWITCH: 99 low = bytes.readInt(); 100 high = bytes.readInt(); 101 102 offset = bytes.getIndex() - 12 - no_pad_bytes - 1; 103 default_offset += offset; 104 105 buf.append("<TABLE BORDER=1><TR>"); 106 107 // Print switch indices in first row (and default) 108 jump_table = new int[high - low + 1]; 109 for(int i=0; i < jump_table.length; i++) { 110 jump_table[i] = offset + bytes.readInt(); 111 112 buf.append("<TH>" + (low + i) + "</TH>"); 113 } 114 buf.append("<TH>default</TH></TR>\n<TR>"); 115 116 // Print target and default indices in second row 117 for(int i=0; i < jump_table.length; i++) 118 buf.append("<TD><A HREF=\"#code" + method_number + "@" + 119 jump_table[i] + "\">" + jump_table[i] + "</A></TD>"); 120 buf.append("<TD><A HREF=\"#code" + method_number + "@" + 121 default_offset + "\">" + default_offset + "</A></TD></TR>\n</TABLE>\n"); 122 123 break; 124 125 /* Lookup switch has variable length arguments. 126 */ 127 case LOOKUPSWITCH: 128 int npairs = bytes.readInt(); 129 offset = bytes.getIndex() - 8 - no_pad_bytes - 1; 130 jump_table = new int[npairs]; 131 default_offset += offset; 132 133 buf.append("<TABLE BORDER=1><TR>"); 134 135 // Print switch indices in first row (and default) 136 for(int i=0; i < npairs; i++) { 137 int match = bytes.readInt(); 138 139 jump_table[i] = offset + bytes.readInt(); 140 buf.append("<TH>" + match + "</TH>"); 141 } 142 buf.append("<TH>default</TH></TR>\n<TR>"); 143 144 // Print target and default indices in second row 145 for(int i=0; i < npairs; i++) 146 buf.append("<TD><A HREF=\"#code" + method_number + "@" + 147 jump_table[i] + "\">" + jump_table[i] + "</A></TD>"); 148 buf.append("<TD><A HREF=\"#code" + method_number + "@" + 149 default_offset + "\">" + default_offset + "</A></TD></TR>\n</TABLE>\n"); 150 break; 151 152 /* Two address bytes + offset from start of byte stream form the 153 * jump target. 154 */ 155 case GOTO: case IFEQ: case IFGE: case IFGT: 156 case IFLE: case IFLT: 157 case IFNE: case IFNONNULL: case IFNULL: case IF_ACMPEQ: 158 case IF_ACMPNE: case IF_ICMPEQ: case IF_ICMPGE: case IF_ICMPGT: 159 case IF_ICMPLE: case IF_ICMPLT: case IF_ICMPNE: case JSR: 160 161 index = (int)(bytes.getIndex() + bytes.readShort() - 1); 162 163 buf.append("<A HREF=\"#code" + method_number + "@" + index + "\">" + index + "</A>"); 164 break; 165 166 /* Same for 32-bit wide jumps 167 */ 168 case GOTO_W: case JSR_W: 169 int windex = bytes.getIndex() + bytes.readInt() - 1; 170 buf.append("<A HREF=\"#code" + method_number + "@" + windex + "\">" + 171 windex + "</A>"); 172 break; 173 174 /* Index byte references local variable (register) 175 */ 176 case ALOAD: case ASTORE: case DLOAD: case DSTORE: case FLOAD: 177 case FSTORE: case ILOAD: case ISTORE: case LLOAD: case LSTORE: 178 case RET: 179 if(wide) { 180 vindex = bytes.readShort(); 181 wide=false; // Clear flag 182 } 183 else 184 vindex = bytes.readUnsignedByte(); 185 186 buf.append("%" + vindex); 187 break; 188 189 /* 190 * Remember wide byte which is used to form a 16-bit address in the 191 * following instruction. Relies on that the method is called again with 192 * the following opcode. 193 */ 194 case WIDE: 195 wide = true; 196 buf.append("(wide)"); 197 break; 198 199 /* Array of basic type. 200 */ 201 case NEWARRAY: 202 buf.append("<FONT COLOR=\"#00FF00\">" + TYPE_NAMES[bytes.readByte()] + "</FONT>"); 203 break; 204 205 /* Access object/class fields. 206 */ 207 case GETFIELD: case GETSTATIC: case PUTFIELD: case PUTSTATIC: 208 index = bytes.readShort(); 209 ConstantFieldref c1 = (ConstantFieldref)constant_pool.getConstant(index, CONSTANT_Fieldref); 210 211 class_index = c1.getClassIndex(); 212 name = constant_pool.getConstantString(class_index, CONSTANT_Class); 213 name = Utility.compactClassName(name, false); 214 215 index = c1.getNameAndTypeIndex(); 216 String field_name = constant_pool.constantToString(index, CONSTANT_NameAndType); 217 218 if(name.equals(class_name)) { // Local field 219 buf.append("<A HREF=\"" + class_name + "_methods.html#field" + field_name + 220 "\" TARGET=Methods>" + field_name + "</A>\n"); 221 } 222 else 223 buf.append(constant_html.referenceConstant(class_index) + "." + field_name); 224 225 break; 226 227 /* Operands are references to classes in constant pool 228 */ 229 case CHECKCAST: case INSTANCEOF: case NEW: 230 index = bytes.readShort(); 231 buf.append(constant_html.referenceConstant(index)); 232 break; 233 234 /* Operands are references to methods in constant pool 235 */ 236 case INVOKESPECIAL: case INVOKESTATIC: case INVOKEVIRTUAL: case INVOKEINTERFACE: 237 int m_index = bytes.readShort(); 238 String str; 239 240 if(opcode == INVOKEINTERFACE) { // Special treatment needed 241 int nargs = bytes.readUnsignedByte(); // Redundant 242 int reserved = bytes.readUnsignedByte(); // Reserved 243 244 ConstantInterfaceMethodref c=(ConstantInterfaceMethodref)constant_pool.getConstant(m_index, CONSTANT_InterfaceMethodref); 245 246 class_index = c.getClassIndex(); 247 str = constant_pool.constantToString(c); 248 index = c.getNameAndTypeIndex(); 249 } 250 else { 251 ConstantMethodref c = (ConstantMethodref)constant_pool.getConstant(m_index, CONSTANT_Methodref); 252 class_index = c.getClassIndex(); 253 254 str = constant_pool.constantToString(c); 255 index = c.getNameAndTypeIndex(); 256 } 257 258 name = Class2HTML.referenceClass(class_index); 259 str = Class2HTML.toHTML(constant_pool.constantToString(constant_pool.getConstant(index, CONSTANT_NameAndType))); 260 261 // Get signature, i.e., types 262 ConstantNameAndType c2 = (ConstantNameAndType)constant_pool. 263 getConstant(index, CONSTANT_NameAndType); 264 signature = constant_pool.constantToString(c2.getSignatureIndex(), 265 CONSTANT_Utf8); 266 String[] args = Utility.methodSignatureArgumentTypes(signature, false); 267 String type = Utility.methodSignatureReturnType(signature, false); 268 269 buf.append(name + ".<A HREF=\"" + class_name + "_cp.html#cp" + m_index + 270 "\" TARGET=ConstantPool>" + str + "</A>" + "("); 271 272 // List arguments 273 for(int i=0; i < args.length; i++) { 274 buf.append(Class2HTML.referenceType(args[i])); 275 276 if(i < args.length - 1) 277 buf.append(", "); 278 } 279 // Attach return type 280 buf.append("):" + Class2HTML.referenceType(type)); 281 282 break; 283 284 /* Operands are references to items in constant pool 285 */ 286 case LDC_W: case LDC2_W: 287 index = bytes.readShort(); 288 289 buf.append("<A HREF=\"" + class_name + "_cp.html#cp" + index + 290 "\" TARGET=\"ConstantPool\">" + 291 Class2HTML.toHTML(constant_pool.constantToString(index, 292 constant_pool. 293 getConstant(index).getTag()))+ 294 "</a>"); 295 break; 296 297 case LDC: 298 index = bytes.readUnsignedByte(); 299 buf.append("<A HREF=\"" + class_name + "_cp.html#cp" + index + 300 "\" TARGET=\"ConstantPool\">" + 301 Class2HTML.toHTML(constant_pool.constantToString(index, 302 constant_pool. 303 getConstant(index).getTag()))+ 304 "</a>"); 305 break; 306 307 /* Array of references. 308 */ 309 case ANEWARRAY: 310 index = bytes.readShort(); 311 312 buf.append(constant_html.referenceConstant(index)); 313 break; 314 315 /* Multidimensional array of references. 316 */ 317 case MULTIANEWARRAY: 318 index = bytes.readShort(); 319 int dimensions = bytes.readByte(); 320 buf.append(constant_html.referenceConstant(index) + ":" + dimensions + "-dimensional"); 321 break; 322 323 /* Increment local variable. 324 */ 325 case IINC: 326 if(wide) { 327 vindex = bytes.readShort(); 328 constant = bytes.readShort(); 329 wide = false; 330 } 331 else { 332 vindex = bytes.readUnsignedByte(); 333 constant = bytes.readByte(); 334 } 335 buf.append("%" + vindex + " " + constant); 336 break; 337 338 default: 339 if(NO_OF_OPERANDS[opcode] > 0) { 340 for(int i=0; i < TYPE_OF_OPERANDS[opcode].length; i++) { 341 switch(TYPE_OF_OPERANDS[opcode][i]) { 342 case T_BYTE: 343 buf.append(bytes.readUnsignedByte()); 344 break; 345 346 case T_SHORT: // Either branch or index 347 buf.append(bytes.readShort()); 348 break; 349 350 case T_INT: 351 buf.append(bytes.readInt()); 352 break; 353 354 default: // Never reached 355 System.err.println("Unreachable default case reached!"); 356 System.exit(-1); 357 } 358 buf.append(" "); 359 } 360 } 361 } 362 363 buf.append("</TD>"); 364 return buf.toString(); 365 } 366 367 /** 368 * Find all target addresses in code, so that they can be marked 369 * with <A NAME = ...>. Target addresses are kept in an BitSet object. 370 */ 371 private final void findGotos(ByteSequence bytes, Method method, Code code) 372 throws IOException 373 { 374 int index; 375 goto_set = new BitSet(bytes.available()); 376 int opcode; 377 378 /* First get Code attribute from method and the exceptions handled 379 * (try .. catch) in this method. We only need the line number here. 380 */ 381 382 if(code != null) { 383 CodeException[] ce = code.getExceptionTable(); 384 int len = ce.length; 385 386 for(int i=0; i < len; i++) { 387 goto_set.set(ce[i].getStartPC()); 388 goto_set.set(ce[i].getEndPC()); 389 goto_set.set(ce[i].getHandlerPC()); 390 } 391 392 // Look for local variables and their range 393 Attribute[] attributes = code.getAttributes(); 394 for(int i=0; i < attributes.length; i++) { 395 if(attributes[i].getTag() == ATTR_LOCAL_VARIABLE_TABLE) { 396 LocalVariable[] vars = ((LocalVariableTable)attributes[i]).getLocalVariableTable(); 397 398 for(int j=0; j < vars.length; j++) { 399 int start = vars[j].getStartPC(); 400 int end = (int)(start + vars[j].getLength()); 401 goto_set.set(start); 402 goto_set.set(end); 403 } 404 break; 405 } 406 } 407 } 408 409 // Get target addresses from GOTO, JSR, TABLESWITCH, etc. 410 for(int i=0; bytes.available() > 0; i++) { 411 opcode = bytes.readUnsignedByte(); 412 //System.out.println(OPCODE_NAMES[opcode]); 413 switch(opcode) { 414 case TABLESWITCH: case LOOKUPSWITCH: 415 //bytes.readByte(); // Skip already read byte 416 417 int remainder = bytes.getIndex() % 4; 418 int no_pad_bytes = (remainder == 0)? 0 : 4 - remainder; 419 int default_offset, offset; 420 421 for(int j=0; j < no_pad_bytes; j++) 422 bytes.readByte(); 423 424 // Both cases have a field default_offset in common 425 default_offset = bytes.readInt(); 426 427 if(opcode == TABLESWITCH) { 428 int low = bytes.readInt(); 429 int high = bytes.readInt(); 430 431 offset = bytes.getIndex() - 12 - no_pad_bytes - 1; 432 default_offset += offset; 433 goto_set.set(default_offset); 434 435 for(int j=0; j < (high - low + 1); j++) { 436 index = offset + bytes.readInt(); 437 goto_set.set(index); 438 } 439 } 440 else { // LOOKUPSWITCH 441 int npairs = bytes.readInt(); 442 443 offset = bytes.getIndex() - 8 - no_pad_bytes - 1; 444 default_offset += offset; 445 goto_set.set(default_offset); 446 447 for(int j=0; j < npairs; j++) { 448 int match = bytes.readInt(); 449 450 index = offset + bytes.readInt(); 451 goto_set.set(index); 452 } 453 } 454 break; 455 456 case GOTO: case IFEQ: case IFGE: case IFGT: 457 case IFLE: case IFLT: 458 case IFNE: case IFNONNULL: case IFNULL: case IF_ACMPEQ: 459 case IF_ACMPNE: case IF_ICMPEQ: case IF_ICMPGE: case IF_ICMPGT: 460 case IF_ICMPLE: case IF_ICMPLT: case IF_ICMPNE: case JSR: 461 //bytes.readByte(); // Skip already read byte 462 index = bytes.getIndex() + bytes.readShort() - 1; 463 464 goto_set.set(index); 465 break; 466 467 case GOTO_W: case JSR_W: 468 //bytes.readByte(); // Skip already read byte 469 index = bytes.getIndex() + bytes.readInt() - 1; 470 goto_set.set(index); 471 break; 472 473 default: 474 bytes.unreadByte(); 475 codeToHTML(bytes, 0); // Ignore output 476 } 477 } 478 } 479 480 /** 481 * Write a single method with the byte code associated with it. 482 */ 483 private void writeMethod(Method method, int method_number) 484 throws IOException 485 { 486 // Get raw signature 487 String signature = method.getSignature(); 488 // Get array of strings containing the argument types 489 String[] args = Utility.methodSignatureArgumentTypes(signature, false); 490 // Get return type string 491 String type = Utility.methodSignatureReturnType(signature, false); 492 // Get method name 493 String name = method.getName(); 494 String html_name = Class2HTML.toHTML(name); 495 // Get method's access flags 496 String access = Utility.accessToString(method.getAccessFlags()); 497 access = Utility.replace(access, " ", " "); 498 // Get the method's attributes, the Code Attribute in particular 499 Attribute[] attributes= method.getAttributes(); 500 501 file.print("<P><B><FONT COLOR=\"#FF0000\">" + access + "</FONT> " + 502 "<A NAME=method" + method_number + ">" + Class2HTML.referenceType(type) + 503 "</A> <A HREF=\"" + class_name + "_methods.html#method" + method_number + 504 "\" TARGET=Methods>" + html_name + "</A>("); 505 506 for(int i=0; i < args.length; i++) { 507 file.print(Class2HTML.referenceType(args[i])); 508 if(i < args.length - 1) 509 file.print(", "); 510 } 511 512 file.println(")</B></P>"); 513 514 Code c=null; 515 byte[] code=null; 516 517 if(attributes.length > 0) { 518 file.print("<H4>Attributes</H4><UL>\n"); 519 for(int i=0; i < attributes.length; i++) { 520 byte tag = attributes[i].getTag(); 521 522 if(tag != ATTR_UNKNOWN) 523 file.print("<LI><A HREF=\"" + class_name + "_attributes.html#method" + method_number + "@" + i + 524 "\" TARGET=Attributes>" + ATTRIBUTE_NAMES[tag] + "</A></LI>\n"); 525 else 526 file.print("<LI>" + attributes[i] + "</LI>"); 527 528 if(tag == ATTR_CODE) { 529 c = (Code)attributes[i]; 530 Attribute[] attributes2 = c.getAttributes(); 531 code = c.getCode(); 532 533 file.print("<UL>"); 534 for(int j=0; j < attributes2.length; j++) { 535 tag = attributes2[j].getTag(); 536 file.print("<LI><A HREF=\"" + class_name + "_attributes.html#" + 537 "method" + method_number + "@" + i + "@" + j + "\" TARGET=Attributes>" + 538 ATTRIBUTE_NAMES[tag] + "</A></LI>\n"); 539 540 } 541 file.print("</UL>"); 542 } 543 } 544 file.println("</UL>"); 545 } 546 547 if(code != null) { // No code, an abstract method, e.g. 548 //System.out.println(name + "\n" + Utility.codeToString(code, constant_pool, 0, -1)); 549 550 // Print the byte code 551 ByteSequence stream = new ByteSequence(code); 552 stream.mark(stream.available()); 553 findGotos(stream, method, c); 554 stream.reset(); 555 556 file.println("<TABLE BORDER=0><TR><TH ALIGN=LEFT>Byte<BR>offset</TH>" + 557 "<TH ALIGN=LEFT>Instruction</TH><TH ALIGN=LEFT>Argument</TH>"); 558 559 for(int i=0; stream.available() > 0; i++) { 560 int offset = stream.getIndex(); 561 String str = codeToHTML(stream, method_number); 562 String anchor = ""; 563 564 /* Set an anchor mark if this line is targetted by a goto, jsr, etc. 565 * Defining an anchor for every line is very inefficient! 566 */ 567 if(goto_set.get(offset)) 568 anchor = "<A NAME=code" + method_number + "@" + offset + "></A>"; 569 570 String anchor2; 571 if(stream.getIndex() == code.length) // last loop 572 anchor2 = "<A NAME=code" + method_number + "@" + code.length + ">" + offset + "</A>"; 573 else 574 anchor2 = "" + offset; 575 576 file.println("<TR VALIGN=TOP><TD>" + anchor2 + "</TD><TD>" + anchor + str + "</TR>"); 577 } 578 579 // Mark last line, may be targetted from Attributes window 580 file.println("<TR><TD> </A></TD></TR>"); 581 file.println("</TABLE>"); 582 } 583 584 } 585 } | 4 */ 5 /* 6 * Licensed to the Apache Software Foundation (ASF) under one or more 7 * contributor license agreements. See the NOTICE file distributed with 8 * this work for additional information regarding copyright ownership. 9 * The ASF licenses this file to You under the Apache License, Version 2.0 10 * (the "License"); you may not use this file except in compliance with 11 * the License. You may obtain a copy of the License at 12 * 13 * http://www.apache.org/licenses/LICENSE-2.0 14 * 15 * Unless required by applicable law or agreed to in writing, software 16 * distributed under the License is distributed on an "AS IS" BASIS, 17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 * See the License for the specific language governing permissions and 19 * limitations under the License. 20 */ 21 22 package com.sun.org.apache.bcel.internal.util; 23 24 import java.io.FileOutputStream; 25 import java.io.IOException; 26 import java.io.PrintWriter; 27 import java.util.BitSet; 28 29 import com.sun.org.apache.bcel.internal.Const; 30 import com.sun.org.apache.bcel.internal.classfile.Attribute; 31 import com.sun.org.apache.bcel.internal.classfile.Code; 32 import com.sun.org.apache.bcel.internal.classfile.CodeException; 33 import com.sun.org.apache.bcel.internal.classfile.ConstantFieldref; 34 import com.sun.org.apache.bcel.internal.classfile.ConstantInterfaceMethodref; 35 import com.sun.org.apache.bcel.internal.classfile.ConstantInvokeDynamic; 36 import com.sun.org.apache.bcel.internal.classfile.ConstantMethodref; 37 import com.sun.org.apache.bcel.internal.classfile.ConstantNameAndType; 38 import com.sun.org.apache.bcel.internal.classfile.ConstantPool; 39 import com.sun.org.apache.bcel.internal.classfile.LocalVariable; 40 import com.sun.org.apache.bcel.internal.classfile.LocalVariableTable; 41 import com.sun.org.apache.bcel.internal.classfile.Method; 42 import com.sun.org.apache.bcel.internal.classfile.Utility; 43 44 /** 45 * Convert code into HTML file. 46 * 47 * @version $Id: CodeHTML.java 1749603 2016-06-21 20:50:19Z ggregory $ 48 * 49 */ 50 final class CodeHTML { 51 52 private final String class_name; // name of current class 53 // private Method[] methods; // Methods to print 54 private final PrintWriter file; // file to write to 55 private BitSet goto_set; 56 private final ConstantPool constant_pool; 57 private final ConstantHTML constant_html; 58 private static boolean wide = false; 59 60 61 CodeHTML(final String dir, final String class_name, final Method[] methods, final ConstantPool constant_pool, 62 final ConstantHTML constant_html) throws IOException { 63 this.class_name = class_name; 64 // this.methods = methods; 65 this.constant_pool = constant_pool; 66 this.constant_html = constant_html; 67 file = new PrintWriter(new FileOutputStream(dir + class_name + "_code.html")); 68 file.println("<HTML><BODY BGCOLOR=\"#C0C0C0\">"); 69 for (int i = 0; i < methods.length; i++) { 70 writeMethod(methods[i], i); 71 } 72 file.println("</BODY></HTML>"); 73 file.close(); 74 } 75 76 77 /** 78 * Disassemble a stream of byte codes and return the 79 * string representation. 80 * 81 * @param stream data input stream 82 * @return String representation of byte code 83 */ 84 private String codeToHTML( final ByteSequence bytes, final int method_number ) throws IOException { 85 final short opcode = (short) bytes.readUnsignedByte(); 86 String name; 87 String signature; 88 int default_offset = 0; 89 int low; 90 int high; 91 int index; 92 int class_index; 93 int vindex; 94 int constant; 95 int[] jump_table; 96 int no_pad_bytes = 0; 97 int offset; 98 final StringBuilder buf = new StringBuilder(256); // CHECKSTYLE IGNORE MagicNumber 99 buf.append("<TT>").append(Const.getOpcodeName(opcode)).append("</TT></TD><TD>"); 100 /* Special case: Skip (0-3) padding bytes, i.e., the 101 * following bytes are 4-byte-aligned 102 */ 103 if ((opcode == Const.TABLESWITCH) || (opcode == Const.LOOKUPSWITCH)) { 104 final int remainder = bytes.getIndex() % 4; 105 no_pad_bytes = (remainder == 0) ? 0 : 4 - remainder; 106 for (int i = 0; i < no_pad_bytes; i++) { 107 bytes.readByte(); 108 } 109 // Both cases have a field default_offset in common 110 default_offset = bytes.readInt(); 111 } 112 switch (opcode) { 113 case Const.TABLESWITCH: 114 low = bytes.readInt(); 115 high = bytes.readInt(); 116 offset = bytes.getIndex() - 12 - no_pad_bytes - 1; 117 default_offset += offset; 118 buf.append("<TABLE BORDER=1><TR>"); 119 // Print switch indices in first row (and default) 120 jump_table = new int[high - low + 1]; 121 for (int i = 0; i < jump_table.length; i++) { 122 jump_table[i] = offset + bytes.readInt(); 123 buf.append("<TH>").append(low + i).append("</TH>"); 124 } 125 buf.append("<TH>default</TH></TR>\n<TR>"); 126 // Print target and default indices in second row 127 for (final int element : jump_table) { 128 buf.append("<TD><A HREF=\"#code").append(method_number).append("@").append( 129 element).append("\">").append(element).append("</A></TD>"); 130 } 131 buf.append("<TD><A HREF=\"#code").append(method_number).append("@").append( 132 default_offset).append("\">").append(default_offset).append( 133 "</A></TD></TR>\n</TABLE>\n"); 134 break; 135 /* Lookup switch has variable length arguments. 136 */ 137 case Const.LOOKUPSWITCH: 138 final int npairs = bytes.readInt(); 139 offset = bytes.getIndex() - 8 - no_pad_bytes - 1; 140 jump_table = new int[npairs]; 141 default_offset += offset; 142 buf.append("<TABLE BORDER=1><TR>"); 143 // Print switch indices in first row (and default) 144 for (int i = 0; i < npairs; i++) { 145 final int match = bytes.readInt(); 146 jump_table[i] = offset + bytes.readInt(); 147 buf.append("<TH>").append(match).append("</TH>"); 148 } 149 buf.append("<TH>default</TH></TR>\n<TR>"); 150 // Print target and default indices in second row 151 for (int i = 0; i < npairs; i++) { 152 buf.append("<TD><A HREF=\"#code").append(method_number).append("@").append( 153 jump_table[i]).append("\">").append(jump_table[i]).append("</A></TD>"); 154 } 155 buf.append("<TD><A HREF=\"#code").append(method_number).append("@").append( 156 default_offset).append("\">").append(default_offset).append( 157 "</A></TD></TR>\n</TABLE>\n"); 158 break; 159 /* Two address bytes + offset from start of byte stream form the 160 * jump target. 161 */ 162 case Const.GOTO: 163 case Const.IFEQ: 164 case Const.IFGE: 165 case Const.IFGT: 166 case Const.IFLE: 167 case Const.IFLT: 168 case Const.IFNE: 169 case Const.IFNONNULL: 170 case Const.IFNULL: 171 case Const.IF_ACMPEQ: 172 case Const.IF_ACMPNE: 173 case Const.IF_ICMPEQ: 174 case Const.IF_ICMPGE: 175 case Const.IF_ICMPGT: 176 case Const.IF_ICMPLE: 177 case Const.IF_ICMPLT: 178 case Const.IF_ICMPNE: 179 case Const.JSR: 180 index = bytes.getIndex() + bytes.readShort() - 1; 181 buf.append("<A HREF=\"#code").append(method_number).append("@").append(index) 182 .append("\">").append(index).append("</A>"); 183 break; 184 /* Same for 32-bit wide jumps 185 */ 186 case Const.GOTO_W: 187 case Const.JSR_W: 188 final int windex = bytes.getIndex() + bytes.readInt() - 1; 189 buf.append("<A HREF=\"#code").append(method_number).append("@").append(windex) 190 .append("\">").append(windex).append("</A>"); 191 break; 192 /* Index byte references local variable (register) 193 */ 194 case Const.ALOAD: 195 case Const.ASTORE: 196 case Const.DLOAD: 197 case Const.DSTORE: 198 case Const.FLOAD: 199 case Const.FSTORE: 200 case Const.ILOAD: 201 case Const.ISTORE: 202 case Const.LLOAD: 203 case Const.LSTORE: 204 case Const.RET: 205 if (wide) { 206 vindex = bytes.readShort(); 207 wide = false; // Clear flag 208 } else { 209 vindex = bytes.readUnsignedByte(); 210 } 211 buf.append("%").append(vindex); 212 break; 213 /* 214 * Remember wide byte which is used to form a 16-bit address in the 215 * following instruction. Relies on that the method is called again with 216 * the following opcode. 217 */ 218 case Const.WIDE: 219 wide = true; 220 buf.append("(wide)"); 221 break; 222 /* Array of basic type. 223 */ 224 case Const.NEWARRAY: 225 buf.append("<FONT COLOR=\"#00FF00\">").append(Const.getTypeName(bytes.readByte())).append( 226 "</FONT>"); 227 break; 228 /* Access object/class fields. 229 */ 230 case Const.GETFIELD: 231 case Const.GETSTATIC: 232 case Const.PUTFIELD: 233 case Const.PUTSTATIC: 234 index = bytes.readShort(); 235 final ConstantFieldref c1 = (ConstantFieldref) constant_pool.getConstant(index, 236 Const.CONSTANT_Fieldref); 237 class_index = c1.getClassIndex(); 238 name = constant_pool.getConstantString(class_index, Const.CONSTANT_Class); 239 name = Utility.compactClassName(name, false); 240 index = c1.getNameAndTypeIndex(); 241 final String field_name = constant_pool.constantToString(index, Const.CONSTANT_NameAndType); 242 if (name.equals(class_name)) { // Local field 243 buf.append("<A HREF=\"").append(class_name).append("_methods.html#field") 244 .append(field_name).append("\" TARGET=Methods>").append(field_name) 245 .append("</A>\n"); 246 } else { 247 buf.append(constant_html.referenceConstant(class_index)).append(".").append( 248 field_name); 249 } 250 break; 251 /* Operands are references to classes in constant pool 252 */ 253 case Const.CHECKCAST: 254 case Const.INSTANCEOF: 255 case Const.NEW: 256 index = bytes.readShort(); 257 buf.append(constant_html.referenceConstant(index)); 258 break; 259 /* Operands are references to methods in constant pool 260 */ 261 case Const.INVOKESPECIAL: 262 case Const.INVOKESTATIC: 263 case Const.INVOKEVIRTUAL: 264 case Const.INVOKEINTERFACE: 265 case Const.INVOKEDYNAMIC: 266 final int m_index = bytes.readShort(); 267 String str; 268 if (opcode == Const.INVOKEINTERFACE) { // Special treatment needed 269 bytes.readUnsignedByte(); // Redundant 270 bytes.readUnsignedByte(); // Reserved 271 // int nargs = bytes.readUnsignedByte(); // Redundant 272 // int reserved = bytes.readUnsignedByte(); // Reserved 273 final ConstantInterfaceMethodref c = (ConstantInterfaceMethodref) constant_pool 274 .getConstant(m_index, Const.CONSTANT_InterfaceMethodref); 275 class_index = c.getClassIndex(); 276 index = c.getNameAndTypeIndex(); 277 name = Class2HTML.referenceClass(class_index); 278 } else if (opcode == Const.INVOKEDYNAMIC) { // Special treatment needed 279 bytes.readUnsignedByte(); // Reserved 280 bytes.readUnsignedByte(); // Reserved 281 final ConstantInvokeDynamic c = (ConstantInvokeDynamic) constant_pool 282 .getConstant(m_index, Const.CONSTANT_InvokeDynamic); 283 index = c.getNameAndTypeIndex(); 284 name = "#" + c.getBootstrapMethodAttrIndex(); 285 } else { 286 // UNDONE: Java8 now allows INVOKESPECIAL and INVOKESTATIC to 287 // reference EITHER a Methodref OR an InterfaceMethodref. 288 // Not sure if that affects this code or not. (markro) 289 final ConstantMethodref c = (ConstantMethodref) constant_pool.getConstant(m_index, 290 Const.CONSTANT_Methodref); 291 class_index = c.getClassIndex(); 292 index = c.getNameAndTypeIndex(); 293 name = Class2HTML.referenceClass(class_index); 294 } 295 str = Class2HTML.toHTML(constant_pool.constantToString(constant_pool.getConstant( 296 index, Const.CONSTANT_NameAndType))); 297 // Get signature, i.e., types 298 final ConstantNameAndType c2 = (ConstantNameAndType) constant_pool.getConstant(index, 299 Const.CONSTANT_NameAndType); 300 signature = constant_pool.constantToString(c2.getSignatureIndex(), Const.CONSTANT_Utf8); 301 final String[] args = Utility.methodSignatureArgumentTypes(signature, false); 302 final String type = Utility.methodSignatureReturnType(signature, false); 303 buf.append(name).append(".<A HREF=\"").append(class_name).append("_cp.html#cp") 304 .append(m_index).append("\" TARGET=ConstantPool>").append(str).append( 305 "</A>").append("("); 306 // List arguments 307 for (int i = 0; i < args.length; i++) { 308 buf.append(Class2HTML.referenceType(args[i])); 309 if (i < args.length - 1) { 310 buf.append(", "); 311 } 312 } 313 // Attach return type 314 buf.append("):").append(Class2HTML.referenceType(type)); 315 break; 316 /* Operands are references to items in constant pool 317 */ 318 case Const.LDC_W: 319 case Const.LDC2_W: 320 index = bytes.readShort(); 321 buf.append("<A HREF=\"").append(class_name).append("_cp.html#cp").append(index) 322 .append("\" TARGET=\"ConstantPool\">").append( 323 Class2HTML.toHTML(constant_pool.constantToString(index, 324 constant_pool.getConstant(index).getTag()))).append("</a>"); 325 break; 326 case Const.LDC: 327 index = bytes.readUnsignedByte(); 328 buf.append("<A HREF=\"").append(class_name).append("_cp.html#cp").append(index) 329 .append("\" TARGET=\"ConstantPool\">").append( 330 Class2HTML.toHTML(constant_pool.constantToString(index, 331 constant_pool.getConstant(index).getTag()))).append("</a>"); 332 break; 333 /* Array of references. 334 */ 335 case Const.ANEWARRAY: 336 index = bytes.readShort(); 337 buf.append(constant_html.referenceConstant(index)); 338 break; 339 /* Multidimensional array of references. 340 */ 341 case Const.MULTIANEWARRAY: 342 index = bytes.readShort(); 343 final int dimensions = bytes.readByte(); 344 buf.append(constant_html.referenceConstant(index)).append(":").append(dimensions) 345 .append("-dimensional"); 346 break; 347 /* Increment local variable. 348 */ 349 case Const.IINC: 350 if (wide) { 351 vindex = bytes.readShort(); 352 constant = bytes.readShort(); 353 wide = false; 354 } else { 355 vindex = bytes.readUnsignedByte(); 356 constant = bytes.readByte(); 357 } 358 buf.append("%").append(vindex).append(" ").append(constant); 359 break; 360 default: 361 if (Const.getNoOfOperands(opcode) > 0) { 362 for (int i = 0; i < Const.getOperandTypeCount(opcode); i++) { 363 switch (Const.getOperandType(opcode, i)) { 364 case Const.T_BYTE: 365 buf.append(bytes.readUnsignedByte()); 366 break; 367 case Const.T_SHORT: // Either branch or index 368 buf.append(bytes.readShort()); 369 break; 370 case Const.T_INT: 371 buf.append(bytes.readInt()); 372 break; 373 default: // Never reached 374 throw new IllegalStateException( 375 "Unreachable default case reached! " + 376 Const.getOperandType(opcode, i)); 377 } 378 buf.append(" "); 379 } 380 } 381 } 382 buf.append("</TD>"); 383 return buf.toString(); 384 } 385 386 387 /** 388 * Find all target addresses in code, so that they can be marked 389 * with <A NAME = ...>. Target addresses are kept in an BitSet object. 390 */ 391 private void findGotos( final ByteSequence bytes, final Code code ) throws IOException { 392 int index; 393 goto_set = new BitSet(bytes.available()); 394 int opcode; 395 /* First get Code attribute from method and the exceptions handled 396 * (try .. catch) in this method. We only need the line number here. 397 */ 398 if (code != null) { 399 final CodeException[] ce = code.getExceptionTable(); 400 for (final CodeException cex : ce) { 401 goto_set.set(cex.getStartPC()); 402 goto_set.set(cex.getEndPC()); 403 goto_set.set(cex.getHandlerPC()); 404 } 405 // Look for local variables and their range 406 final Attribute[] attributes = code.getAttributes(); 407 for (final Attribute attribute : attributes) { 408 if (attribute.getTag() == Const.ATTR_LOCAL_VARIABLE_TABLE) { 409 final LocalVariable[] vars = ((LocalVariableTable) attribute) 410 .getLocalVariableTable(); 411 for (final LocalVariable var : vars) { 412 final int start = var.getStartPC(); 413 final int end = start + var.getLength(); 414 goto_set.set(start); 415 goto_set.set(end); 416 } 417 break; 418 } 419 } 420 } 421 // Get target addresses from GOTO, JSR, TABLESWITCH, etc. 422 for (; bytes.available() > 0;) { 423 opcode = bytes.readUnsignedByte(); 424 //System.out.println(getOpcodeName(opcode)); 425 switch (opcode) { 426 case Const.TABLESWITCH: 427 case Const.LOOKUPSWITCH: 428 //bytes.readByte(); // Skip already read byte 429 final int remainder = bytes.getIndex() % 4; 430 final int no_pad_bytes = (remainder == 0) ? 0 : 4 - remainder; 431 int default_offset; 432 int offset; 433 for (int j = 0; j < no_pad_bytes; j++) { 434 bytes.readByte(); 435 } 436 // Both cases have a field default_offset in common 437 default_offset = bytes.readInt(); 438 if (opcode == Const.TABLESWITCH) { 439 final int low = bytes.readInt(); 440 final int high = bytes.readInt(); 441 offset = bytes.getIndex() - 12 - no_pad_bytes - 1; 442 default_offset += offset; 443 goto_set.set(default_offset); 444 for (int j = 0; j < (high - low + 1); j++) { 445 index = offset + bytes.readInt(); 446 goto_set.set(index); 447 } 448 } else { // LOOKUPSWITCH 449 final int npairs = bytes.readInt(); 450 offset = bytes.getIndex() - 8 - no_pad_bytes - 1; 451 default_offset += offset; 452 goto_set.set(default_offset); 453 for (int j = 0; j < npairs; j++) { 454 // int match = bytes.readInt(); 455 bytes.readInt(); 456 index = offset + bytes.readInt(); 457 goto_set.set(index); 458 } 459 } 460 break; 461 case Const.GOTO: 462 case Const.IFEQ: 463 case Const.IFGE: 464 case Const.IFGT: 465 case Const.IFLE: 466 case Const.IFLT: 467 case Const.IFNE: 468 case Const.IFNONNULL: 469 case Const.IFNULL: 470 case Const.IF_ACMPEQ: 471 case Const.IF_ACMPNE: 472 case Const.IF_ICMPEQ: 473 case Const.IF_ICMPGE: 474 case Const.IF_ICMPGT: 475 case Const.IF_ICMPLE: 476 case Const.IF_ICMPLT: 477 case Const.IF_ICMPNE: 478 case Const.JSR: 479 //bytes.readByte(); // Skip already read byte 480 index = bytes.getIndex() + bytes.readShort() - 1; 481 goto_set.set(index); 482 break; 483 case Const.GOTO_W: 484 case Const.JSR_W: 485 //bytes.readByte(); // Skip already read byte 486 index = bytes.getIndex() + bytes.readInt() - 1; 487 goto_set.set(index); 488 break; 489 default: 490 bytes.unreadByte(); 491 codeToHTML(bytes, 0); // Ignore output 492 } 493 } 494 } 495 496 497 /** 498 * Write a single method with the byte code associated with it. 499 */ 500 private void writeMethod( final Method method, final int method_number ) throws IOException { 501 // Get raw signature 502 final String signature = method.getSignature(); 503 // Get array of strings containing the argument types 504 final String[] args = Utility.methodSignatureArgumentTypes(signature, false); 505 // Get return type string 506 final String type = Utility.methodSignatureReturnType(signature, false); 507 // Get method name 508 final String name = method.getName(); 509 final String html_name = Class2HTML.toHTML(name); 510 // Get method's access flags 511 String access = Utility.accessToString(method.getAccessFlags()); 512 access = Utility.replace(access, " ", " "); 513 // Get the method's attributes, the Code Attribute in particular 514 final Attribute[] attributes = method.getAttributes(); 515 file.print("<P><B><FONT COLOR=\"#FF0000\">" + access + "</FONT> " + "<A NAME=method" 516 + method_number + ">" + Class2HTML.referenceType(type) + "</A> <A HREF=\"" 517 + class_name + "_methods.html#method" + method_number + "\" TARGET=Methods>" 518 + html_name + "</A>("); 519 for (int i = 0; i < args.length; i++) { 520 file.print(Class2HTML.referenceType(args[i])); 521 if (i < args.length - 1) { 522 file.print(", "); 523 } 524 } 525 file.println(")</B></P>"); 526 Code c = null; 527 byte[] code = null; 528 if (attributes.length > 0) { 529 file.print("<H4>Attributes</H4><UL>\n"); 530 for (int i = 0; i < attributes.length; i++) { 531 byte tag = attributes[i].getTag(); 532 if (tag != Const.ATTR_UNKNOWN) { 533 file.print("<LI><A HREF=\"" + class_name + "_attributes.html#method" 534 + method_number + "@" + i + "\" TARGET=Attributes>" 535 + Const.getAttributeName(tag) + "</A></LI>\n"); 536 } else { 537 file.print("<LI>" + attributes[i] + "</LI>"); 538 } 539 if (tag == Const.ATTR_CODE) { 540 c = (Code) attributes[i]; 541 final Attribute[] attributes2 = c.getAttributes(); 542 code = c.getCode(); 543 file.print("<UL>"); 544 for (int j = 0; j < attributes2.length; j++) { 545 tag = attributes2[j].getTag(); 546 file.print("<LI><A HREF=\"" + class_name + "_attributes.html#" + "method" 547 + method_number + "@" + i + "@" + j + "\" TARGET=Attributes>" 548 + Const.getAttributeName(tag) + "</A></LI>\n"); 549 } 550 file.print("</UL>"); 551 } 552 } 553 file.println("</UL>"); 554 } 555 if (code != null) { // No code, an abstract method, e.g. 556 //System.out.println(name + "\n" + Utility.codeToString(code, constant_pool, 0, -1)); 557 // Print the byte code 558 try (ByteSequence stream = new ByteSequence(code)) { 559 stream.mark(stream.available()); 560 findGotos(stream, c); 561 stream.reset(); 562 file.println("<TABLE BORDER=0><TR><TH ALIGN=LEFT>Byte<BR>offset</TH>" 563 + "<TH ALIGN=LEFT>Instruction</TH><TH ALIGN=LEFT>Argument</TH>"); 564 for (; stream.available() > 0;) { 565 final int offset = stream.getIndex(); 566 final String str = codeToHTML(stream, method_number); 567 String anchor = ""; 568 /* 569 * Set an anchor mark if this line is targetted by a goto, jsr, etc. Defining an anchor for every 570 * line is very inefficient! 571 */ 572 if (goto_set.get(offset)) { 573 anchor = "<A NAME=code" + method_number + "@" + offset + "></A>"; 574 } 575 String anchor2; 576 if (stream.getIndex() == code.length) { 577 anchor2 = "<A NAME=code" + method_number + "@" + code.length + ">" + offset + "</A>"; 578 } else { 579 anchor2 = "" + offset; 580 } 581 file.println("<TR VALIGN=TOP><TD>" + anchor2 + "</TD><TD>" + anchor + str + "</TR>"); 582 } 583 } 584 // Mark last line, may be targetted from Attributes window 585 file.println("<TR><TD> </A></TD></TR>"); 586 file.println("</TABLE>"); 587 } 588 } 589 } |