1 /*
   2  * Copyright (c) 2009, 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.jdec;
  24 
  25 import org.openjdk.asmtools.common.Module;
  26 import org.openjdk.asmtools.asmutils.StringUtils;
  27 import org.openjdk.asmtools.jasm.Modifiers;
  28 import org.openjdk.asmtools.jcoder.JcodTokens;
  29 import org.openjdk.asmtools.util.I18NResourceBundle;
  30 
  31 import java.awt.event.KeyEvent;
  32 import java.io.DataInputStream;
  33 import java.io.EOFException;
  34 import java.io.IOException;
  35 import java.io.PrintWriter;
  36 
  37 import static java.lang.String.format;
  38 import static org.openjdk.asmtools.jasm.Tables.*;
  39 import static org.openjdk.asmtools.jasm.Tables.AnnotElemType.AE_UNKNOWN;
  40 import static org.openjdk.asmtools.jasm.TypeAnnotationTypes.*;
  41 
  42 /**
  43  * Class data of the Java Decoder
  44  */
  45 class ClassData {
  46 
  47     private byte[] types;
  48     private Object[] cpool;
  49     private int CPlen;
  50     private NestedByteArrayInputStream countedin;
  51     private DataInputStream in;
  52     private PrintWriter out;
  53     private int[] cpe_pos;
  54     private boolean printDetails;
  55     private String entityType = "";
  56     private String entityName = "";
  57 
  58     public static I18NResourceBundle i18n
  59             = I18NResourceBundle.getBundleForClass(Main.class);
  60 
  61     ClassData(DataInputStream dis, int printFlags, PrintWriter out) throws IOException {
  62         byte[] buf = new byte[dis.available()];
  63         try {
  64             if (dis.read(buf) <= 0)
  65                 throw new IOException("The file is empty");
  66         } finally {
  67             dis.close();
  68         }
  69         countedin = new NestedByteArrayInputStream(buf);
  70         in = new DataInputStream(countedin);
  71         this.out = out;
  72         printDetails = ((printFlags & 1) == 1);
  73     }
  74 
  75     /*========================================================*/
  76     private static final char[] hexTable = {
  77             '0', '1', '2', '3', '4', '5', '6', '7',
  78             '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
  79     };
  80 
  81     private String toHex(long val, int width) {
  82         StringBuilder s = new StringBuilder();
  83         for (int i = width * 2 - 1; i >= 0; i--) {
  84             s.append(hexTable[((int) (val >> (4 * i))) & 0xF]);
  85         }
  86         return "0x" + s.toString();
  87     }
  88 
  89     private String toHex(long val) {
  90         int width;
  91         for (width = 8; width > 0; width--) {
  92             if ((val >> (width - 1) * 8) != 0) {
  93                 break;
  94             }
  95         }
  96         return toHex(val, width);
  97     }
  98 
  99     private void printByteHex(PrintWriter out, int b) {
 100         out.print(hexTable[(b >> 4) & 0xF]);
 101         out.print(hexTable[b & 0xF]);
 102     }
 103 
 104     private void printBytes(PrintWriter out, DataInputStream in, int len)
 105             throws IOException {
 106         try {
 107             for (int i = 0; i < len; i++) {
 108                 if (i % 8 == 0) {
 109                     out_print("0x");
 110                 }
 111                 printByteHex(out, in.readByte());
 112                 if (i % 8 == 7) {
 113                     out.println(";");
 114                 }
 115             }
 116         } finally {
 117             if (len % 8 != 0) {
 118                 out.println(";");
 119             }
 120         }
 121     }
 122 
 123     private void printRestOfBytes() {
 124         for (int i = 0; ; i++) {
 125             try {
 126                 byte b = in.readByte();
 127                 if (i % 8 == 0) {
 128                     out_print("0x");
 129                 }
 130                 printByteHex(out, b);
 131                 if (i % 8 == 7) {
 132                     out.print(";\n");
 133                 }
 134             } catch (IOException e) {
 135                 return;
 136             }
 137         }
 138     }
 139 
 140     private void printUtf8InfoIndex(int index, String indexName) {
 141         String name = (String) cpool[index];
 142         out_print("#" + index + "; // ");
 143         if (printDetails) {
 144             out.println(String.format("%-16s",indexName) + " : " + name);
 145         } else {
 146             out.println(indexName);
 147         }
 148     }
 149 
 150     /*========================================================*/
 151     private int shift = 0;
 152 
 153     private void out_begin(String s) {
 154         for (int i = 0; i < shift; i++) {
 155             out.print("  ");
 156         }
 157         out.println(s);
 158         shift++;
 159     }
 160 
 161     private void out_print(String s) {
 162         for (int i = 0; i < shift; i++) {
 163             out.print("  ");
 164         }
 165         out.print(s);
 166     }
 167 
 168     private void out_println(String s) {
 169         for (int i = 0; i < shift; i++) {
 170             out.print("  ");
 171         }
 172         out.println(s);
 173     }
 174 
 175     private void out_end(String s) {
 176         shift--;
 177         for (int i = 0; i < shift; i++) {
 178             out.print("  ");
 179         }
 180         out.println(s);
 181     }
 182 
 183     private String startArray(int length) {
 184         return "[" + (printDetails ? Integer.toString(length) : "") + "]";
 185     }
 186 
 187     private void startArrayCmt(int length, String comment) {
 188         out_begin(startArray(length) + format(" {%s", comment == null ? "" : " // " + comment));
 189     }
 190 
 191     private void startArrayCmtB(int length, String comment) {
 192         out_begin(startArray(length) + format("b {%s", comment == null ? "" : " // " + comment));
 193     }
 194 
 195     /*========================================================*/
 196     private void readCP(DataInputStream in) throws IOException {
 197         int length = in.readUnsignedShort();
 198         CPlen = length;
 199         traceln(i18n.getString("jdec.trace.CP_len", length));
 200         types = new byte[length];
 201         cpool = new Object[length];
 202         cpe_pos = new int[length];
 203         for (int i = 1; i < length; i++) {
 204             byte btag;
 205             int v1;
 206             long lv;
 207             cpe_pos[i] = countedin.getPos();
 208             btag = in.readByte();
 209             traceln(i18n.getString("jdec.trace.CP_entry", i, btag));
 210             types[i] = btag;
 211             ConstType tg = tag(btag);
 212             switch (tg) {
 213                 case CONSTANT_UTF8:
 214                     cpool[i] = in.readUTF();
 215                     break;
 216                 case CONSTANT_INTEGER:
 217                     v1 = in.readInt();
 218                     cpool[i] = v1;
 219                     break;
 220                 case CONSTANT_FLOAT:
 221                     v1 = Float.floatToIntBits(in.readFloat());
 222                     cpool[i] = v1;
 223                     break;
 224                 case CONSTANT_LONG:
 225                     lv = in.readLong();
 226                     cpool[i] = lv;
 227                     i++;
 228                     break;
 229                 case CONSTANT_DOUBLE:
 230                     lv = Double.doubleToLongBits(in.readDouble());
 231                     cpool[i] = lv;
 232                     i++;
 233                     break;
 234                 case CONSTANT_CLASS:
 235                 case CONSTANT_STRING:
 236                 case CONSTANT_MODULE:
 237                 case CONSTANT_PACKAGE:
 238                     v1 = in.readUnsignedShort();
 239                     cpool[i] = v1;
 240                     break;
 241                 case CONSTANT_INTERFACEMETHOD:
 242                 case CONSTANT_FIELD:
 243                 case CONSTANT_METHOD:
 244                 case CONSTANT_NAMEANDTYPE:
 245                     cpool[i] = "#" + in.readUnsignedShort() + " #" + in.readUnsignedShort();
 246                     break;
 247                 case CONSTANT_DYNAMIC:
 248                 case CONSTANT_INVOKEDYNAMIC:
 249                     cpool[i] = in.readUnsignedShort() + "s #" + in.readUnsignedShort();
 250                     break;
 251                 case CONSTANT_METHODHANDLE:
 252                     cpool[i] = in.readUnsignedByte() + "b #" + in.readUnsignedShort();
 253                     break;
 254                 case CONSTANT_METHODTYPE:
 255                     cpool[i] = "#" + in.readUnsignedShort();
 256                     break;
 257                 default:
 258                     CPlen = i;
 259                     printCP(out);
 260                     out_println(toHex(btag, 1) + "; // invalid constant type: " + (int) btag + " for element " + i);
 261                     throw new ClassFormatError();
 262             }
 263         }
 264     }
 265 
 266     private void printCP(PrintWriter out) {
 267         int length = CPlen;
 268         startArrayCmt(length, "Constant Pool");
 269         out_println("; // first element is empty");
 270         try {
 271             int size;
 272             for (int i = 1; i < length; i = i + size) {
 273                 size = 1;
 274                 byte btag = types[i];
 275                 ConstType tg = tag(btag);
 276                 int pos = cpe_pos[i];
 277                 String tagstr;
 278                 String valstr;
 279                 int v1;
 280                 long lv;
 281                 if (tg != null) {
 282                     tagstr = tg.parseKey();
 283                 } else {
 284                     throw new Error("Can't get a tg representing the type of Constant in the Constant Pool at: " + i);
 285                 }
 286                 switch (tg) {
 287                     case CONSTANT_UTF8: {
 288                         tagstr = "Utf8";
 289                         valstr = StringUtils.Utf8ToString((String) cpool[i]);
 290                     }
 291                     break;
 292                     case CONSTANT_FLOAT:
 293                     case CONSTANT_INTEGER:
 294                         v1 = (Integer) cpool[i];
 295                         valstr = toHex(v1, 4);
 296                         break;
 297                     case CONSTANT_DOUBLE:
 298                     case CONSTANT_LONG:
 299                         lv = (Long) cpool[i];
 300                         valstr = toHex(lv, 8) + ";";
 301                         size = 2;
 302                         break;
 303                     case CONSTANT_CLASS:
 304                     case CONSTANT_MODULE:
 305                     case CONSTANT_PACKAGE:
 306                     case CONSTANT_STRING:
 307                         v1 = (Integer) cpool[i];
 308                         valstr = "#" + v1;
 309                         break;
 310                     case CONSTANT_INTERFACEMETHOD:
 311                     case CONSTANT_FIELD:
 312                     case CONSTANT_METHOD:
 313                     case CONSTANT_NAMEANDTYPE:
 314                     case CONSTANT_METHODHANDLE:
 315                     case CONSTANT_METHODTYPE:
 316                     case CONSTANT_DYNAMIC:
 317                     case CONSTANT_INVOKEDYNAMIC:
 318                         valstr = (String) cpool[i];
 319                         break;
 320                     default:
 321                         throw new Error("invalid constant type: " + (int) btag);
 322                 }
 323                 out_print(tagstr + " " + valstr + "; // #" + i);
 324                 if (printDetails) {
 325                     out_println(" at " + toHex(pos));
 326                 } else {
 327                     out.println();
 328                 }
 329             }
 330         } finally {
 331             out_end("} // Constant Pool");
 332             out.println();
 333         }
 334     }
 335 
 336     private String getStringPos() {
 337         return " at " + toHex(countedin.getPos());
 338     }
 339 
 340     private String getCommentPosCond() {
 341         if (printDetails) {
 342             return " // " + getStringPos();
 343         } else {
 344             return "";
 345         }
 346     }
 347 
 348     private void decodeCPXAttr(DataInputStream in, int len, String attrname, PrintWriter out) throws IOException {
 349         decodeCPXAttrM(in, len, attrname, out, 1);
 350     }
 351 
 352     private void decodeCPXAttrM(DataInputStream in, int len, String attrname, PrintWriter out, int expectedIndices) throws IOException {
 353         if (len != expectedIndices * 2) {
 354             out_println("// invalid length of " + attrname + " attr: " + len + " (should be " + (expectedIndices * 2) + ") > ");
 355             printBytes(out, in, len);
 356         } else {
 357             StringBuilder outputString = new StringBuilder();
 358             for (int k = 1; k <= expectedIndices; k++) {
 359                 outputString.append("#").append(in.readUnsignedShort()).append("; ");
 360                 if (k % 16 == 0) {
 361                     out_println(outputString.toString().replaceAll("\\s+$",""));
 362                     outputString = new StringBuilder();
 363                 }
 364             }
 365             if (outputString.length() > 0) {
 366                 out_println(outputString.toString().replaceAll("\\s+$",""));
 367             }
 368         }
 369     }
 370 
 371     private void printStackMap(DataInputStream in, int elementsNum) throws IOException {
 372         int num;
 373         if (elementsNum > 0) {
 374             num = elementsNum;
 375         } else {
 376             num = in.readUnsignedShort();
 377         }
 378         out.print(startArray(num) + (elementsNum > 0 ? "z" : "") + "{");
 379         try {
 380             for (int k = 0; k < num; k++) {
 381                 int maptype = in.readUnsignedByte();
 382                 StackMapType mptyp = stackMapType(maptype, out);
 383                 String maptypeImg;
 384                 if (printDetails) {
 385                     maptypeImg = maptype + "b";
 386                 } else {
 387                     try {
 388                         maptypeImg = mptyp.parsekey();
 389                     } catch (ArrayIndexOutOfBoundsException e) {
 390                         maptypeImg = "/* BAD TYPE: */ " + maptype + "b";
 391                     }
 392                 }
 393                 switch (mptyp) {
 394                     case ITEM_Object:
 395                     case ITEM_NewObject:
 396                         maptypeImg = maptypeImg + "," + in.readUnsignedShort();
 397                         break;
 398                     case ITEM_UNKNOWN:
 399                         maptypeImg = maptype + "b";
 400                         break;
 401                     default:
 402                 }
 403                 out.print(maptypeImg);
 404                 if (k < num - 1) {
 405                     out.print("; ");
 406                 }
 407             }
 408         } finally {
 409             out.print("}");
 410         }
 411     }
 412 
 413     /**
 414      * Processes 4.7.20 The RuntimeVisibleTypeAnnotations Attribute, 4.7.21 The RuntimeInvisibleTypeAnnotations Attribute
 415      * <code>type_annotation</code> structure.
 416      */
 417     private void decodeTargetTypeAndRefInfo(DataInputStream in) throws IOException {
 418         int tt = in.readUnsignedByte(); // [4.7.20] annotations[], type_annotation { u1 target_type; ...}
 419         ETargetType targetType = ETargetType.getTargetType(tt);
 420         if( targetType == null ) {
 421             throw new Error("Type annotation: invalid target_type(u1) " + tt);
 422         }
 423         ETargetInfo targetInfo = targetType.targetInfo();
 424         out_println(toHex(tt, 1) + ";  //  target_type: " + targetType.parseKey());
 425         switch (targetInfo) {
 426             case TYPEPARAM:          //[3.3.1] meth_type_param, class_type_param:
 427                 out_println(toHex(in.readUnsignedByte(), 1) + ";  //  param_index");
 428                 break;
 429             case SUPERTYPE:         //[3.3.2]  class_exts_impls
 430                 out_println(toHex(in.readUnsignedShort(), 2) + ";  //  type_index");
 431                 break;
 432             case TYPEPARAM_BOUND:   //[3.3.3]  class_type_param_bnds, meth_type_param_bnds
 433                 out_println(toHex(in.readUnsignedByte(), 1) + ";  //  param_index");
 434                 out_println(toHex(in.readUnsignedByte(), 1) + ";  //  bound_index");
 435                 break;
 436             case EMPTY:             //[3.3.4]  meth_receiver, meth_ret_type, field
 437                 // NOTE: reference_info is empty for this annotation's target
 438                 break;
 439             case METHODPARAM:       //[3.3.5]  meth_formal_param:
 440                 out_println(toHex(in.readUnsignedByte(), 1) + ";  //  parameter_index");
 441                 break;
 442             case EXCEPTION:         //[3.3.61]  throws_type
 443                 //KTL:  Updated index to UShort for JSR308 change
 444                 out_println(in.readUnsignedShort() + ";  //  type_index");
 445                 break;
 446             case LOCALVAR: //[3.3.7]  local_var, resource_var
 447             {
 448                 int lv_num = in.readUnsignedShort();
 449                 startArrayCmt(lv_num, "local_variables");
 450                 try {
 451                     for (int i = 0; i < lv_num; i++) {
 452                         out_println(in.readUnsignedShort() + " " + in.readUnsignedShort()
 453                                 + " " + in.readUnsignedShort() + ";" + getCommentPosCond());
 454                     }
 455                 } finally {
 456                     out_end("}");
 457                 }
 458             }
 459             break;
 460             case CATCH:             //[3.3.8]  exception_param
 461                 out_println(in.readUnsignedShort() + ";  //  exception_table_index");
 462                 break;
 463             case OFFSET:            //[3.3.9]  type_test (instanceof), obj_creat (new)
 464                 // constr_ref_receiver, meth_ref_receiver
 465                 out_println(in.readUnsignedShort() + ";  //  offset");
 466                 break;
 467             case TYPEARG:           //[3.3.10]  cast, constr_ref_typearg, meth_invoc_typearg
 468                 // constr_invoc_typearg, meth_ref_typearg
 469                 out_println(in.readUnsignedShort() + ";  //  offset");
 470                 out_println(toHex(in.readUnsignedByte(), 1) + ";  //  type_index");
 471                 break;
 472             default:                // should never happen
 473                 out_println(toHex(tt, 1) + "; // invalid target_info: " + tt);
 474                 throw new ClassFormatError();
 475         }
 476         // [4.7.20.2]
 477         int path_length = in.readUnsignedByte();  // type_path { u1 path_length; ...}
 478         startArrayCmtB(path_length, "type_paths");
 479         try {
 480             for (int i = 0; i < path_length; i++) {
 481                 // print the type_path elements
 482                 out_println("{ " + toHex(in.readUnsignedByte(), 1)  // { u1 type_path_kind;
 483                         + "; " + toHex(in.readUnsignedByte(), 1)    //   u1 type_argument_index; }
 484                         + "; } // type_path[" + i + "]");           // path[i]
 485             }
 486         } finally {
 487             out_end("}");
 488         }
 489     }
 490 
 491     private void decodeElementValue(DataInputStream in, PrintWriter out) throws IOException {
 492         out_begin("{  //  element_value");
 493         try {
 494             char tg = (char) in.readByte();
 495             AnnotElemType tag = annotElemType(tg);
 496             if (tag != AE_UNKNOWN) {
 497                 out_println("'" + tg + "';");
 498             }
 499             switch (tag) {
 500                 case AE_BYTE:
 501                 case AE_CHAR:
 502                 case AE_DOUBLE:
 503                 case AE_FLOAT:
 504                 case AE_INT:
 505                 case AE_LONG:
 506                 case AE_SHORT:
 507                 case AE_BOOLEAN:
 508                 case AE_STRING:
 509                     decodeCPXAttr(in, 2, "const_value_index", out);
 510                     break;
 511                 case AE_ENUM:
 512                     out_begin("{  //  enum_const_value");
 513                     decodeCPXAttr(in, 2, "type_name_index", out);
 514                     decodeCPXAttr(in, 2, "const_name_index", out);
 515                     out_end("}  //  enum_const_value");
 516                     break;
 517                 case AE_CLASS:
 518                     decodeCPXAttr(in, 2, "class_info_index", out);
 519                     break;
 520                 case AE_ANNOTATION:
 521                     decodeAnnotation(in, out);
 522                     break;
 523                 case AE_ARRAY:
 524                     int ev_num = in.readUnsignedShort();
 525                     startArrayCmt(ev_num, "array_value");
 526                     try {
 527                         for (int i = 0; i < ev_num; i++) {
 528                             decodeElementValue(in, out);
 529                             if (i < ev_num - 1) {
 530                                 out_println(";");
 531                             }
 532                         }
 533                     } finally {
 534                         out_end("}  //  array_value");
 535                     }
 536                     break;
 537                 case AE_UNKNOWN:
 538                 default:
 539                     String msg = "invalid element_value" + (isPrintableChar(tg) ? " tag type : " + tg : "");
 540                     out_println(toHex(tg, 1) + "; // " + msg);
 541                     throw new ClassFormatError(msg);
 542             }
 543         } finally {
 544             out_end("}  //  element_value");
 545         }
 546     }
 547 
 548     public boolean isPrintableChar(char c) {
 549         Character.UnicodeBlock block = Character.UnicodeBlock.of(c);
 550         return (!Character.isISOControl(c)) &&
 551                 c != KeyEvent.CHAR_UNDEFINED &&
 552                 block != null &&
 553                 block != Character.UnicodeBlock.SPECIALS;
 554     }
 555 
 556     private void decodeAnnotation(DataInputStream in, PrintWriter out) throws IOException {
 557         out_begin("{  //  annotation");
 558         try {
 559             decodeCPXAttr(in, 2, "field descriptor", out);
 560             int evp_num = in.readUnsignedShort();
 561             decodeElementValuePairs(evp_num, in, out);
 562         } finally {
 563             out_end("}  //  annotation");
 564         }
 565     }
 566 
 567     private void decodeElementValuePairs(int count, DataInputStream in, PrintWriter out) throws IOException {
 568         startArrayCmt(count, "element_value_pairs");
 569         try {
 570             for (int i = 0; i < count; i++) {
 571                 out_begin("{  //  element value pair");
 572                 try {
 573                     decodeCPXAttr(in, 2, "name of the annotation type element", out);
 574                     decodeElementValue(in, out);
 575                 } finally {
 576                     out_end("}  //  element value pair");
 577                     if (i < count - 1) {
 578                         out_println(";");
 579                     }
 580                 }
 581             }
 582         } finally {
 583             out_end("}  //  element_value_pairs");
 584         }
 585     }
 586 
 587     /**
 588      * component_info {     JEP 359 Record(Preview): class file 58.65535
 589      *     u2               name_index;
 590      *     u2               descriptor_index;
 591      *     u2               attributes_count;
 592      *     attribute_info attributes[attributes_count];
 593      * }
 594      *
 595      * or
 596      * field_info {
 597      *     u2             access_flags;
 598      *     u2             name_index;
 599      *     u2             descriptor_index;
 600      *     u2             attributes_count;
 601      *     attribute_info attributes[attributes_count];
 602      * }
 603      * or
 604      * method_info {
 605      *     u2             access_flags;
 606      *     u2             name_index;
 607      *     u2             descriptor_index;
 608      *     u2             attributes_count;
 609      *     attribute_info attributes[attributes_count];
 610      * }
 611      *
 612      */
 613     private void decodeInfo(DataInputStream in, PrintWriter out, String elementName, boolean hasAccessFlag) throws IOException {
 614         out_begin("{  // " + elementName + (printDetails ? getStringPos() : ""));
 615         try {
 616             if(hasAccessFlag) {
 617                 //  u2 access_flags;
 618                 out_println(toHex(in.readShort(), 2) + "; // access");
 619             }
 620             // u2 name_index
 621             printUtf8InfoIndex(in.readUnsignedShort(), "name_index");
 622             // u2 descriptor_index
 623             printUtf8InfoIndex(in.readUnsignedShort(), "descriptor_index");
 624             // u2 attributes_count;
 625             // attribute_info attributes[attributes_count]
 626             decodeAttrs(in, out);
 627         } finally {
 628             out_end("}");
 629         }
 630     }
 631 
 632     private void decodeTypeAnnotation(DataInputStream in, PrintWriter out) throws IOException {
 633         out_begin("{  //  type_annotation");
 634         try {
 635             decodeTargetTypeAndRefInfo(in);
 636             decodeCPXAttr(in, 2, "field descriptor", out);
 637             int evp_num = in.readUnsignedShort();
 638             decodeElementValuePairs(evp_num, in, out);
 639         } finally {
 640             out_end("}  //  type_annotation");
 641         }
 642     }
 643 
 644     private void decodeBootstrapMethod(DataInputStream in) throws IOException {
 645         out_begin("{  //  bootstrap_method");
 646         try {
 647             out_println("#" + in.readUnsignedShort() + "; // bootstrap_method_ref");
 648             int bm_args_cnt = in.readUnsignedShort();
 649             startArrayCmt(bm_args_cnt, "bootstrap_arguments");
 650             try {
 651                 for (int i = 0; i < bm_args_cnt; i++) {
 652                     out_println("#" + in.readUnsignedShort() + ";" + getCommentPosCond());
 653                 }
 654             } finally {
 655                 out_end("}  //  bootstrap_arguments");
 656             }
 657         } finally {
 658             out_end("}  //  bootstrap_method");
 659         }
 660     }
 661 
 662     private void decodeAttr(DataInputStream in, PrintWriter out) throws IOException {
 663         // Read one attribute
 664         String posComment = getStringPos();
 665         int name_cpx = in.readUnsignedShort(), btag, len;
 666 
 667         String AttrName = "";
 668         try {
 669             btag = types[name_cpx];
 670             ConstType tag = tag(btag);
 671 
 672             if (tag == ConstType.CONSTANT_UTF8) {
 673                 AttrName = (String) cpool[name_cpx];
 674             }
 675         } catch (ArrayIndexOutOfBoundsException ignored) {
 676         }
 677         AttrTag tg = attrtag(AttrName);
 678         String endingComment = AttrName;
 679         len = in.readInt();
 680         countedin.enter(len);
 681         try {
 682             if (printDetails) {
 683                 out_begin("Attr(#" + name_cpx + ", " + len + ") { // " + AttrName + posComment);
 684             } else {
 685                 out_begin("Attr(#" + name_cpx + ") { // " + AttrName);
 686             }
 687 
 688             switch (tg) {
 689                 case ATT_Code:
 690                     out_println(in.readUnsignedShort() + "; // max_stack");
 691                     out_println(in.readUnsignedShort() + "; // max_locals");
 692                     int code_len = in.readInt();
 693                     out_begin("Bytes" + startArray(code_len) + "{");
 694                     try {
 695                         printBytes(out, in, code_len);
 696                     } finally {
 697                         out_end("}");
 698                     }
 699                     int trap_num = in.readUnsignedShort();
 700                     startArrayCmt(trap_num, "Traps");
 701                     try {
 702                         for (int i = 0; i < trap_num; i++) {
 703                             out_println(in.readUnsignedShort() + " " +
 704                                     in.readUnsignedShort() + " " +
 705                                     in.readUnsignedShort() + " " +
 706                                     in.readUnsignedShort() + ";" +
 707                                     getCommentPosCond());
 708                         }
 709                     } finally {
 710                         out_end("} // end Traps");
 711                     }
 712                     // Read the attributes
 713                     decodeAttrs(in, out);
 714                     break;
 715 
 716                 case ATT_Exceptions:
 717                     int count = in.readUnsignedShort();
 718                     startArrayCmt(count, AttrName);
 719                     try {
 720                         for (int i = 0; i < count; i++) {
 721                             out_println("#" + in.readUnsignedShort() + ";" +
 722                                     getCommentPosCond());
 723                         }
 724                     } finally {
 725                         out_end("}");
 726                     }
 727                     break;
 728                 case ATT_LineNumberTable:
 729                     int ll_num = in.readUnsignedShort();
 730                     startArrayCmt(ll_num, "line_number_table");
 731                     try {
 732                         for (int i = 0; i < ll_num; i++) {
 733                             out_println(in.readUnsignedShort() + "  " +
 734                                     in.readUnsignedShort() + ";" +
 735                                     getCommentPosCond());
 736                         }
 737                     } finally {
 738                         out_end("}");
 739                     }
 740                     break;
 741                 case ATT_LocalVariableTable:
 742                 case ATT_LocalVariableTypeTable:
 743                     int lvt_num = in.readUnsignedShort();
 744                     startArrayCmt(lvt_num, AttrName);
 745                     try {
 746                         for (int i = 0; i < lvt_num; i++) {
 747                             out_println(in.readUnsignedShort() + " " +
 748                                     in.readUnsignedShort() + " " +
 749                                     in.readUnsignedShort() + " " +
 750                                     in.readUnsignedShort() + " " +
 751                                     in.readUnsignedShort() + ";" +
 752                                     getCommentPosCond());
 753                         }
 754                     } finally {
 755                         out_end("}");
 756                     }
 757                     break;
 758                 case ATT_InnerClasses:
 759                     int ic_num = in.readUnsignedShort();
 760                     startArrayCmt(ic_num, "classes");
 761                     try {
 762                         for (int i = 0; i < ic_num; i++) {
 763                             out_println("#" + in.readUnsignedShort() + " #" +
 764                                     in.readUnsignedShort() + " #" +
 765                                     in.readUnsignedShort() + " " +
 766                                     in.readUnsignedShort() + ";" + getCommentPosCond());
 767                         }
 768                     } finally {
 769                         out_end("}");
 770                     }
 771                     break;
 772                 case ATT_StackMap:
 773                     int e_num = in.readUnsignedShort();
 774                     startArrayCmt(e_num, "");
 775                     try {
 776                         for (int k = 0; k < e_num; k++) {
 777                             int start_pc = in.readUnsignedShort();
 778                             out_print("" + start_pc + ", ");
 779                             printStackMap(in, 0);
 780                             out.print(", ");
 781                             printStackMap(in, 0);
 782                             out.println(";");
 783                         }
 784                     } finally {
 785                         out_end("}");
 786                     }
 787                     break;
 788                 case ATT_StackMapTable:
 789                     int et_num = in.readUnsignedShort();
 790                     startArrayCmt(et_num, "");
 791                     try {
 792                         for (int k = 0; k < et_num; k++) {
 793                             int frame_type = in.readUnsignedByte();
 794                             StackMapFrameType ftype = stackMapFrameType(frame_type);
 795                             switch (ftype) {
 796                                 case SAME_FRAME:
 797                                     // type is same_frame;
 798                                     out_print("" + frame_type + "b");
 799                                     out.println("; // same_frame");
 800                                     break;
 801                                 case SAME_LOCALS_1_STACK_ITEM_FRAME:
 802                                     // type is same_locals_1_stack_item_frame
 803                                     out_print("" + frame_type + "b, ");
 804                                     // read additional single stack element
 805                                     printStackMap(in, 1);
 806                                     out.println("; // same_locals_1_stack_item_frame");
 807                                     break;
 808                                 case SAME_LOCALS_1_STACK_ITEM_EXTENDED_FRAME:
 809                                     // type is same_locals_1_stack_item_frame_extended
 810                                     int noffset = in.readUnsignedShort();
 811                                     out_print("" + frame_type + "b, " + noffset + ", ");
 812                                     // read additional single stack element
 813                                     printStackMap(in, 1);
 814                                     out.println("; // same_locals_1_stack_item_frame_extended");
 815                                     break;
 816                                 case CHOP_1_FRAME:
 817                                 case CHOP_2_FRAME:
 818                                 case CHOP_3_FRAME:
 819                                     // type is chop_frame
 820                                     int coffset = in.readUnsignedShort();
 821                                     out_print("" + frame_type + "b, " + coffset);
 822                                     out.println("; // chop_frame " + (251 - frame_type));
 823                                     break;
 824                                 case SAME_FRAME_EX:
 825                                     // type is same_frame_extended;
 826                                     int xoffset = in.readUnsignedShort();
 827                                     out_print("" + frame_type + "b, " + xoffset);
 828                                     out.println("; // same_frame_extended");
 829                                     break;
 830                                 case APPEND_FRAME:
 831                                     // type is append_frame
 832                                     int aoffset = in.readUnsignedShort();
 833                                     out_print("" + frame_type + "b, " + aoffset + ", ");
 834                                     // read additional locals
 835                                     printStackMap(in, frame_type - 251);
 836                                     out.println("; // append_frame " + (frame_type - 251));
 837                                     break;
 838                                 case FULL_FRAME:
 839                                     // type is full_frame
 840                                     int foffset = in.readUnsignedShort();
 841                                     out_print("" + frame_type + "b, " + foffset + ", ");
 842                                     printStackMap(in, 0);
 843                                     out.print(", ");
 844                                     printStackMap(in, 0);
 845                                     out.println("; // full_frame");
 846                                     break;
 847                             }
 848                         }
 849                     } finally {
 850                         out_end("}");
 851                     }
 852                     break;
 853                 case ATT_EnclosingMethod:
 854                     decodeCPXAttrM(in, len, AttrName, out, 2);
 855                     break;
 856                 case ATT_AnnotationDefault:
 857                     decodeElementValue(in, out);
 858                     break;
 859                 case ATT_RuntimeInvisibleAnnotations:
 860                 case ATT_RuntimeVisibleAnnotations:
 861                     int an_num = in.readUnsignedShort();
 862                     startArrayCmt(an_num, "annotations");
 863                     try {
 864                         for (int i = 0; i < an_num; i++) {
 865                             decodeAnnotation(in, out);
 866                             if (i < an_num - 1) {
 867                                 out_println(";");
 868                             }
 869                         }
 870                     } finally {
 871                         out_end("}");
 872                     }
 873                     break;
 874                 // 4.7.20 The RuntimeVisibleTypeAnnotations Attribute
 875                 // 4.7.21 The RuntimeInvisibleTypeAnnotations Attribute
 876                 case ATT_RuntimeInvisibleTypeAnnotations:
 877                 case ATT_RuntimeVisibleTypeAnnotations:
 878                     int ant_num = in.readUnsignedShort();
 879                     startArrayCmt(ant_num, "annotations");
 880                     try {
 881                         for (int i = 0; i < ant_num; i++) {
 882                             decodeTypeAnnotation(in, out);
 883                             if (i < ant_num - 1) {
 884                                 out_println(";");
 885                             }
 886                         }
 887                     } finally {
 888                         out_end("}");
 889                     }
 890                     break;
 891                 case ATT_RuntimeInvisibleParameterAnnotations:
 892                 case ATT_RuntimeVisibleParameterAnnotations:
 893                     int pm_num = in.readUnsignedByte();
 894                     startArrayCmtB(pm_num, "parameters");
 895                     try {
 896                         for (int k = 0; k < pm_num; k++) {
 897                             int anp_num = in.readUnsignedShort();
 898                             startArrayCmt(anp_num, "annotations");
 899                             try {
 900                                 for (int i = 0; i < anp_num; i++) {
 901                                     decodeAnnotation(in, out);
 902                                     if (k < anp_num - 1) {
 903                                         out_println(";");
 904                                     }
 905                                 }
 906                             } finally {
 907                                 out_end("}");
 908                             }
 909                             if (k < pm_num - 1) {
 910                                 out_println(";");
 911                             }
 912                         }
 913                     } finally {
 914                         out_end("}");
 915                     }
 916                     break;
 917                 case ATT_BootstrapMethods:
 918                     int bm_num = in.readUnsignedShort();
 919                     startArrayCmt(bm_num, "bootstrap_methods");
 920                     try {
 921                         for (int i = 0; i < bm_num; i++) {
 922                             decodeBootstrapMethod(in);
 923                             if (i < bm_num - 1) {
 924                                 out_println(";");
 925                             }
 926                         }
 927                     } finally {
 928                         out_end("}");
 929                     }
 930                     break;
 931                 case ATT_Module:
 932                     decodeModule(in);
 933                     break;
 934                 case ATT_TargetPlatform:
 935                     decodeCPXAttrM(in, len, AttrName, out, 3);
 936                     break;
 937                 case ATT_ModulePackages:
 938                     int p_num = in.readUnsignedShort();
 939                     startArrayCmt(p_num, null);
 940                     try {
 941                         decodeCPXAttrM(in, len - 2, AttrName, out, p_num);
 942                     } finally {
 943                         out_end("}");
 944                     }
 945                     break;
 946                 //  MethodParameters_attribute {
 947                 //    u2 attribute_name_index;
 948                 //    u4 attribute_length;
 949                 //    u1 parameters_count;
 950                 //    {   u2 name_index;
 951                 //        u2 access_flags;
 952                 //    } parameters[parameters_count];
 953                 //  }
 954                 case ATT_MethodParameters:
 955                     int pcount = in.readUnsignedByte();
 956                     startArrayCmtB(pcount, AttrName);
 957                     try {
 958                         for (int i = 0; i < pcount; i++) {
 959                             out_println("#" + in.readUnsignedShort() + "  " +
 960                                     toHex(in.readUnsignedShort(), 2) + ";" +
 961                                     getCommentPosCond());
 962                         }
 963                     } finally {
 964                         out_end("}");
 965                     }
 966                     break;
 967                 //  JEP 359 Record(Preview): class file 58.65535
 968                 //  Record_attribute {
 969                 //      u2 attribute_name_index;
 970                 //      u4 attribute_length;
 971                 //      u2 components_count;
 972                 //      component_info components[components_count];
 973                 //  }
 974                 case ATT_Record:
 975                     int ncomps = in.readUnsignedShort();
 976                     startArrayCmt(ncomps, "components");
 977                     try {
 978                         for (int i = 0; i < ncomps; i++) {
 979                             decodeInfo(in,out,"component",false);
 980                             if (i < ncomps - 1) {
 981                                 out_println(";");
 982                             }
 983                         }
 984                     } finally {
 985                         out_end("}");
 986                     }
 987                     break;
 988                 case ATT_ConstantValue:
 989                 case ATT_Signature:
 990                 case ATT_SourceFile:
 991                     decodeCPXAttr(in, len, AttrName, out);
 992                     break;
 993                     //  JEP 181 (Nest-based Access Control): class file 55.0
 994                     //  NestHost_attribute {
 995                     //    u2 attribute_name_index;
 996                     //    u4 attribute_length;
 997                     //    u2 host_class_index;
 998                     //  }
 999                 case ATT_NestHost:
1000                     decodeTypes(in, out, 1);
1001                     break;
1002                     //  JEP 181 (Nest-based Access Control): class file 55.0
1003                     //  NestMembers_attribute {
1004                     //    u2 attribute_name_index;
1005                     //    u4 attribute_length;
1006                     //    u2 number_of_classes;
1007                     //    u2 classes[number_of_classes];
1008                     //  }
1009                 case ATT_NestMembers:
1010                     //  JEP 360 (Sealed types): class file 59.65535
1011                     //  PermittedSubclasses_attribute {
1012                     //    u2 attribute_name_index;
1013                     //    u4 attribute_length;
1014                     //    u2 number_of_classes;
1015                     //    u2 classes[number_of_classes];
1016                     //  }
1017                 case ATT_PermittedSubclasses:
1018                     // Preload attribute has same format
1019                 case ATT_Preload:
1020                     int nsubtypes = in.readUnsignedShort();
1021                     startArrayCmt(nsubtypes, "classes");
1022                     try {
1023                         decodeTypes(in, out, nsubtypes);
1024                     } finally {
1025                         out_end("}");
1026                     }
1027                     break;
1028                 default:
1029                     printBytes(out, in, len);
1030                     if (AttrName == null) {
1031                         endingComment = "Attr(#" + name_cpx + ")";
1032                     }
1033             }
1034 
1035         } catch (EOFException e) {
1036             out.println("// ======== unexpected end of attribute array");
1037         } finally {
1038             int rest = countedin.available();
1039             if (rest > 0) {
1040                 out.println("// ======== attribute array started " + posComment + " has " + rest + " bytes more:");
1041                 printBytes(out, in, rest);
1042             }
1043             out_end("} // end " + endingComment);
1044             countedin.leave();
1045         }
1046     }
1047 
1048     private void decodeModuleStatement(String statementName, DataInputStream in) throws IOException {
1049         // u2 {exports|opens}_count
1050         int count = in.readUnsignedShort();
1051         startArrayCmt(count, statementName);
1052         try {
1053             for (int i = 0; i < count; i++) {
1054                 // u2 {exports|opens}_index; u2 {exports|opens}_flags
1055                 int index = in.readUnsignedShort();
1056                 int nFlags = in.readUnsignedShort();
1057                 String sFlags = printDetails ? Module.Modifier.getStatementFlags(nFlags) : "";
1058                 out_println("#" + index + " " + toHex(nFlags, 2) + (sFlags.isEmpty() ? "" : " // [ " + sFlags + " ]"));
1059                 int exports_to_count = in.readUnsignedShort();
1060                 startArrayCmt(exports_to_count, null);
1061                 try {
1062                     for (int j = 0; j < exports_to_count; j++) {
1063                         out_println("#" + in.readUnsignedShort() + ";");
1064                     }
1065                 } finally {
1066                     out_end("};");
1067                 }
1068             }
1069         } finally {
1070             out_end("} // " + statementName + "\n");
1071         }
1072     }
1073 
1074     private void decodeModule(DataInputStream in) throws IOException {
1075         //u2 module_name_index
1076         int index = in.readUnsignedShort();
1077         entityName = (String) cpool[(Integer) cpool[index]];
1078         out_print("#" + index + "; // ");
1079         if (printDetails) {
1080             out.println(String.format("%-16s","name_index") + " : " + entityName);
1081         } else {
1082             out.println("name_index");
1083         }
1084 
1085         // u2 module_flags
1086         int moduleFlags = in.readUnsignedShort();
1087         out_print(toHex(moduleFlags, 2) + "; // flags");
1088         if (printDetails) {
1089             out_print(" " + Module.Modifier.getModuleFlags(moduleFlags));
1090         }
1091         out.println();
1092 
1093         //u2 module_version
1094         int versionIndex = in.readUnsignedShort();
1095         out_println("#" + versionIndex + "; // version");
1096 
1097         // u2 requires_count
1098         int count = in.readUnsignedShort();
1099         startArrayCmt(count, "requires");
1100         try {
1101             for (int i = 0; i < count; i++) {
1102                 // u2 requires_index; u2 requires_flags; u2 requires_version_index
1103                 index = in.readUnsignedShort();
1104                 int nFlags = in.readUnsignedShort();
1105                 versionIndex = in.readUnsignedShort();
1106                 String sFlags = printDetails ? Module.Modifier.getStatementFlags(nFlags) : "";
1107                 out_println("#" + index + " " + toHex(nFlags, 2) + " #" + versionIndex + ";" + (sFlags.isEmpty() ? "" : " // " + sFlags));
1108             }
1109         } finally {
1110             out_end("} // requires\n");
1111         }
1112 
1113         decodeModuleStatement("exports", in);
1114 
1115         decodeModuleStatement("opens", in);
1116         // u2 uses_count
1117         count = in.readUnsignedShort();
1118         startArrayCmt(count, "uses");
1119         try {
1120             for (int i = 0; i < count; i++) {
1121                 // u2 uses_index
1122                 out_println("#" + in.readUnsignedShort() + ";");
1123             }
1124         } finally {
1125             out_end("} // uses\n");
1126         }
1127         count = in.readUnsignedShort(); // u2 provides_count
1128         startArrayCmt(count, "provides");
1129         try {
1130             for (int i = 0; i < count; i++) {
1131                 // u2 provides_index
1132                 out_println("#" + in.readUnsignedShort());
1133                 int provides_with_count = in.readUnsignedShort();
1134                 // u2 provides_with_count
1135                 startArrayCmt(provides_with_count, null);
1136                 try {
1137                     for (int j = 0; j < provides_with_count; j++) {
1138                         // u2 provides_with_index;
1139                         out_println("#" + in.readUnsignedShort() + ";");
1140                     }
1141                 } finally {
1142                     out_end("};");
1143                 }
1144             }
1145         } finally {
1146             out_end("} // provides\n");
1147         }
1148     }
1149 
1150     private void decodeAttrs(DataInputStream in, PrintWriter out) throws IOException {
1151         // Read the attributes
1152         int attr_num = in.readUnsignedShort();
1153         startArrayCmt(attr_num, "Attributes");
1154         try {
1155             for (int i = 0; i < attr_num; i++) {
1156                 decodeAttr(in, out);
1157                 if (i + 1 < attr_num) {
1158                     out_println(";");
1159                 }
1160             }
1161         } finally {
1162             out_end("} // Attributes");
1163         }
1164     }
1165 
1166     private void decodeMembers(DataInputStream in, PrintWriter out, String groupName, String elementName) throws IOException {
1167         int count = in.readUnsignedShort();
1168         traceln(groupName + "=" + count);
1169         startArrayCmt(count, groupName);
1170         try {
1171             for (int i = 0; i < count; i++) {
1172                 decodeInfo(in,out,elementName,true);
1173                 if (i + 1 < count) {
1174                     out_println(";");
1175                 }
1176             }
1177         } finally {
1178             out_end("} // " + groupName);
1179             out.println();
1180         }
1181     }
1182 
1183     void decodeClass(String fileName) throws IOException {
1184         // Read the header
1185         try {
1186             int magic = in.readInt();
1187             int min_version = in.readUnsignedShort();
1188             int version = in.readUnsignedShort();
1189 
1190             // Read the constant pool
1191             readCP(in);
1192             short access = in.readShort(); // don't care about sign
1193             int this_cpx = in.readUnsignedShort();
1194 
1195             try {
1196                 entityName = (String) cpool[(Integer) cpool[this_cpx]];
1197                 if (entityName.equals("module-info")) {
1198                     entityType = "module";
1199                     entityName = "";
1200                 } else {
1201                     entityType = "class";
1202                 }
1203                 if (!entityName.isEmpty() && (JcodTokens.keyword_token_ident(entityName) != JcodTokens.Token.IDENT || JcodTokens.constValue(entityName) != -1)) {
1204                     // JCod can't parse a entityName matching a keyword or a constant value,
1205                     // then use the filename instead:
1206                     out_begin(String.format("file \"%s.class\" {", entityName));
1207                 } else {
1208                     out_begin(format("%s %s {", entityType, entityName));
1209                 }
1210             } catch (Exception e) {
1211                 entityName = fileName;
1212                 out.println("// " + e.getMessage() + " while accessing entityName");
1213                 out_begin(format("%s %s { // source file name", entityType, entityName));
1214             }
1215 
1216             out_print(toHex(magic, 4) + ";");
1217             if (magic != JAVA_MAGIC) {
1218                 out.print(" // wrong magic: 0x" + Integer.toString(JAVA_MAGIC, 16) + " expected");
1219             }
1220             out.println();
1221             out_println(min_version + "; // minor version");
1222             out_println(version + "; // version");
1223 
1224             // Print the constant pool
1225             printCP(out);
1226             out_println(toHex(access, 2) + "; // access" +
1227                     (printDetails ? " [" + (" " + Modifiers.accessString(access, CF_Context.CTX_CLASS).toUpperCase()).replaceAll(" (\\S)", " ACC_$1") + "]" : ""));
1228             out_println("#" + this_cpx + ";// this_cpx");
1229             int super_cpx = in.readUnsignedShort();
1230             out_println("#" + super_cpx + ";// super_cpx");
1231             traceln(i18n.getString("jdec.trace.access_thisCpx_superCpx", access, this_cpx, super_cpx));
1232             out.println();
1233 
1234             // Read the interfaces
1235             int numinterfaces = in.readUnsignedShort();
1236             traceln(i18n.getString("jdec.trace.numinterfaces", numinterfaces));
1237             startArrayCmt(numinterfaces, "Interfaces");
1238             try {
1239                 decodeTypes(in, out, numinterfaces);
1240             } finally {
1241                 out_end("} // Interfaces\n");
1242             }
1243             // Read the fields
1244             decodeMembers(in, out, "Fields", "field");
1245 
1246             // Read the methods
1247             decodeMembers(in, out, "Methods", "method");
1248 
1249             // Read the attributes
1250             decodeAttrs(in, out);
1251         } catch (EOFException ignored) {
1252         } catch (ClassFormatError err) {
1253             String msg = err.getMessage();
1254             out.println("//------- ClassFormatError" +
1255                     (msg == null || msg.isEmpty() ? "" : ": " + msg));
1256             printRestOfBytes();
1257         } finally {
1258             out_end(format("} // end %s %s", entityType, entityName));
1259         }
1260     } // end decodeClass()
1261 
1262     private void decodeTypes(DataInputStream in, PrintWriter out, int count) throws IOException {
1263         for (int i = 0; i < count; i++) {
1264             int type_cpx = in.readUnsignedShort();
1265             traceln(i18n.getString("jdec.trace.type", i, type_cpx));
1266             out_print("#" + type_cpx + ";");
1267             if (printDetails) {
1268                 String name = (String) cpool[(int)cpool[type_cpx]];
1269                 out.println(" // " + name + getStringPos());
1270             } else {
1271                 out.println();
1272             }
1273         }
1274     }
1275 
1276     /* ====================================================== */
1277     boolean DebugFlag = false;
1278 
1279     public void trace(String s) {
1280         if (!DebugFlag) {
1281             return;
1282         }
1283         System.out.print(s);
1284     }
1285 
1286     public void traceln(String s) {
1287         if (!DebugFlag) {
1288             return;
1289         }
1290         System.out.println(s);
1291     }
1292 }// end class ClassData