1 /*
  2  * Copyright (c) 2009, 2017, 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.jcdec;
 24 
 25 import static org.openjdk.asmtools.jcoder.JcodTokens.*;
 26 import org.openjdk.asmtools.jdis.uEscWriter;
 27 import org.openjdk.asmtools.util.I18NResourceBundle;
 28 import org.openjdk.asmtools.util.ProductInfo;
 29 import java.io.DataInputStream;
 30 import java.io.FileInputStream;
 31 import java.io.FileNotFoundException;
 32 import java.io.IOException;
 33 import java.io.PrintWriter;
 34 import java.util.ArrayList;
 35 
 36 /**
 37  * Main program of the JavaCard DeCoder
 38  *
 39  */
 40 public class Main {
 41 
 42     /*-------------------------------------------------------- */
 43     /* Main Fields */
 44     /**
 45      * Name of the program.
 46      */
 47     String program;
 48 
 49     public static final I18NResourceBundle i18n
 50             = I18NResourceBundle.getBundleForClass(Main.class);
 51     /**
 52      * The stream where error message are printed.
 53      */
 54     PrintWriter out;
 55     boolean DebugFlag = false;
 56     boolean printDetails = false;
 57     int shift = 0;
 58     private static final char hexTable[] = {
 59         '0', '1', '2', '3', '4', '5', '6', '7',
 60         '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
 61     };
 62     /*-------------------------------------------------------- */
 63 
 64     static String toHex(long val, int width) {
 65         StringBuffer s = new StringBuffer();
 66         for (int i = width * 2 - 1; i >= 0; i--) {
 67             s.append(hexTable[((int) (val >> (4 * i))) & 0xF]);
 68         }
 69         return "0x" + s.toString();
 70     }
 71 
 72     static String toHex(long val) {
 73         int width;
 74         for (width = 8; width > 0; width--) {
 75             if ((val >> (width - 1) * 8) != 0) {
 76                 break;
 77             }
 78         }
 79         return toHex(val, width);
 80     }
 81 
 82     void printByteHex(PrintWriter out, int b) {
 83         out.print(hexTable[(b >> 4) & 0xF]);
 84         out.print(hexTable[b & 0xF]);
 85     }
 86 
 87     /*========================================================*/
 88     void out_begin(String s) {
 89         for (int i = 0; i < shift; i++) {
 90             out.print("  ");
 91         }
 92         out.println(s);
 93         shift++;
 94     }
 95 
 96     void out_print(String s) {
 97         for (int i = 0; i < shift; i++) {
 98             out.print("  ");
 99         }
100         out.print(s);
101     }
102 
103     void out_println(String s) {
104         for (int i = 0; i < shift; i++) {
105             out.print("  ");
106         }
107         out.println(s);
108     }
109 
110     void out_end(String s) {
111         shift--;
112         for (int i = 0; i < shift; i++) {
113             out.print("  ");
114         }
115         out.println(s);
116     }
117 
118     String startArray(int length) {
119         return "[" + (printDetails ? Integer.toString(length) : "") + "]";
120     }
121 
122     void printBytes(DataInputStream in, int len) throws IOException {
123         try {
124             for (int i = 0; i < len; i++) {
125                 if (i % 8 == 0) {
126                     out_print("0x");
127                 }
128                 printByteHex(out, in.readByte());
129                 if (i % 8 == 7) {
130                     out.println(";");
131                 }
132             }
133         } finally {
134             if (len % 8 != 0) {
135                 out.println(";");
136             }
137         }
138     }
139 
140     /*========================================================*/
141     static final int EXPORT_MAGIC = 0x00FACADE;
142     static final int HEADER_MAGIC = 0xDECAFFED;
143     static String[] compNames = {
144         "Header",
145         "Directory",
146         "Applet",
147         "Import",
148         "ConstantPool",
149         "Class",
150         "Method",
151         "StaticField",
152         "RefLocation",
153         "Export",
154         "Descriptor"
155     };
156 
157     static String compName(int compNum) {
158         try {
159             return compNames[compNum - 1];
160         } catch (ArrayIndexOutOfBoundsException e) {
161             return "tag " + compNum + "???";
162         }
163     }
164     String[] cPoolStrings;
165 
166     void decodeAttr(DataInputStream in) throws IOException {
167         int name_cpx = in.readUnsignedShort(), len = in.readInt();
168         String AttrName = null;
169         String endingComment = "Attr(#" + name_cpx + ")";
170         try {
171             endingComment = AttrName = cPoolStrings[name_cpx];
172         } catch (ArrayIndexOutOfBoundsException e) {
173         }
174         if (printDetails) {
175             out_begin("Attr(#" + name_cpx + ", " + len + ") { // " + AttrName);
176         } else {
177             out_begin("Attr(#" + name_cpx + ") { // " + AttrName);
178         }
179         if (AttrName == null) {
180             printBytes(in, len);
181         } else if (AttrName.equals("ConstantValue")) {
182             if (len != 2) {
183                 out_println("// invalid length of ConstantValue attr: " + len + " (should be 2)");
184                 printBytes(in, len);
185             } else {
186                 out_println("#" + in.readUnsignedShort() + ";");
187             }
188         } else {
189             printBytes(in, len);
190         }
191         out_end("} // end " + endingComment);
192     }
193 
194     void decodeExp(String inpName) throws IOException {
195         DataInputStream in = new DataInputStream(new FileInputStream(inpName));
196         out_println("file " + inpName);
197         out_begin("{  // export file");
198 
199         int magic = in.readInt();
200         out_print(toHex(magic, 4) + ";  // ");
201         if (magic != EXPORT_MAGIC) {
202             out.print("wrong magic: 0x" + Integer.toString(EXPORT_MAGIC, 16) + " expected");
203         } else {
204             out_print("magic");
205         }
206         out.println();
207         out_println(in.readUnsignedByte() + "b;  // minor version");
208         out_println(in.readUnsignedByte() + "b;  // major version");
209 
210         int cp_count = in.readUnsignedShort();
211         cPoolStrings = new String[cp_count];
212         out_begin(startArray(cp_count) + " { //  Constant Pool");
213         for (int i = 0; i < cp_count; i++) {
214             int tag = in.readUnsignedByte();
215             ConstType tg = constType(tag);
216             switch (tg) {
217                 case CONSTANT_UTF8:
218                     out_print("Utf8 \"");
219 
220                     StringBuffer sb = new StringBuffer();
221                     String s = in.readUTF();
222                     cPoolStrings[i] = s;
223                     for (int k = 0; k < s.length(); k++) {
224                         char c = s.charAt(k);
225                         switch (c) {
226                             case '\t':
227                                 sb.append('\\').append('t');
228                                 break;
229                             case '\n':
230                                 sb.append('\\').append('n');
231                                 break;
232                             case '\r':
233                                 sb.append('\\').append('r');
234                                 break;
235                             case '\"':
236                                 sb.append('\\').append('\"');
237                                 break;
238                             default:
239                                 sb.append(c);
240                         }
241                     }
242                     out.println(sb.append("\"; // #").append(i).toString());
243                     break;
244 
245                 case CONSTANT_INTEGER:
246                     out_println("int " + toHex(in.readInt(), 4) + "; // #" + i);
247                     break;
248 
249                 case CONSTANT_CLASS:
250                     out_println("class #" + in.readUnsignedShort() + "; // #" + i);
251                     break;
252 
253                 case CONSTANT_JAVACARD_PACKAGE:
254                     out_begin("package { // #" + i);
255                     out_println(toHex(in.readUnsignedByte(), 1) + ";  // flags");
256                     out_println("#" + in.readUnsignedShort() + "; // name");
257                     out_println(in.readUnsignedByte() + "b;  // minor version");
258                     out_println(in.readUnsignedByte() + "b;  // major version");
259                     int aid_len = in.readUnsignedByte();
260                     out_begin("Bytes" + startArray(aid_len) + "b {");
261                     printBytes(in, aid_len);
262                     out_end("};"); // Bytes[]
263                     out_end("};"); // package info
264                     break;
265 
266                 default:
267                     throw new Error("invalid constant type: " + (int) tag);
268             }
269         }
270         ;
271         out_end("} // Constant pool");
272         out_println("#" + in.readUnsignedShort() + ";  // this package");
273         int class_count = in.readUnsignedByte();
274         out_begin(startArray(class_count) + "b { //  classes");
275         for (int i = 0; i < class_count; i++) {
276             out_begin("{ // class " + i);
277 
278             out_println(in.readUnsignedByte() + "b; // token");
279 
280             int flags = in.readUnsignedShort();
281             out_print("0x");
282             printByteHex(out, flags >> 8);
283             printByteHex(out, flags);
284             out.println("; // flags");
285 
286             out_println("#" + in.readUnsignedShort() + ";  // this class");
287 
288             int sup_count = in.readUnsignedShort();
289             out_begin(startArray(sup_count) + " { // supers");
290             for (int k = 0; k < sup_count; k++) {
291                 out_println("#" + in.readUnsignedShort() + ";");
292             }
293             out_end("} // supers");
294 
295             int int_count = in.readUnsignedByte();
296             out_begin(startArray(int_count) + "b { // interfaces");
297             for (int k = 0; k < int_count; k++) {
298                 out_println("#" + in.readUnsignedShort() + ";");
299             }
300             out_end("} // interfaces");
301 
302             int field_count = in.readUnsignedShort();
303             out_begin(startArray(field_count) + " { // fields");
304             for (int k = 0; k < field_count; k++) {
305                 out_begin("{ // field " + k);
306                 out_println(in.readUnsignedByte() + "b; // token");
307 
308                 int f_flags = in.readUnsignedShort();
309                 out_print("0x");
310                 printByteHex(out, f_flags >> 8);
311                 printByteHex(out, f_flags);
312                 out.println("; // flags");
313 
314                 out_println("#" + in.readUnsignedShort() + ";  // this field name");
315                 out_println("#" + in.readUnsignedShort() + ";  // this field descriptor");
316 
317                 int attr_count = in.readUnsignedShort();
318                 out_begin(startArray(attr_count) + " { // Attributes");
319                 for (int ai = 0; ai < attr_count; ai++) {
320                     decodeAttr(in);
321                 }
322                 out_end("} // Attributes");
323                 out_end("};");
324             }
325             out_end("} // fields");
326 
327             int mth_count = in.readUnsignedShort();
328             out_begin(startArray(mth_count) + " { // methods");
329             for (int k = 0; k < mth_count; k++) {
330                 out_begin("{ // method " + k);
331                 out_println(in.readUnsignedByte() + "b; // token");
332 
333                 int mth_flags = in.readUnsignedShort();
334                 out_print("0x");
335                 printByteHex(out, mth_flags >> 8);
336                 printByteHex(out, mth_flags);
337                 out.println("; // flags");
338 
339                 out_println("#" + in.readUnsignedShort() + ";  // this method name");
340                 out_println("#" + in.readUnsignedShort() + ";  // this method descriptor");
341                 out_end("};");
342             }
343             out_end("} // methods");
344             out_end("};");
345         }
346         out_end("} // classes");
347         endComponent(in);
348     }
349 
350     DataInputStream beginComponent(String inpName) throws IOException {
351         DataInputStream in = new DataInputStream(new FileInputStream(inpName));
352         out_println("file " + inpName);
353 
354         int tag = in.readUnsignedByte();
355         out_print("Component(" + tag);
356         int size = in.readUnsignedShort();
357         if (printDetails) {
358             out.print(", " + size);
359         }
360         out_begin(") { // " + compName(tag));
361         return in;
362     }
363 
364     void endComponent(DataInputStream in) throws IOException {
365         out_end("};"); // Component
366         int avail = in.available();
367         if (avail > 0) {
368             out.println("=========== extra bytes:");
369             for (int k = 0; k < 8; k++) {
370                 printBytes(in, avail >= 8 ? 8 : avail);
371                 avail = in.available();
372                 if (avail == 0) {
373                     break;
374                 }
375             }
376             if (avail > 0) {
377                 out.println("  there is also " + avail + " bytes available");
378             }
379         }
380         in.close();
381     }
382 
383     ArrayList<Integer> methodsLengths = null;
384     ArrayList<Integer> methodsOffsets = null;
385 
386     void decodeHeader(String inpName) throws IOException {
387         DataInputStream in = beginComponent(inpName);
388 
389         int magic = in.readInt();
390         out_print(toHex(magic, 4) + ";  // ");
391         if (magic != HEADER_MAGIC) {
392             out.print("wrong magic: 0x" + Integer.toString(HEADER_MAGIC, 16) + " expected");
393         } else {
394             out_print("magic");
395         }
396         out.println();
397         out_println(in.readUnsignedByte() + "b;  // minor version");
398         out_println(in.readUnsignedByte() + "b;  // major version");
399         out_println(toHex(in.readUnsignedByte(), 1) + ";  // flags");
400 
401         out_begin("{  // package info");
402         out_println(in.readUnsignedByte() + "b;  // minor version");
403         out_println(in.readUnsignedByte() + "b;  // major version");
404         int aid_len = in.readUnsignedByte();
405         out_begin("Bytes" + startArray(aid_len) + "b {");
406         printBytes(in, aid_len);
407         out_end("};"); // Bytes[]
408         out_end("};"); // package info
409         endComponent(in);
410     }
411 
412     void decodeDirectory(String inpName) throws IOException {
413         DataInputStream in = beginComponent(inpName);
414 
415         int i;
416         out_begin("{  // component sizes");
417         for (i = 0; i < 11; i++) {
418             out_println(in.readUnsignedShort() + ";  // " + (i + 1));
419         }
420         out_end("};");
421 
422         out_begin("{  // static field size");
423         out_println(in.readUnsignedShort() + ";  // image size");
424         out_println(in.readUnsignedShort() + ";  // array init count");
425         out_println(in.readUnsignedShort() + ";  // array init size");
426         out_end("};");
427 
428         out_println(in.readUnsignedByte() + "b;  // import count");
429         out_println(in.readUnsignedByte() + "b;  // applet count");
430 
431         int custom_count = in.readUnsignedByte();
432         out_begin(startArray(custom_count) + "b { // custom components");
433         for (i = 0; i < custom_count; i++) {
434             out_print("Comp(" + in.readUnsignedByte());  // tag;
435             int size2 = in.readUnsignedShort();
436             if (printDetails) {
437                 out_print(", " + size2);
438             }
439             out_begin(") {");
440             int aid_len = in.readUnsignedByte();
441             out_begin("Bytes" + startArray(aid_len) + "b {");
442             printBytes(in, aid_len);
443             out_end("};");
444             out_end("};");
445         }
446         out_end("};");
447 
448         endComponent(in);
449     }
450 
451     void decodeApplet(String inpName) throws IOException {
452         DataInputStream in = beginComponent(inpName);
453 
454         int applet_count = in.readUnsignedByte();
455         out_begin(startArray(applet_count) + "b { // applets");
456         for (int i = 0; i < applet_count; i++) {
457             out_begin("{ // applet " + i);
458             int aid_len = in.readUnsignedByte();
459             out_begin("Bytes" + startArray(aid_len) + "b {");
460             printBytes(in, aid_len);
461             out_end("};"); // Bytes[]
462             out_println(in.readUnsignedShort() + ";  // install method offset");
463             out_end("};"); // applet
464         }
465         out_end("};"); // applets
466         endComponent(in);
467     }
468 
469     void decodeImport(String inpName) throws IOException {
470         DataInputStream in = beginComponent(inpName);
471 
472         int package_count = in.readUnsignedByte();
473         out_begin(startArray(package_count) + "b { //  packages");
474         for (int i = 0; i < package_count; i++) {
475             out_begin("{ // package " + i);
476             out_println(in.readUnsignedByte() + "b;  // minor version");
477             out_println(in.readUnsignedByte() + "b;  // major version");
478             int aid_len = in.readUnsignedByte();
479             out_begin("Bytes" + startArray(aid_len) + "b {");
480             printBytes(in, aid_len);
481             out_end("};"); // Bytes[]
482             out_end("};"); // package info
483         }
484         out_end("};"); //  package info
485         endComponent(in);
486     }
487 
488     static String[] refNames = {
489         "Classref",
490         "InstanceFieldref",
491         "VirtualMethodref",
492         "SuperMethodref",
493         "StaticFieldref",
494         "StaticMethodref"
495     };
496 
497     void decodeConstantPool(String inpName) throws IOException {
498         DataInputStream in = beginComponent(inpName);
499 
500         int items_count = in.readUnsignedShort();
501         out_begin(startArray(items_count) + " { //  items");
502         for (int i = 0; i < items_count; i++) {
503             int tag = in.readUnsignedByte();
504             int info1 = in.readUnsignedByte(),
505                     info2 = in.readUnsignedByte(),
506                     info3 = in.readUnsignedByte();
507             out_print(tag + "b ");
508             if ((tag > 0) && (tag <= 6)) {
509                 if ((info1 & 0x80) == 0) {
510                     if (tag <= 4) {
511                         out_print(((info1 << 8) | info2) + " " + info3 + "b;");
512                     } else {
513                         out_print(info1 + "b " + ((info2 << 8) | info3) + ";");
514                     }
515                     out.print(" // internal ");
516                 } else {
517                     out.print(info1 + "b " + info2 + "b " + info3 + "b;");
518                     out.print(" // external ");
519                 }
520                 out.println(refNames[tag - 1]);
521             } else {
522                 out.print(info1 + "b " + info2 + "b " + info3 + "b;");
523                 out.println(" // unknown tag ");
524             }
525         }
526         out_end("};"); //  CP array
527         endComponent(in);
528     }
529 
530     void printClassref(DataInputStream in) throws IOException {
531         int info1 = in.readUnsignedByte(),
532                 info2 = in.readUnsignedByte();
533         if ((info1 & 0x80) == 0) {
534             out_print(((info1 << 8) | info2) + ";");
535             out_print(" // internal ");
536         } else {
537             out_print(info1 + "b " + info2 + "b;");
538             out_print(" // external ");
539         }
540         out_println(" Classref ");
541     }
542 
543     void decodeClass(String inpName) throws IOException {
544         DataInputStream in = beginComponent(inpName);
545 
546         for (int i = 0; in.available() > 0; i++) {
547             out_begin("{ // class " + i);
548             int bitfield = in.readUnsignedByte();
549             int interface_count = bitfield & 0x0F;
550             out_print("0x");
551             printByteHex(out, bitfield);
552             out.println("; // bitfield");
553             if ((bitfield & 0x80) != 0) {
554                 // interface
555                 for (int k = 0; k < interface_count; k++) {
556                     printClassref(in);
557                 }
558             } else {
559                 // class
560                 printClassref(in);
561                 out_println(in.readUnsignedByte() + "b;  // declared instance size");
562                 out_println(in.readUnsignedByte() + "b;  // first reference token");
563                 out_println(in.readUnsignedByte() + "b;  // reference count");
564                 out_println(in.readUnsignedByte() + "b;  // public method table base");
565                 int pumrc = in.readUnsignedByte();
566                 out_println(pumrc + "b;  // public method table count");
567                 out_println(in.readUnsignedByte() + "b;  // package method table base");
568                 int pamrc = in.readUnsignedByte();
569                 out_println(pamrc + "b;  // package method table count");
570                 out_begin("{ // public method table");
571                 for (int k = 0; k < pumrc; k++) {
572                     out_println(in.readUnsignedShort() + ";");
573                 }
574                 out_end("};");
575                 out_begin("{ // package method table");
576                 for (int k = 0; k < pamrc; k++) {
577                     out_println(in.readUnsignedShort() + ";");
578                 }
579                 out_end("};");
580                 out_begin("{ // implemented interfaces");
581                 for (int k = 0; k < interface_count; k++) {
582                     out_begin("{ // interface " + k);
583                     printClassref(in);
584                     int count = in.readUnsignedByte();
585                     out_begin("Bytes" + startArray(count) + "b {");
586                     printBytes(in, count);
587                     out_end("};"); // Bytes[]
588                     out_end("};");
589                 }
590                 out_end("};");
591             }
592             out_end("};");
593         }
594         endComponent(in);
595     }
596 
597     void decodeDescriptor(String inpName) throws IOException {
598         DataInputStream in = beginComponent(inpName);
599 
600         methodsLengths = new ArrayList<>();
601         methodsOffsets = new ArrayList<>();
602         int class_count = in.readUnsignedByte();
603         out_begin(startArray(class_count) + "b { // classes");
604         for (int c = 0; c < class_count; c++) {
605             out_begin("{ // class " + c);
606             out_println(in.readUnsignedByte() + "b; // token");
607             out_print("0x");
608             printByteHex(out, in.readUnsignedByte());
609             out.println("; // flags");
610             printClassref(in);
611             int icount = in.readUnsignedByte();
612             out_println(icount + "b; // interface count");
613             int fcount = in.readUnsignedShort();
614             out_println(fcount + "; // field count");
615             int mcount = in.readUnsignedShort();
616             out_println(mcount + "; // method count");
617             if (icount != 0) {
618                 out_begin("{ // interfaces");
619                 for (int i = 0; i < icount; i++) {
620                     printClassref(in);
621                 }
622                 out_end("};");
623             }
624             for (int i = 0; i < fcount; i++) {
625                 out_begin("{ // field " + i);
626                 out_println(in.readUnsignedByte() + "b; // token");
627                 int flags = in.readUnsignedByte();
628                 out_print("0x");
629                 printByteHex(out, flags);
630                 out.println("; // flags");
631                 if ((flags & 0x08) == 0) {
632                     printClassref(in);
633                     out_println(in.readUnsignedByte() + "b; // token");
634                 } else { // static field
635                     int info1 = in.readUnsignedByte(),
636                             info2 = in.readUnsignedByte(),
637                             info3 = in.readUnsignedByte();
638                     if ((info1 & 0x80) == 0) {
639                         out_print(info1 + "b " + ((info2 << 8) | info3) + ";");
640                         out.println(" // internal field");
641                     } else {
642                         out.print(info1 + "b " + info2 + "b " + info3 + "b;");
643                         out.println(" // external field");
644                     }
645                 }
646                 int type = in.readUnsignedShort();
647                 if ((type & 0x8000) == 0) {
648                     out_println(type + "; // reference type");
649                 } else {
650                     out_print("0x");
651                     printByteHex(out, type >> 8);
652                     printByteHex(out, type);
653                     out.println("; // primitive type");
654                 }
655                 out_end("};");
656             }
657             for (int i = 0; i < mcount; i++) {
658                 int token = in.readUnsignedByte();
659                 int flags = in.readUnsignedByte();
660                 int m_offset = in.readUnsignedShort();
661                 int t_offset = in.readUnsignedShort();
662                 int bytecode_count = in.readUnsignedShort();
663                 if (m_offset != 0) {
664                     out_begin("{ // method " + i + " (" + methodsLengths.size() + ")");
665                     methodsLengths.add(bytecode_count);
666                     methodsOffsets.add(m_offset);
667                 } else {
668                     out_begin("{ // method " + i);
669                 }
670                 out_println(token + "b; // token");
671                 out_print("0x");
672                 printByteHex(out, flags);
673                 out.println("; // flags");
674                 out_println(m_offset + "; // method offset");
675                 out_println(t_offset + "; // type offset");
676                 out_println(bytecode_count + "; // bytecode count");
677                 out_println(in.readUnsignedShort() + "; // exception handler count");
678                 out_println(in.readUnsignedShort() + "; // exception handler index");
679                 out_end("};");
680             }
681             out_end("};"); // class i
682         }
683         out_end("}; // classes");
684 
685         int cp_count = in.readUnsignedShort();
686         out_begin(startArray(cp_count) + " { // constant pool types");
687         for (int i = 0; i < cp_count; i++) {
688             int type = in.readUnsignedShort();
689             if (type == 0xFFFF) {
690                 out_println("0xFFFF;");
691             } else {
692                 out_println(type + "; ");
693             }
694         }
695         out_end("}; // constant pool types");
696 
697         out_begin("{ // type descriptors");
698         for (int i = 0; in.available() > 0; i++) {
699             int nibble_count = in.readUnsignedByte();
700             out_print(nibble_count + "b; ");
701             printBytes(in, (nibble_count + 1) / 2);
702         }
703         out_end("}; // type descriptors");
704         endComponent(in);
705     }
706 
707     void decodeMethod(String inpName) throws IOException {
708         DataInputStream in = beginComponent(inpName);
709 
710         int handler_count = in.readUnsignedByte();
711         out_begin(startArray(handler_count) + "b { // exception handlers");
712         for (int i = 0; i < handler_count; i++) {
713             out_print(in.readUnsignedShort() + ", ");
714             int bitfield = in.readUnsignedShort();
715             out.print("0x");
716             printByteHex(out, bitfield >> 8);
717             printByteHex(out, bitfield);
718             out.print(", " + in.readUnsignedShort() + ", ");
719             out.println(in.readUnsignedShort() + "; // handler " + i);
720         }
721         out_end("};"); // handlers
722 
723         if (methodsLengths == null) {
724             out.println("// Descriptor.cap absent - methods not printed");
725         } else {
726             int f_offset = 1 + handler_count * 8;
727             for (int i = 0; i < methodsLengths.size(); i++) {
728                 out_begin("{ // method " + i);
729                 int m_offset = methodsOffsets.get(i);
730                 if (m_offset != f_offset) {
731                     out.println("file offset=" + f_offset + " but m_offset=" + m_offset);
732                     break;
733                 }
734                 int bitfield = in.readUnsignedByte();
735                 if ((bitfield & 0x80) == 0) {
736                     out_print("0x");
737                     printByteHex(out, bitfield);
738                     out.println("; // flags, max_stack");
739                     out_print("0x");
740                     printByteHex(out, in.readUnsignedByte());
741                     out.println("; // nargs, max_locals");
742                     f_offset += 2;
743                 } else {
744                     out_print("0x");
745                     printByteHex(out, bitfield);
746                     out.println("; // flags, padding");
747                     out_println(in.readUnsignedByte() + "b; // max_stack");
748                     out_println(in.readUnsignedByte() + "b; // nargs");
749                     out_println(in.readUnsignedByte() + "b; // max_locals");
750                     f_offset += 4;
751                 }
752                 int bytecode_count = methodsLengths.get(i);
753                 out_begin("{ // bytecodes");
754                 printBytes(in, bytecode_count);
755                 f_offset += bytecode_count;
756                 out_end("};");
757                 out_end("};");
758             }
759         }
760 
761         endComponent(in);
762     }
763 
764     void decodeStaticField(String inpName) throws IOException {
765         DataInputStream in = beginComponent(inpName);
766 
767         int image_size = in.readUnsignedShort();
768         out_println(image_size + "; // image size");
769         int reference_count = in.readUnsignedShort();
770         out_println(reference_count + "; // reference count");
771         int array_init_count = in.readUnsignedShort();
772         out_begin(startArray(array_init_count) + " { // array_init_info");
773         for (int i = 0; i < array_init_count; i++) {
774             out_println(in.readUnsignedByte() + "b // type ");
775             int count = in.readUnsignedShort();
776             out_begin("Bytes" + startArray(count) + "s { // values");
777             printBytes(in, count);
778             out_end("};"); // Bytes[]
779         }
780         out_end("};"); // array_init_info
781         int default_value_count = in.readUnsignedShort();
782         out_println(default_value_count + "; // default value count");
783         int non_default_value_count = in.readUnsignedShort();
784         out_begin("Bytes" + startArray(non_default_value_count) + "s { // non default values");
785         printBytes(in, non_default_value_count);
786         out_end("};"); // Bytes[]
787 
788         endComponent(in);
789     }
790 
791     void decodeRefLocation(String inpName) throws IOException {
792         DataInputStream in = beginComponent(inpName);
793 
794         int byte_index_count = in.readUnsignedShort();
795         out_begin("Bytes" + startArray(byte_index_count) + "s { // offsets to byte indices");
796         printBytes(in, byte_index_count);
797         out_end("};"); // Bytes[]
798 
799         byte_index_count = in.readUnsignedShort();
800         out_begin("Bytes" + startArray(byte_index_count) + "s { // offsets to byte2 indices");
801         printBytes(in, byte_index_count);
802         out_end("};"); // Bytes[]
803 
804         endComponent(in);
805     }
806 
807     void decodeExport(String inpName) throws IOException {
808         DataInputStream in = beginComponent(inpName);
809         int class_count = in.readUnsignedByte();
810         out_begin(startArray(class_count) + "b { // classes");
811         for (int i = 0; i < class_count; i++) {
812             out_begin("{ // class " + i);
813             out_println(in.readUnsignedShort() + "; // class offset");
814             int fcount = in.readUnsignedByte();
815             out_println(fcount + "b; // static field count");
816             int mcount = in.readUnsignedByte();
817             out_println(mcount + "b; // static method count");
818             out_begin("{ // static field offsets");
819             for (int j = 0; j < fcount; j++) {
820                 out_println(in.readUnsignedShort() + "; // field " + j + " offset");
821             }
822             out_end("};");
823             out_begin("{ // static method offsets");
824             for (int j = 0; j < mcount; j++) {
825                 out_println(in.readUnsignedShort() + "; // method " + j + " offset");
826             }
827             out_end("};");
828             out_end("};"); // class i
829         }
830         out_end("};"); // classes
831         endComponent(in);
832     }
833     /*========================================================*/
834 
835     /**
836      * Constructor.
837      */
838     public Main(PrintWriter out, String program) {
839         this.out = out;
840         this.program = program;
841     }
842 
843     public void error(String msg) {
844         out.println(program + ": " + msg);
845     }
846 
847     /**
848      * Usage
849      */
850     public void usage() {
851         out.println(i18n.getString("jcdec.usage"));
852         out.println(i18n.getString("jcdec.opt.g"));
853         out.println(i18n.getString("jcdec.opt.version"));
854     }
855 
856     /**
857      * Run the decoder
858      */
859     public synchronized boolean decode(String argv[]) {
860 //      int flags = F_WARNINGS;
861         long tm = System.currentTimeMillis();
862         ArrayList<String> vargs = new ArrayList<>();
863         ArrayList<String> vj = new ArrayList<>();
864         boolean nowrite = false;
865         int addOptions = 0;
866 
867         // Parse arguments
868         for (int i = 0; i < argv.length; i++) {
869             String arg = argv[i];
870             if (arg.equals("-g")) {
871                 printDetails = true;
872                 vargs.add(arg);
873             } else if (arg.equals("-v")) {
874                 DebugFlag = true;
875                 vargs.add(arg);
876                 out.println("arg[" + i + "]=" + argv[i] + "/verbose");
877             } else if (arg.equals("-version")) {
878                 out.println(ProductInfo.FULL_VERSION);
879             } else if (arg.startsWith("-")) {
880 //out.println("arg["+i+"]="+argv[i]+"/invalid flag");
881                 error(i18n.getString("jcdec.error.invalid_flag", arg));
882                 usage();
883                 return false;
884             } else {
885                 vargs.add(arg);
886                 vj.add(arg);
887             }
888         }
889 
890         if (vj.isEmpty()) {
891             usage();
892             return false;
893         }
894 
895 //        String[] names = new String[vj.size()];
896 //        vj.copyInto(names);
897         String[] names = null;
898         names = vj.toArray(names);
899 decode:
900         for (int k = 0; k < names.length; k++) {
901             String inpname = names[k];
902             try {
903                 if (inpname.endsWith(".cap")) {
904                     String shortName = inpname.substring(0, inpname.length() - 4);
905                     if (shortName.endsWith("Header")) {
906                         decodeHeader(inpname);
907                     } else if (shortName.endsWith("Directory")) {
908                         decodeDirectory(inpname);
909                     } else if (shortName.endsWith("Applet")) {
910                         decodeApplet(inpname);
911                     } else if (shortName.endsWith("Import")) {
912                         decodeImport(inpname);
913                     } else if (shortName.endsWith("ConstantPool")) {
914                         decodeConstantPool(inpname);
915                     } else if (shortName.endsWith("Class")) {
916                         decodeClass(inpname);
917                     } else if (shortName.endsWith("Descriptor")) {
918                         decodeDescriptor(inpname);
919                     } else if (shortName.endsWith("Method")) {
920                         decodeMethod(inpname);
921                     } else if (shortName.endsWith("StaticField")) {
922                         decodeStaticField(inpname);
923                     } else if (shortName.endsWith("RefLocation")) {
924                         decodeRefLocation(inpname);
925                     } else if (shortName.endsWith("Export")) {
926                         decodeExport(inpname);
927                     } else {
928                         continue decode;
929                     }
930                     out.println("");
931                 } else if (inpname.endsWith(".exp")) {
932                     decodeExp(inpname);
933                     out.println("");
934                 }
935                 continue decode;
936             } catch (FileNotFoundException ee) {
937                 error(i18n.getString("jcdec.error.cannot_read", inpname));
938             } catch (Error ee) {
939                 ee.printStackTrace();
940                 error(i18n.getString("jcdec.error.fatal_error"));
941             } catch (Exception ee) {
942                 ee.printStackTrace();
943                 error(i18n.getString("jcdec.error.fatal_exception"));
944             }
945             return false;
946         }
947         return true;
948     }
949 
950     /**
951      * Main program
952      */
953     public static void main(String argv[]) {
954         Main decoder = new Main(new PrintWriter(new uEscWriter(System.out)), "jcdec");
955         System.exit(decoder.decode(argv) ? 0 : 1);
956     }
957 }