1 /*
2 * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23 package org.openjdk.asmtools.jdis;
24
25 import org.openjdk.asmtools.jasm.Tables;
26
27 import java.io.DataInputStream;
28 import java.io.IOException;
29 import java.io.PrintWriter;
30 import java.util.ArrayList;
31 import java.util.HashMap;
32
33 import static org.openjdk.asmtools.jasm.OpcodeTables.Opcode;
34 import static org.openjdk.asmtools.jasm.OpcodeTables.opcode;
35 import static org.openjdk.asmtools.jasm.Tables.*;
36 import static org.openjdk.asmtools.jasm.Tables.AttrTag.ATT_RuntimeInvisibleTypeAnnotations;
37 import static org.openjdk.asmtools.jasm.Tables.AttrTag.ATT_RuntimeVisibleTypeAnnotations;
38 import static org.openjdk.asmtools.jdis.Utils.commentString;
39
40 /**
41 * Code data for a code attribute in method members in a class of the Java Disassembler
42 */
43 public class CodeData extends Indenter {
44
45 /**
46 * Raw byte array for the byte codes
47 */
48 protected byte[] code;
49 /**
50 * Limit for the stack size
51 */
52 protected int max_stack;
53
54 /* CodeData Fields */
55 /**
56 * Limit for the number of local vars
57 */
58 protected int max_locals;
59 /**
60 * The remaining attributes of this class
61 */
62 protected ArrayList<AttrData> attrs = new ArrayList<>(0); // AttrData
63
64 // internal references
65 protected ClassData cls;
66 protected MethodData meth;
67 /**
68 * (parsed) Trap table, describes exceptions caught
69 */
70 private ArrayList<TrapData> trap_table = new ArrayList<>(0); // TrapData
71 /**
72 * (parsed) Line Number table, describes source lines associated with ByteCode indexes
73 */
74 private ArrayList<LineNumData> lin_num_tb = new ArrayList<>(0); // LineNumData
75 /**
76 * (parsed) Local Variable table, describes variable scopes associated with ByteCode
77 * indexes
78 */
79 private ArrayList<LocVarData> loc_var_tb = new ArrayList<>(0); // LocVarData
80 /**
81 * (parsed) stack map table, describes compiler hints for stack rep, associated with
82 * ByteCode indexes
83 */
84 private ArrayList<StackMapData> stack_map = null;
85 /**
86 * The visible type annotations for this method
87 */
88 private ArrayList<TypeAnnotationData> visibleTypeAnnotations;
89 /**
90 * The invisible type annotations for this method
91 */
92 private ArrayList<TypeAnnotationData> invisibleTypeAnnotations;
93
94 /**
95 * (parsed) reversed bytecode index hash, associates labels with ByteCode indexes
96 */
97 private HashMap<Integer, iAtt> iattrs = new HashMap<>();
98 private PrintWriter out;
99 public CodeData(MethodData meth) {
100 this.meth = meth;
101 this.cls = meth.cls;
102 this.out = cls.out;
103 }
104
105 private static int align(int n) {
106 return (n + 3) & ~3;
107 }
108 /*-------------------------------------------------------- */
109
110 private int getbyte(int pc) {
111 return code[pc];
112 }
113
114 private int getUbyte(int pc) {
115 return code[pc] & 0xFF;
116 }
117
118 private int getShort(int pc) {
119 return (code[pc] << 8) | (code[pc + 1] & 0xFF);
120 }
121
122 private int getUShort(int pc) {
123 return ((code[pc] << 8) | (code[pc + 1] & 0xFF)) & 0xFFFF;
124 }
125
126 private int getInt(int pc) {
127 return (getShort(pc) << 16) | (getShort(pc + 2) & 0xFFFF);
128 }
129
130 protected iAtt get_iAtt(int pc) {
131 Integer PC = pc;
132 iAtt res = iattrs.get(PC);
133 if (res == null) {
134 res = new iAtt(this);
135 iattrs.put(PC, res);
136 }
137 return res;
138 }
139
140 /*========================================================*/
141 /* Read Methods */
142 private void readLineNumTable(DataInputStream in) throws IOException {
143 int len = in.readInt(); // attr_length
144 int numlines = in.readUnsignedShort();
145 lin_num_tb = new ArrayList<>(numlines);
146 TraceUtils.traceln(3, "CodeAttr: LineNumTable[" + numlines + "] len=" + len);
147 for (int l = 0; l < numlines; l++) {
148 lin_num_tb.add(new LineNumData(in));
149 }
150 }
151
152 private void readLocVarTable(DataInputStream in) throws IOException {
153 int len = in.readInt(); // attr_length
154 int numlines = in.readUnsignedShort();
155 loc_var_tb = new ArrayList<>(numlines);
156 TraceUtils.traceln(3, "CodeAttr: LocalVariableTable[" + numlines + "] len=" + len);
157 for (int l = 0; l < numlines; l++) {
158 loc_var_tb.add(new LocVarData(in));
159 }
160 }
161
162 private void readTrapTable(DataInputStream in) throws IOException {
163 int trap_table_len = in.readUnsignedShort();
164 TraceUtils.traceln(3, "CodeAttr: TrapTable[" + trap_table_len + "]");
165 trap_table = new ArrayList<>(trap_table_len);
166 for (int l = 0; l < trap_table_len; l++) {
167 trap_table.add(new TrapData(in, l));
168 }
169 }
170
171 private void readStackMap(DataInputStream in) throws IOException {
172 int len = in.readInt(); // attr_length
173 int stack_map_len = in.readUnsignedShort();
174 TraceUtils.traceln(3, "CodeAttr: Stack_Map: attrlen=" + len + " num=" + stack_map_len);
175 stack_map = new ArrayList<>(stack_map_len);
176 StackMapData.prevFramePC = 0;
177 for (int k = 0; k < stack_map_len; k++) {
178 stack_map.add(new StackMapData(this, in));
179 }
180 }
181
182 private void readStackMapTable(DataInputStream in) throws IOException {
183 int len = in.readInt(); // attr_length
184 int stack_map_len = in.readUnsignedShort();
185 TraceUtils.traceln(3, "CodeAttr: Stack_Map_Table: attrlen=" + len + " num=" + stack_map_len);
186 stack_map = new ArrayList<>(stack_map_len);
187 StackMapData.prevFramePC = 0;
188 for (int k = 0; k < stack_map_len; k++) {
189 stack_map.add(new StackMapData(this, in, true));
190 }
191 }
192
193 private void readTypeAnnotations(DataInputStream in, boolean isInvisible) throws IOException {
194 int attrLength = in.readInt();
195 // Read Type Annotations Attr
196 int count = in.readShort();
197 ArrayList<TypeAnnotationData> tannots = new ArrayList<>(count);
198 TraceUtils.traceln(3, "CodeAttr: Runtime" +
199 (isInvisible ? "Inv" : "V") +
200 "isibleTypeAnnotation: attrlen=" +
201 attrLength + " num=" + count);
202 for (int index = 0; index < count; index++) {
203 TraceUtils.traceln("\t\t\t[" + index +"]:");
204 TypeAnnotationData tannot = new TypeAnnotationData(isInvisible, cls);
205 tannot.read(in);
206 tannots.add(tannot);
207 }
208 if (isInvisible) {
209 invisibleTypeAnnotations = tannots;
210 } else {
211 visibleTypeAnnotations = tannots;
212 }
213 }
214
215 /**
216 * read
217 * <p>
218 * read and resolve the code attribute data called from MethodData. precondition:
219 * NumFields has already been read from the stream.
220 */
221 public void read(DataInputStream in, int codeattrlen) throws IOException {
222
223 // Read the code in the Code Attribute
224 max_stack = in.readUnsignedShort();
225 max_locals = in.readUnsignedShort();
226 int codelen = in.readInt();
227 TraceUtils.traceln(3, "CodeAttr: Codelen=" + codelen +
228 " fulllen=" + codeattrlen +
229 " max_stack=" + max_stack +
230 " max_locals=" + max_locals);
231
232 // read the raw code bytes
233 code = new byte[codelen];
234 in.read(code, 0, codelen);
235
236 //read the trap table
237 readTrapTable(in);
238
239 // Read any attributes of the Code Attribute
240 int nattr = in.readUnsignedShort();
241 TraceUtils.traceln(3, "CodeAttr: add.attr:" + nattr);
242 for (int k = 0; k < nattr; k++) {
243 int name_cpx = in.readUnsignedShort();
244 // verify the Attrs name
245 ConstantPool.Constant name_const = cls.pool.getConst(name_cpx);
246 if (name_const != null && name_const.tag == ConstantPool.TAG.CONSTANT_UTF8) {
247 String attrname = cls.pool.getString(name_cpx);
248 TraceUtils.traceln(3, "CodeAttr: attr: " + attrname);
249 // process the attr
250 AttrTag attrtag = attrtag(attrname);
251 switch (attrtag) {
252 case ATT_LineNumberTable:
253 readLineNumTable(in);
254 break;
255 case ATT_LocalVariableTable:
256 readLocVarTable(in);
257 break;
258 case ATT_StackMap:
259 readStackMap(in);
260 break;
261 case ATT_StackMapTable:
262 readStackMapTable(in);
263 break;
264 case ATT_RuntimeVisibleTypeAnnotations:
265 case ATT_RuntimeInvisibleTypeAnnotations:
266 readTypeAnnotations(in, attrtag == ATT_RuntimeInvisibleTypeAnnotations);
267 break;
268 default:
269 AttrData attr = new AttrData(cls);
270 int attrlen = in.readInt(); // attr_length
271 attr.read(name_cpx, attrlen, in);
272 attrs.add(attr);
273 break;
274 }
275 }
276 }
277 }
278
279 /*========================================================*/
280 /* Code Resolution Methods */
281 private int checkForLabelRef(int pc) {
282 // throws IOException {
283 int opc = getUbyte(pc);
284 Opcode opcode = opcode(opc);
285 switch (opcode) {
286 case opc_tableswitch: {
287 int tb = align(pc + 1);
288 int default_skip = getInt(tb); /* default skip pamount */
289
290 int low = getInt(tb + 4);
291 int high = getInt(tb + 8);
292 int count = high - low;
293 for (int i = 0; i <= count; i++) {
294 get_iAtt(pc + getInt(tb + 12 + 4 * i)).referred = true;
295 }
296 get_iAtt(default_skip + pc).referred = true;
297 return tb - pc + 16 + count * 4;
298 }
299 case opc_lookupswitch: {
300 int tb = align(pc + 1);
301 int default_skip = getInt(tb); /* default skip pamount */
302
303 int npairs = getInt(tb + 4);
304 for (int i = 1; i <= npairs; i++) {
305 get_iAtt(pc + getInt(tb + 4 + i * 8)).referred = true;
306 }
307 get_iAtt(default_skip + pc).referred = true;
308 return tb - pc + (npairs + 1) * 8;
309 }
310 case opc_jsr:
311 case opc_goto:
312 case opc_ifeq:
313 case opc_ifge:
314 case opc_ifgt:
315 case opc_ifle:
316 case opc_iflt:
317 case opc_ifne:
318 case opc_if_icmpeq:
319 case opc_if_icmpne:
320 case opc_if_icmpge:
321 case opc_if_icmpgt:
322 case opc_if_icmple:
323 case opc_if_icmplt:
324 case opc_if_acmpeq:
325 case opc_if_acmpne:
326 case opc_ifnull:
327 case opc_ifnonnull:
328 get_iAtt(pc + getShort(pc + 1)).referred = true;
329 return 3;
330 case opc_jsr_w:
331 case opc_goto_w:
332 get_iAtt(pc + getInt(pc + 1)).referred = true;
333 return 5;
334 case opc_wide:
335 case opc_nonpriv:
336 case opc_priv:
337 int opc2 = (opcode.value() << 8) + getUbyte(pc + 1);
338 opcode = opcode(opc2);
339 }
340 try {
341 int opclen = opcode.length();
342 return opclen == 0 ? 1 : opclen; // bugfix for 4614404
343 } catch (ArrayIndexOutOfBoundsException e) {
344 return 1;
345 }
346 } // end checkForLabelRef
347
348 private void loadLabelTable() {
349 for (int pc = 0; pc < code.length; ) {
350 pc = pc + checkForLabelRef(pc);
351 }
352 }
353
354 private void loadLineNumTable() {
355 for (LineNumData entry : lin_num_tb) {
356 get_iAtt(entry.start_pc).lnum = entry.line_number;
357 }
358 }
359
360 private void loadStackMap() {
361 for (StackMapData entry : stack_map) {
362 get_iAtt(entry.start_pc).stackMapEntry = entry;
363 }
364 }
365
366 private void loadLocVarTable() {
367 for (LocVarData entry : loc_var_tb) {
368 get_iAtt(entry.start_pc).add_var(entry);
369 get_iAtt(entry.start_pc + entry.length).add_endvar(entry);
370 }
371 }
372
373 private void loadTrapTable() {
374 for (TrapData entry : trap_table) {
375 get_iAtt(entry.start_pc).add_trap(entry);
376 get_iAtt(entry.end_pc).add_endtrap(entry);
377 get_iAtt(entry.handler_pc).add_handler(entry);
378 }
379 }
380
381 /*========================================================*/
382 /* Print Methods */
383 private void PrintConstant(int cpx) {
384 out.print("\t");
385 cls.pool.PrintConstant(out, cpx);
386 }
387
388 private void PrintCommentedConstant(int cpx) {
389 out.print(commentString(cls.pool.ConstantStrValue(cpx)));
390 }
391
392 private int printInstr(int pc) {
393 boolean pr_cpx = meth.options.contains(Options.PR.CPX);
394 int opc = getUbyte(pc);
395 int opc2;
396 Opcode opcode = opcode(opc);
397 Opcode opcode2;
398 String mnem;
399 switch (opcode) {
400 case opc_nonpriv:
401 case opc_priv:
402 opc2 = getUbyte(pc + 1);
403 int finalopc = (opc << 8) + opc2;
404 opcode2 = opcode(finalopc);
405 if (opcode2 == null) {
406 // assume all (even nonexistent) priv and nonpriv instructions
407 // are 2 bytes long
408 mnem = opcode.parsekey() + " " + opc2;
409 } else {
410 mnem = opcode2.parsekey();
411 }
412 out.print(mnem);
413 return 2;
414 case opc_wide: {
415 opc2 = getUbyte(pc + 1);
416 int finalopcwide = (opc << 8) + opc2;
417 opcode2 = opcode(finalopcwide);
418 if (opcode2 == null) {
419 // nonexistent opcode - but we have to print something
420 out.print("bytecode " + opcode);
421 return 1;
422 } else {
423 mnem = opcode2.parsekey();
424 }
425 out.print(mnem + " " + getUShort(pc + 2));
426 if (opcode2 == Opcode.opc_iinc_w) {
427 out.print(", " + getShort(pc + 4));
428 return 6;
429 }
430 return 4;
431 }
432 }
433 mnem = opcode.parsekey();
434 if (mnem == null) {
435 // nonexistent opcode - but we have to print something
436 out.print("bytecode " + opcode);
437 return 1;
438 }
439 if (opcode.value() >= Opcode.opc_bytecode.value()) {
440 // pseudo opcodes should be printed as bytecodes
441 out.print("bytecode " + opcode);
442 return 1;
443 }
444 out.print(opcode.parsekey());
445 // TraceUtils.traceln("****** [CodeData.printInstr]: got an '" + opcode.parseKey() + "' [" + opc + "] instruction ****** ");
446 switch (opcode) {
447 case opc_aload:
448 case opc_astore:
449 case opc_fload:
450 case opc_fstore:
451 case opc_iload:
452 case opc_istore:
453 case opc_lload:
454 case opc_lstore:
455 case opc_dload:
456 case opc_dstore:
457 case opc_ret:
458 out.print("\t" + getUbyte(pc + 1));
459 return 2;
460 case opc_iinc:
461 out.print("\t" + getUbyte(pc + 1) + ", " + getbyte(pc + 2));
462 return 3;
463 case opc_tableswitch: {
464 int tb = align(pc + 1);
465 int default_skip = getInt(tb); /* default skip pamount */
466
467 int low = getInt(tb + 4);
468 int high = getInt(tb + 8);
469 int count = high - low;
470 out.print("{ //" + low + " to " + high);
471 for (int i = 0; i <= count; i++) {
472 out.print("\n\t\t" + (i + low) + ": " + meth.lP + (pc + getInt(tb + 12 + 4 * i)) + ";");
473 }
474 out.print("\n\t\tdefault: " + meth.lP + (default_skip + pc) + " }");
475 return tb - pc + 16 + count * 4;
476 }
477 case opc_lookupswitch: {
478 int tb = align(pc + 1);
479 int default_skip = getInt(tb);
480 int npairs = getInt(tb + 4);
481 out.print("{ //" + npairs);
482 for (int i = 1; i <= npairs; i++) {
483 out.print("\n\t\t" + getInt(tb + i * 8) + ": " + meth.lP + (pc + getInt(tb + 4 + i * 8)) + ";");
484 }
485 out.print("\n\t\tdefault: " + meth.lP + (default_skip + pc) + " }");
486 return tb - pc + (npairs + 1) * 8;
487 }
488 case opc_newarray:
489 int tp = getUbyte(pc + 1);
490 BasicType type = basictype(tp);
491 switch (type) {
492 case T_BOOLEAN:
493 out.print(" boolean");
494 break;
495 case T_BYTE:
496 out.print(" byte");
497 break;
498 case T_CHAR:
499 out.print(" char");
500 break;
501 case T_SHORT:
502 out.print(" short");
503 break;
504 case T_INT:
505 out.print(" int");
506 break;
507 case T_LONG:
508 out.print(" long");
509 break;
510 case T_FLOAT:
511 out.print(" float");
512 break;
513 case T_DOUBLE:
514 out.print(" double");
515 break;
516 case T_CLASS:
517 out.print(" class");
518 break;
519 default:
520 out.print(" BOGUS TYPE:" + type);
521 }
522 return 2;
523 case opc_ldc_w:
524 case opc_ldc2_w: {
525 // added printing of the tag: Method/Interface to clarify
526 // interpreting CONSTANT_MethodHandle_info:reference_kind
527 // Example: ldc_w Dynamic REF_invokeStatic:Method CondyIndy.condy_bsm
528 cls.pool.setPrintTAG(true);
529 int index = getUShort(pc + 1);
530 if (pr_cpx) {
531 out.print("\t#" + index + "; //");
532 }
533 PrintConstant(index);
534 cls.pool.setPrintTAG(false);
535 return 3;
536 }
537 case opc_anewarray:
538 case opc_instanceof:
539 case opc_checkcast:
540 case opc_new:
541 case opc_aconst_init:
542 case opc_putstatic:
543 case opc_getstatic:
544 case opc_putfield:
545 case opc_getfield:
546 case opc_withfield:
547 case opc_invokevirtual:
548 case opc_invokespecial:
549 case opc_invokestatic: {
550 int index = getUShort(pc + 1);
551 if (pr_cpx) {
552 out.print("\t#" + index + "; //");
553 }
554 PrintConstant(index);
555 return 3;
556 }
557 case opc_sipush:
558 out.print("\t" + getShort(pc + 1));
559 return 3;
560 case opc_bipush:
561 out.print("\t" + getbyte(pc + 1));
562 return 2;
563 case opc_ldc: {
564 // added printing of the tag: Method/Interface to clarify
565 // interpreting CONSTANT_MethodHandle_info:reference_kind
566 // Example: ldc Dynamic REF_invokeStatic:Method CondyIndy.condy_bsm
567 cls.pool.setPrintTAG(true);
568 int index = getUbyte(pc + 1);
569 if (pr_cpx) {
570 out.print("\t#" + index + "; //");
571 }
572 PrintConstant(index);
573 cls.pool.setPrintTAG(false);
574 return 2;
575 }
576 case opc_invokeinterface: {
577 int index = getUShort(pc + 1), nargs = getUbyte(pc + 3);
578 if (pr_cpx) {
579 out.print("\t#" + index + ", " + nargs + "; //");
580 PrintConstant(index);
581 } else {
582 PrintConstant(index);
583 out.print(", " + nargs); // args count
584 }
585 return 5;
586 }
587 case opc_invokedynamic: { // JSR-292
588 cls.pool.setPrintTAG(true);
589 int index = getUShort(pc + 1);
590 // getUbyte(pc + 3); // reserved byte
591 // getUbyte(pc + 4); // reserved byte
592 if (pr_cpx) {
593 out.print("\t#" + index + ";\t");
594 PrintCommentedConstant(index);
595 } else {
596 PrintConstant(index);
597 }
598 cls.pool.setPrintTAG(false);
599 return 5;
600 }
601 case opc_multianewarray: {
602 int index = getUShort(pc + 1), dimensions = getUbyte(pc + 3);
603 if (pr_cpx) {
604 out.print("\t#" + index + ", " + dimensions + "; //");
605 PrintConstant(index);
606 } else {
607 PrintConstant(index);
608 out.print(", " + dimensions); // dimensions count
609 }
610 return 4;
611 }
612 case opc_jsr:
613 case opc_goto:
614 case opc_ifeq:
615 case opc_ifge:
616 case opc_ifgt:
617 case opc_ifle:
618 case opc_iflt:
619 case opc_ifne:
620 case opc_if_icmpeq:
621 case opc_if_icmpne:
622 case opc_if_icmpge:
623 case opc_if_icmpgt:
624 case opc_if_icmple:
625 case opc_if_icmplt:
626 case opc_if_acmpeq:
627 case opc_if_acmpne:
628 case opc_ifnull:
629 case opc_ifnonnull:
630 out.print("\t" + meth.lP + (pc + getShort(pc + 1)));
631 return 3;
632 case opc_jsr_w:
633 case opc_goto_w:
634 out.print("\t" + meth.lP + (pc + getInt(pc + 1)));
635 return 5;
636 default:
637 return 1;
638 }
639 } // end printInstr
640
641 /**
642 * print
643 * <p>
644 * prints the code data to the current output stream. called from MethodData.
645 */
646 public void print() throws IOException {
647 if (!lin_num_tb.isEmpty()) {
648 loadLineNumTable();
649 }
650 if (stack_map != null) {
651 loadStackMap();
652 }
653 if (!meth.options.contains(Options.PR.PC)) {
654 loadLabelTable();
655 }
656 loadTrapTable();
657 if (!loc_var_tb.isEmpty()) {
658 loadLocVarTable();
659 }
660
661 out.println();
662 out.println("\tstack " + max_stack + " locals " + max_locals);
663
664 // Need to print ParamAnnotations here.
665 meth.printPAnnotations();
666
667 out.println(getIndentString() + "{");
668
669 iAtt iatt = iattrs.get(0);
670 for (int pc = 0; pc < code.length; ) {
671 if (iatt != null) {
672 iatt.printBegins(); // equ. print("\t");
673 } else {
674 out.print("\t");
675 }
676 if (meth.options.contains(Options.PR.PC)) {
677 out.print(pc + ":\t");
678 } else if ((iatt != null) && iatt.referred) {
679 out.print(meth.lP + pc + ":\t");
680 } else {
681 out.print("\t");
682 }
683 if (iatt != null) {
684 iatt.printStackMap();
685 }
686 pc = pc + printInstr(pc);
687 out.println(";");
688 iatt = iattrs.get(pc);
689 if (iatt != null) {
690 iatt.printEnds();
691 }
692 }
693 // the right brace can be labelled:
694 if (iatt != null) {
695 iatt.printBegins(); // equ. print("\t");
696 if (iatt.referred) {
697 out.print(meth.lP + code.length + ":\t");
698 }
699 iatt.printStackMap();
700 out.println();
701 }
702 // print TypeAnnotations
703 if (visibleTypeAnnotations != null) {
704 out.println();
705 for (TypeAnnotationData visad : visibleTypeAnnotations) {
706 visad.print(out, getIndentString());
707 out.println();
708 }
709 }
710 if (invisibleTypeAnnotations != null) {
711 for (TypeAnnotationData invisad : invisibleTypeAnnotations) {
712 invisad.print(out, getIndentString());
713 out.println();
714 }
715 }
716 // end of code
717 out.println(getIndentString() + "}");
718 }
719
720
721 public static class LocVarData {
722
723 short start_pc, length, name_cpx, sig_cpx, slot;
724
725 public LocVarData(DataInputStream in) throws IOException {
726 start_pc = in.readShort();
727 length = in.readShort();
728 name_cpx = in.readShort();
729 sig_cpx = in.readShort();
730 slot = in.readShort();
731 }
732 }
733
734 /* Code Data inner classes */
735 class LineNumData {
736
737 short start_pc, line_number;
738
739 public LineNumData(DataInputStream in) throws IOException {
740 start_pc = in.readShort();
741 line_number = in.readShort();
742 }
743 }
744
745 }