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_putstatic:
542             case opc_getstatic:
543             case opc_putfield:
544             case opc_getfield:
545             case opc_invokevirtual:
546             case opc_invokespecial:
547             case opc_invokestatic: {
548                 int index = getUShort(pc + 1);
549                 if (pr_cpx) {
550                     out.print("\t#" + index + "; //");
551                 }
552                 PrintConstant(index);
553                 return 3;
554             }
555             case opc_sipush:
556                 out.print("\t" + getShort(pc + 1));
557                 return 3;
558             case opc_bipush:
559                 out.print("\t" + getbyte(pc + 1));
560                 return 2;
561             case opc_ldc: {
562                 // added printing of the tag: Method/Interface to clarify
563                 // interpreting CONSTANT_MethodHandle_info:reference_kind
564                 // Example: ldc Dynamic REF_invokeStatic:Method CondyIndy.condy_bsm
565                 cls.pool.setPrintTAG(true);
566                 int index = getUbyte(pc + 1);
567                 if (pr_cpx) {
568                     out.print("\t#" + index + "; //");
569                 }
570                 PrintConstant(index);
571                 cls.pool.setPrintTAG(false);
572                 return 2;
573             }
574             case opc_invokeinterface: {
575                 int index = getUShort(pc + 1), nargs = getUbyte(pc + 3);
576                 if (pr_cpx) {
577                     out.print("\t#" + index + ",  " + nargs + "; //");
578                     PrintConstant(index);
579                 } else {
580                     PrintConstant(index);
581                     out.print(",  " + nargs); // args count
582                 }
583                 return 5;
584             }
585             case opc_invokedynamic: { // JSR-292
586                 cls.pool.setPrintTAG(true);
587                 int index = getUShort(pc + 1);
588                 // getUbyte(pc + 3); // reserved byte
589                 // getUbyte(pc + 4); // reserved byte
590                 if (pr_cpx) {
591                     out.print("\t#" + index + ";\t");
592                     PrintCommentedConstant(index);
593                 } else {
594                     PrintConstant(index);
595                 }
596                 cls.pool.setPrintTAG(false);
597                 return 5;
598             }
599             case opc_multianewarray: {
600                 int index = getUShort(pc + 1), dimensions = getUbyte(pc + 3);
601                 if (pr_cpx) {
602                     out.print("\t#" + index + ",  " + dimensions + "; //");
603                     PrintConstant(index);
604                 } else {
605                     PrintConstant(index);
606                     out.print(",  " + dimensions); // dimensions count
607                 }
608                 return 4;
609             }
610             case opc_jsr:
611             case opc_goto:
612             case opc_ifeq:
613             case opc_ifge:
614             case opc_ifgt:
615             case opc_ifle:
616             case opc_iflt:
617             case opc_ifne:
618             case opc_if_icmpeq:
619             case opc_if_icmpne:
620             case opc_if_icmpge:
621             case opc_if_icmpgt:
622             case opc_if_icmple:
623             case opc_if_icmplt:
624             case opc_if_acmpeq:
625             case opc_if_acmpne:
626             case opc_ifnull:
627             case opc_ifnonnull:
628                 out.print("\t" + meth.lP + (pc + getShort(pc + 1)));
629                 return 3;
630             case opc_jsr_w:
631             case opc_goto_w:
632                 out.print("\t" + meth.lP + (pc + getInt(pc + 1)));
633                 return 5;
634             default:
635                 return 1;
636         }
637     } // end printInstr
638 
639     /**
640      * print
641      * <p>
642      * prints the code data to the current output stream. called from MethodData.
643      */
644     public void print() throws IOException {
645         if (!lin_num_tb.isEmpty()) {
646             loadLineNumTable();
647         }
648         if (stack_map != null) {
649             loadStackMap();
650         }
651         if (!meth.options.contains(Options.PR.PC)) {
652             loadLabelTable();
653         }
654         loadTrapTable();
655         if (!loc_var_tb.isEmpty()) {
656             loadLocVarTable();
657         }
658 
659         out.println();
660         out.println("\tstack " + max_stack + " locals " + max_locals);
661 
662         // Need to print ParamAnnotations here.
663         meth.printPAnnotations();
664 
665         out.println(getIndentString() + "{");
666 
667         iAtt iatt = iattrs.get(0);
668         for (int pc = 0; pc < code.length; ) {
669             if (iatt != null) {
670                 iatt.printBegins(); // equ. print("\t");
671             } else {
672                 out.print("\t");
673             }
674             if (meth.options.contains(Options.PR.PC)) {
675                 out.print(pc + ":\t");
676             } else if ((iatt != null) && iatt.referred) {
677                 out.print(meth.lP + pc + ":\t");
678             } else {
679                 out.print("\t");
680             }
681             if (iatt != null) {
682                 iatt.printStackMap();
683             }
684             pc = pc + printInstr(pc);
685             out.println(";");
686             iatt = iattrs.get(pc);
687             if (iatt != null) {
688                 iatt.printEnds();
689             }
690         }
691         // the right brace can be labelled:
692         if (iatt != null) {
693             iatt.printBegins(); // equ. print("\t");
694             if (iatt.referred) {
695                 out.print(meth.lP + code.length + ":\t");
696             }
697             iatt.printStackMap();
698             out.println();
699         }
700         // print TypeAnnotations
701         if (visibleTypeAnnotations != null) {
702             out.println();
703             for (TypeAnnotationData visad : visibleTypeAnnotations) {
704                 visad.print(out, getIndentString());
705                 out.println();
706             }
707         }
708         if (invisibleTypeAnnotations != null) {
709             for (TypeAnnotationData invisad : invisibleTypeAnnotations) {
710                 invisad.print(out, getIndentString());
711                 out.println();
712             }
713         }
714         // end of code
715         out.println(getIndentString() + "}");
716     }
717 
718 
719     public static class LocVarData {
720 
721         short start_pc, length, name_cpx, sig_cpx, slot;
722 
723         public LocVarData(DataInputStream in) throws IOException {
724             start_pc = in.readShort();
725             length = in.readShort();
726             name_cpx = in.readShort();
727             sig_cpx = in.readShort();
728             slot = in.readShort();
729         }
730     }
731 
732     /* Code Data inner classes */
733     class LineNumData {
734 
735         short start_pc, line_number;
736 
737         public LineNumData(DataInputStream in) throws IOException {
738             start_pc = in.readShort();
739             line_number = in.readShort();
740         }
741     }
742 
743 }