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