author | joehw |
Sun, 13 Aug 2017 21:10:40 -0700 | |
changeset 46174 | 5611d2529b49 |
parent 44797 | 8b3b3b911b8a |
permissions | -rw-r--r-- |
6 | 1 |
/* |
2 |
* reserved comment block |
|
3 |
* DO NOT REMOVE OR ALTER! |
|
4 |
*/ |
|
44797
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
25868
diff
changeset
|
5 |
/* |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
25868
diff
changeset
|
6 |
* Licensed to the Apache Software Foundation (ASF) under one or more |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
25868
diff
changeset
|
7 |
* contributor license agreements. See the NOTICE file distributed with |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
25868
diff
changeset
|
8 |
* this work for additional information regarding copyright ownership. |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
25868
diff
changeset
|
9 |
* The ASF licenses this file to You under the Apache License, Version 2.0 |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
25868
diff
changeset
|
10 |
* (the "License"); you may not use this file except in compliance with |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
25868
diff
changeset
|
11 |
* the License. You may obtain a copy of the License at |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
25868
diff
changeset
|
12 |
* |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
25868
diff
changeset
|
13 |
* http://www.apache.org/licenses/LICENSE-2.0 |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
25868
diff
changeset
|
14 |
* |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
25868
diff
changeset
|
15 |
* Unless required by applicable law or agreed to in writing, software |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
25868
diff
changeset
|
16 |
* distributed under the License is distributed on an "AS IS" BASIS, |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
25868
diff
changeset
|
17 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
25868
diff
changeset
|
18 |
* See the License for the specific language governing permissions and |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
25868
diff
changeset
|
19 |
* limitations under the License. |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
25868
diff
changeset
|
20 |
*/ |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
25868
diff
changeset
|
21 |
|
6 | 22 |
package com.sun.org.apache.bcel.internal.util; |
23 |
||
46174 | 24 |
import java.io.FileOutputStream; |
25 |
import java.io.IOException; |
|
26 |
import java.io.PrintWriter; |
|
27 |
import java.util.BitSet; |
|
6 | 28 |
|
46174 | 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; |
|
6 | 43 |
|
44 |
/** |
|
45 |
* Convert code into HTML file. |
|
46 |
* |
|
46174 | 47 |
* @version $Id: CodeHTML.java 1749603 2016-06-21 20:50:19Z ggregory $ |
6 | 48 |
* |
49 |
*/ |
|
46174 | 50 |
final class CodeHTML { |
6 | 51 |
|
46174 | 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; |
|
6 | 59 |
|
60 |
||
46174 | 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(); |
|
6 | 74 |
} |
75 |
||
76 |
||
46174 | 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(); |
|
6 | 111 |
} |
46174 | 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(); |
|
6 | 384 |
} |
385 |
||
386 |
||
46174 | 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. |
|
6 | 390 |
*/ |
46174 | 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 |
} |
|
6 | 420 |
} |
46174 | 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 |
} |
|
6 | 494 |
} |
495 |
||
496 |
||
46174 | 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 |
} |
|
6 | 524 |
} |
46174 | 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>"); |
|
6 | 554 |
} |
46174 | 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 |
} |
|
6 | 588 |
} |
589 |
} |