1. package com.sun.org.apache.bcel.internal.util;
  2. /* ====================================================================
  3. * The Apache Software License, Version 1.1
  4. *
  5. * Copyright (c) 2001 The Apache Software Foundation. All rights
  6. * reserved.
  7. *
  8. * Redistribution and use in source and binary forms, with or without
  9. * modification, are permitted provided that the following conditions
  10. * are met:
  11. *
  12. * 1. Redistributions of source code must retain the above copyright
  13. * notice, this list of conditions and the following disclaimer.
  14. *
  15. * 2. Redistributions in binary form must reproduce the above copyright
  16. * notice, this list of conditions and the following disclaimer in
  17. * the documentation and/or other materials provided with the
  18. * distribution.
  19. *
  20. * 3. The end-user documentation included with the redistribution,
  21. * if any, must include the following acknowledgment:
  22. * "This product includes software developed by the
  23. * Apache Software Foundation (http://www.apache.org/)."
  24. * Alternately, this acknowledgment may appear in the software itself,
  25. * if and wherever such third-party acknowledgments normally appear.
  26. *
  27. * 4. The names "Apache" and "Apache Software Foundation" and
  28. * "Apache BCEL" must not be used to endorse or promote products
  29. * derived from this software without prior written permission. For
  30. * written permission, please contact apache@apache.org.
  31. *
  32. * 5. Products derived from this software may not be called "Apache",
  33. * "Apache BCEL", nor may "Apache" appear in their name, without
  34. * prior written permission of the Apache Software Foundation.
  35. *
  36. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  37. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  38. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  39. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  40. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  41. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  42. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  43. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  44. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  45. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  46. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  47. * SUCH DAMAGE.
  48. * ====================================================================
  49. *
  50. * This software consists of voluntary contributions made by many
  51. * individuals on behalf of the Apache Software Foundation. For more
  52. * information on the Apache Software Foundation, please see
  53. * <http://www.apache.org/>.
  54. */
  55. import com.sun.org.apache.bcel.internal.classfile.*;
  56. import java.io.*;
  57. import java.util.BitSet;
  58. /**
  59. * Convert code into HTML file.
  60. *
  61. * @version $Id: CodeHTML.java,v 1.1.1.1 2001/10/29 20:00:30 jvanzyl Exp $
  62. * @author <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A>
  63. *
  64. */
  65. final class CodeHTML implements com.sun.org.apache.bcel.internal.Constants {
  66. private String class_name; // name of current class
  67. private Method[] methods; // Methods to print
  68. private PrintWriter file; // file to write to
  69. private BitSet goto_set;
  70. private ConstantPool constant_pool;
  71. private ConstantHTML constant_html;
  72. private static boolean wide=false;
  73. CodeHTML(String dir, String class_name,
  74. Method[] methods, ConstantPool constant_pool,
  75. ConstantHTML constant_html) throws IOException
  76. {
  77. this.class_name = class_name;
  78. this.methods = methods;
  79. this.constant_pool = constant_pool;
  80. this.constant_html = constant_html;
  81. file = new PrintWriter(new FileOutputStream(dir + class_name + "_code.html"));
  82. file.println("<HTML><BODY BGCOLOR=\"#C0C0C0\">");
  83. for(int i=0; i < methods.length; i++)
  84. writeMethod(methods[i], i);
  85. file.println("</BODY></HTML>");
  86. file.close();
  87. }
  88. /**
  89. * Disassemble a stream of byte codes and return the
  90. * string representation.
  91. *
  92. * @param stream data input stream
  93. * @return String representation of byte code
  94. */
  95. private final String codeToHTML(ByteSequence bytes, int method_number)
  96. throws IOException
  97. {
  98. short opcode = (short)bytes.readUnsignedByte();
  99. StringBuffer buf;
  100. String name, sig, signature;
  101. int default_offset=0, low, high;
  102. int index, class_index, vindex, constant;
  103. int[] jump_table;
  104. int no_pad_bytes=0, offset;
  105. buf = new StringBuffer("<TT>" + OPCODE_NAMES[opcode] + "</TT></TD><TD>");
  106. /* Special case: Skip (0-3) padding bytes, i.e., the
  107. * following bytes are 4-byte-aligned
  108. */
  109. if((opcode == TABLESWITCH) || (opcode == LOOKUPSWITCH)) {
  110. int remainder = bytes.getIndex() % 4;
  111. no_pad_bytes = (remainder == 0)? 0 : 4 - remainder;
  112. for(int i=0; i < no_pad_bytes; i++)
  113. bytes.readByte();
  114. // Both cases have a field default_offset in common
  115. default_offset = bytes.readInt();
  116. }
  117. switch(opcode) {
  118. case TABLESWITCH:
  119. low = bytes.readInt();
  120. high = bytes.readInt();
  121. offset = bytes.getIndex() - 12 - no_pad_bytes - 1;
  122. default_offset += offset;
  123. buf.append("<TABLE BORDER=1><TR>");
  124. // Print switch indices in first row (and default)
  125. jump_table = new int[high - low + 1];
  126. for(int i=0; i < jump_table.length; i++) {
  127. jump_table[i] = offset + bytes.readInt();
  128. buf.append("<TH>" + (low + i) + "</TH>");
  129. }
  130. buf.append("<TH>default</TH></TR>\n<TR>");
  131. // Print target and default indices in second row
  132. for(int i=0; i < jump_table.length; i++)
  133. buf.append("<TD><A HREF=\"#code" + method_number + "@" +
  134. jump_table[i] + "\">" + jump_table[i] + "</A></TD>");
  135. buf.append("<TD><A HREF=\"#code" + method_number + "@" +
  136. default_offset + "\">" + default_offset + "</A></TD></TR>\n</TABLE>\n");
  137. break;
  138. /* Lookup switch has variable length arguments.
  139. */
  140. case LOOKUPSWITCH:
  141. int npairs = bytes.readInt();
  142. offset = bytes.getIndex() - 8 - no_pad_bytes - 1;
  143. jump_table = new int[npairs];
  144. default_offset += offset;
  145. buf.append("<TABLE BORDER=1><TR>");
  146. // Print switch indices in first row (and default)
  147. for(int i=0; i < npairs; i++) {
  148. int match = bytes.readInt();
  149. jump_table[i] = offset + bytes.readInt();
  150. buf.append("<TH>" + match + "</TH>");
  151. }
  152. buf.append("<TH>default</TH></TR>\n<TR>");
  153. // Print target and default indices in second row
  154. for(int i=0; i < npairs; i++)
  155. buf.append("<TD><A HREF=\"#code" + method_number + "@" +
  156. jump_table[i] + "\">" + jump_table[i] + "</A></TD>");
  157. buf.append("<TD><A HREF=\"#code" + method_number + "@" +
  158. default_offset + "\">" + default_offset + "</A></TD></TR>\n</TABLE>\n");
  159. break;
  160. /* Two address bytes + offset from start of byte stream form the
  161. * jump target.
  162. */
  163. case GOTO: case IFEQ: case IFGE: case IFGT:
  164. case IFLE: case IFLT:
  165. case IFNE: case IFNONNULL: case IFNULL: case IF_ACMPEQ:
  166. case IF_ACMPNE: case IF_ICMPEQ: case IF_ICMPGE: case IF_ICMPGT:
  167. case IF_ICMPLE: case IF_ICMPLT: case IF_ICMPNE: case JSR:
  168. index = (int)(bytes.getIndex() + bytes.readShort() - 1);
  169. buf.append("<A HREF=\"#code" + method_number + "@" + index + "\">" + index + "</A>");
  170. break;
  171. /* Same for 32-bit wide jumps
  172. */
  173. case GOTO_W: case JSR_W:
  174. int windex = bytes.getIndex() + bytes.readInt() - 1;
  175. buf.append("<A HREF=\"#code" + method_number + "@" + windex + "\">" +
  176. windex + "</A>");
  177. break;
  178. /* Index byte references local variable (register)
  179. */
  180. case ALOAD: case ASTORE: case DLOAD: case DSTORE: case FLOAD:
  181. case FSTORE: case ILOAD: case ISTORE: case LLOAD: case LSTORE:
  182. case RET:
  183. if(wide) {
  184. vindex = bytes.readShort();
  185. wide=false; // Clear flag
  186. }
  187. else
  188. vindex = bytes.readUnsignedByte();
  189. buf.append("%" + vindex);
  190. break;
  191. /*
  192. * Remember wide byte which is used to form a 16-bit address in the
  193. * following instruction. Relies on that the method is called again with
  194. * the following opcode.
  195. */
  196. case WIDE:
  197. wide = true;
  198. buf.append("(wide)");
  199. break;
  200. /* Array of basic type.
  201. */
  202. case NEWARRAY:
  203. buf.append("<FONT COLOR=\"#00FF00\">" + TYPE_NAMES[bytes.readByte()] + "</FONT>");
  204. break;
  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. class_index = c1.getClassIndex();
  211. name = constant_pool.getConstantString(class_index, CONSTANT_Class);
  212. name = Utility.compactClassName(name, false);
  213. index = c1.getNameAndTypeIndex();
  214. String field_name = constant_pool.constantToString(index, CONSTANT_NameAndType);
  215. if(name.equals(class_name)) { // Local field
  216. buf.append("<A HREF=\"" + class_name + "_methods.html#field" + field_name +
  217. "\" TARGET=Methods>" + field_name + "</A>\n");
  218. }
  219. else
  220. buf.append(constant_html.referenceConstant(class_index) + "." + field_name);
  221. break;
  222. /* Operands are references to classes in constant pool
  223. */
  224. case CHECKCAST: case INSTANCEOF: case NEW:
  225. index = bytes.readShort();
  226. buf.append(constant_html.referenceConstant(index));
  227. break;
  228. /* Operands are references to methods in constant pool
  229. */
  230. case INVOKESPECIAL: case INVOKESTATIC: case INVOKEVIRTUAL: case INVOKEINTERFACE:
  231. int m_index = bytes.readShort();
  232. String str;
  233. if(opcode == INVOKEINTERFACE) { // Special treatment needed
  234. int nargs = bytes.readUnsignedByte(); // Redundant
  235. int reserved = bytes.readUnsignedByte(); // Reserved
  236. ConstantInterfaceMethodref c=(ConstantInterfaceMethodref)constant_pool.getConstant(m_index, CONSTANT_InterfaceMethodref);
  237. class_index = c.getClassIndex();
  238. str = constant_pool.constantToString(c);
  239. index = c.getNameAndTypeIndex();
  240. }
  241. else {
  242. ConstantMethodref c = (ConstantMethodref)constant_pool.getConstant(m_index, CONSTANT_Methodref);
  243. class_index = c.getClassIndex();
  244. str = constant_pool.constantToString(c);
  245. index = c.getNameAndTypeIndex();
  246. }
  247. name = Class2HTML.referenceClass(class_index);
  248. str = Class2HTML.toHTML(constant_pool.constantToString(constant_pool.getConstant(index, CONSTANT_NameAndType)));
  249. // Get signature, i.e., types
  250. ConstantNameAndType c2 = (ConstantNameAndType)constant_pool.
  251. getConstant(index, CONSTANT_NameAndType);
  252. signature = constant_pool.constantToString(c2.getSignatureIndex(),
  253. CONSTANT_Utf8);
  254. String[] args = Utility.methodSignatureArgumentTypes(signature, false);
  255. String type = Utility.methodSignatureReturnType(signature, false);
  256. buf.append(name + ".<A HREF=\"" + class_name + "_cp.html#cp" + m_index +
  257. "\" TARGET=ConstantPool>" + str + "</A>" + "(");
  258. // List arguments
  259. for(int i=0; i < args.length; i++) {
  260. buf.append(Class2HTML.referenceType(args[i]));
  261. if(i < args.length - 1)
  262. buf.append(", ");
  263. }
  264. // Attach return type
  265. buf.append("):" + Class2HTML.referenceType(type));
  266. break;
  267. /* Operands are references to items in constant pool
  268. */
  269. case LDC_W: case LDC2_W:
  270. index = bytes.readShort();
  271. buf.append("<A HREF=\"" + class_name + "_cp.html#cp" + index +
  272. "\" TARGET=\"ConstantPool\">" +
  273. Class2HTML.toHTML(constant_pool.constantToString(index,
  274. constant_pool.
  275. getConstant(index).getTag()))+
  276. "</a>");
  277. break;
  278. case LDC:
  279. index = bytes.readUnsignedByte();
  280. buf.append("<A HREF=\"" + class_name + "_cp.html#cp" + index +
  281. "\" TARGET=\"ConstantPool\">" +
  282. Class2HTML.toHTML(constant_pool.constantToString(index,
  283. constant_pool.
  284. getConstant(index).getTag()))+
  285. "</a>");
  286. break;
  287. /* Array of references.
  288. */
  289. case ANEWARRAY:
  290. index = bytes.readShort();
  291. buf.append(constant_html.referenceConstant(index));
  292. break;
  293. /* Multidimensional array of references.
  294. */
  295. case MULTIANEWARRAY:
  296. index = bytes.readShort();
  297. int dimensions = bytes.readByte();
  298. buf.append(constant_html.referenceConstant(index) + ":" + dimensions + "-dimensional");
  299. break;
  300. /* Increment local variable.
  301. */
  302. case IINC:
  303. if(wide) {
  304. vindex = bytes.readShort();
  305. constant = bytes.readShort();
  306. wide = false;
  307. }
  308. else {
  309. vindex = bytes.readUnsignedByte();
  310. constant = bytes.readByte();
  311. }
  312. buf.append("%" + vindex + " " + constant);
  313. break;
  314. default:
  315. if(NO_OF_OPERANDS[opcode] > 0) {
  316. for(int i=0; i < TYPE_OF_OPERANDS[opcode].length; i++) {
  317. switch(TYPE_OF_OPERANDS[opcode][i]) {
  318. case T_BYTE:
  319. buf.append(bytes.readUnsignedByte());
  320. break;
  321. case T_SHORT: // Either branch or index
  322. buf.append(bytes.readShort());
  323. break;
  324. case T_INT:
  325. buf.append(bytes.readInt());
  326. break;
  327. default: // Never reached
  328. System.err.println("Unreachable default case reached!");
  329. System.exit(-1);
  330. }
  331. buf.append(" ");
  332. }
  333. }
  334. }
  335. buf.append("</TD>");
  336. return buf.toString();
  337. }
  338. /**
  339. * Find all target addresses in code, so that they can be marked
  340. * with <A NAME = ...>. Target addresses are kept in an BitSet object.
  341. */
  342. private final void findGotos(ByteSequence bytes, Method method, Code code)
  343. throws IOException
  344. {
  345. int index;
  346. goto_set = new BitSet(bytes.available());
  347. int opcode;
  348. /* First get Code attribute from method and the exceptions handled
  349. * (try .. catch) in this method. We only need the line number here.
  350. */
  351. if(code != null) {
  352. CodeException[] ce = code.getExceptionTable();
  353. int len = ce.length;
  354. for(int i=0; i < len; i++) {
  355. goto_set.set(ce[i].getStartPC());
  356. goto_set.set(ce[i].getEndPC());
  357. goto_set.set(ce[i].getHandlerPC());
  358. }
  359. // Look for local variables and their range
  360. Attribute[] attributes = code.getAttributes();
  361. for(int i=0; i < attributes.length; i++) {
  362. if(attributes[i].getTag() == ATTR_LOCAL_VARIABLE_TABLE) {
  363. LocalVariable[] vars = ((LocalVariableTable)attributes[i]).getLocalVariableTable();
  364. for(int j=0; j < vars.length; j++) {
  365. int start = vars[j].getStartPC();
  366. int end = (int)(start + vars[j].getLength());
  367. goto_set.set(start);
  368. goto_set.set(end);
  369. }
  370. break;
  371. }
  372. }
  373. }
  374. // Get target addresses from GOTO, JSR, TABLESWITCH, etc.
  375. for(int i=0; bytes.available() > 0; i++) {
  376. opcode = bytes.readUnsignedByte();
  377. //System.out.println(OPCODE_NAMES[opcode]);
  378. switch(opcode) {
  379. case TABLESWITCH: case LOOKUPSWITCH:
  380. //bytes.readByte(); // Skip already read byte
  381. int remainder = bytes.getIndex() % 4;
  382. int no_pad_bytes = (remainder == 0)? 0 : 4 - remainder;
  383. int default_offset, offset;
  384. for(int j=0; j < no_pad_bytes; j++)
  385. bytes.readByte();
  386. // Both cases have a field default_offset in common
  387. default_offset = bytes.readInt();
  388. if(opcode == TABLESWITCH) {
  389. int low = bytes.readInt();
  390. int high = bytes.readInt();
  391. offset = bytes.getIndex() - 12 - no_pad_bytes - 1;
  392. default_offset += offset;
  393. goto_set.set(default_offset);
  394. for(int j=0; j < (high - low + 1); j++) {
  395. index = offset + bytes.readInt();
  396. goto_set.set(index);
  397. }
  398. }
  399. else { // LOOKUPSWITCH
  400. int npairs = bytes.readInt();
  401. offset = bytes.getIndex() - 8 - no_pad_bytes - 1;
  402. default_offset += offset;
  403. goto_set.set(default_offset);
  404. for(int j=0; j < npairs; j++) {
  405. int match = bytes.readInt();
  406. index = offset + bytes.readInt();
  407. goto_set.set(index);
  408. }
  409. }
  410. break;
  411. case GOTO: case IFEQ: case IFGE: case IFGT:
  412. case IFLE: case IFLT:
  413. case IFNE: case IFNONNULL: case IFNULL: case IF_ACMPEQ:
  414. case IF_ACMPNE: case IF_ICMPEQ: case IF_ICMPGE: case IF_ICMPGT:
  415. case IF_ICMPLE: case IF_ICMPLT: case IF_ICMPNE: case JSR:
  416. //bytes.readByte(); // Skip already read byte
  417. index = bytes.getIndex() + bytes.readShort() - 1;
  418. goto_set.set(index);
  419. break;
  420. case GOTO_W: case JSR_W:
  421. //bytes.readByte(); // Skip already read byte
  422. index = bytes.getIndex() + bytes.readInt() - 1;
  423. goto_set.set(index);
  424. break;
  425. default:
  426. bytes.unreadByte();
  427. codeToHTML(bytes, 0); // Ignore output
  428. }
  429. }
  430. }
  431. /**
  432. * Write a single method with the byte code associated with it.
  433. */
  434. private void writeMethod(Method method, int method_number)
  435. throws IOException
  436. {
  437. // Get raw signature
  438. String signature = method.getSignature();
  439. // Get array of strings containing the argument types
  440. String[] args = Utility.methodSignatureArgumentTypes(signature, false);
  441. // Get return type string
  442. String type = Utility.methodSignatureReturnType(signature, false);
  443. // Get method name
  444. String name = method.getName();
  445. String html_name = Class2HTML.toHTML(name);
  446. // Get method's access flags
  447. String access = Utility.accessToString(method.getAccessFlags());
  448. access = Utility.replace(access, " ", " ");
  449. // Get the method's attributes, the Code Attribute in particular
  450. Attribute[] attributes= method.getAttributes();
  451. file.print("<P><B><FONT COLOR=\"#FF0000\">" + access + "</FONT> " +
  452. "<A NAME=method" + method_number + ">" + Class2HTML.referenceType(type) +
  453. "</A> <A HREF=\"" + class_name + "_methods.html#method" + method_number +
  454. "\" TARGET=Methods>" + html_name + "</A>(");
  455. for(int i=0; i < args.length; i++) {
  456. file.print(Class2HTML.referenceType(args[i]));
  457. if(i < args.length - 1)
  458. file.print(", ");
  459. }
  460. file.println(")</B></P>");
  461. Code c=null;
  462. byte[] code=null;
  463. if(attributes.length > 0) {
  464. file.print("<H4>Attributes</H4><UL>\n");
  465. for(int i=0; i < attributes.length; i++) {
  466. byte tag = attributes[i].getTag();
  467. if(tag != ATTR_UNKNOWN)
  468. file.print("<LI><A HREF=\"" + class_name + "_attributes.html#method" + method_number + "@" + i +
  469. "\" TARGET=Attributes>" + ATTRIBUTE_NAMES[tag] + "</A></LI>\n");
  470. else
  471. file.print("<LI>" + attributes[i] + "</LI>");
  472. if(tag == ATTR_CODE) {
  473. c = (Code)attributes[i];
  474. Attribute[] attributes2 = c.getAttributes();
  475. code = c.getCode();
  476. file.print("<UL>");
  477. for(int j=0; j < attributes2.length; j++) {
  478. tag = attributes2[j].getTag();
  479. file.print("<LI><A HREF=\"" + class_name + "_attributes.html#" +
  480. "method" + method_number + "@" + i + "@" + j + "\" TARGET=Attributes>" +
  481. ATTRIBUTE_NAMES[tag] + "</A></LI>\n");
  482. }
  483. file.print("</UL>");
  484. }
  485. }
  486. file.println("</UL>");
  487. }
  488. if(code != null) { // No code, an abstract method, e.g.
  489. //System.out.println(name + "\n" + Utility.codeToString(code, constant_pool, 0, -1));
  490. // Print the byte code
  491. ByteSequence stream = new ByteSequence(code);
  492. stream.mark(stream.available());
  493. findGotos(stream, method, c);
  494. stream.reset();
  495. file.println("<TABLE BORDER=0><TR><TH ALIGN=LEFT>Byte<BR>offset</TH>" +
  496. "<TH ALIGN=LEFT>Instruction</TH><TH ALIGN=LEFT>Argument</TH>");
  497. for(int i=0; stream.available() > 0; i++) {
  498. int offset = stream.getIndex();
  499. String str = codeToHTML(stream, method_number);
  500. String anchor = "";
  501. /* Set an anchor mark if this line is targetted by a goto, jsr, etc.
  502. * Defining an anchor for every line is very inefficient!
  503. */
  504. if(goto_set.get(offset))
  505. anchor = "<A NAME=code" + method_number + "@" + offset + "></A>";
  506. String anchor2;
  507. if(stream.getIndex() == code.length) // last loop
  508. anchor2 = "<A NAME=code" + method_number + "@" + code.length + ">" + offset + "</A>";
  509. else
  510. anchor2 = "" + offset;
  511. file.println("<TR VALIGN=TOP><TD>" + anchor2 + "</TD><TD>" + anchor + str + "</TR>");
  512. }
  513. // Mark last line, may be targetted from Attributes window
  514. file.println("<TR><TD> </A></TD></TR>");
  515. file.println("</TABLE>");
  516. }
  517. }
  518. }