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