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 }