asmtools/src/org/openjdk/asmtools/jdec/ClassData.java

1292 lines
51 KiB
Java

/*
* Copyright (c) 2009, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.openjdk.asmtools.jdec;
import org.openjdk.asmtools.common.Module;
import org.openjdk.asmtools.asmutils.StringUtils;
import org.openjdk.asmtools.jasm.Modifiers;
import org.openjdk.asmtools.jcoder.JcodTokens;
import org.openjdk.asmtools.util.I18NResourceBundle;
import java.awt.event.KeyEvent;
import java.io.DataInputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.PrintWriter;
import static java.lang.String.format;
import static org.openjdk.asmtools.jasm.Tables.*;
import static org.openjdk.asmtools.jasm.Tables.AnnotElemType.AE_UNKNOWN;
import static org.openjdk.asmtools.jasm.TypeAnnotationTypes.*;
/**
* Class data of the Java Decoder
*/
class ClassData {
private byte[] types;
private Object[] cpool;
private int CPlen;
private NestedByteArrayInputStream countedin;
private DataInputStream in;
private PrintWriter out;
private int[] cpe_pos;
private boolean printDetails;
private String entityType = "";
private String entityName = "";
public static I18NResourceBundle i18n
= I18NResourceBundle.getBundleForClass(Main.class);
ClassData(DataInputStream dis, int printFlags, PrintWriter out) throws IOException {
byte[] buf = new byte[dis.available()];
try {
if (dis.read(buf) <= 0)
throw new IOException("The file is empty");
} finally {
dis.close();
}
countedin = new NestedByteArrayInputStream(buf);
in = new DataInputStream(countedin);
this.out = out;
printDetails = ((printFlags & 1) == 1);
}
/*========================================================*/
private static final char[] hexTable = {
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
};
private String toHex(long val, int width) {
StringBuilder s = new StringBuilder();
for (int i = width * 2 - 1; i >= 0; i--) {
s.append(hexTable[((int) (val >> (4 * i))) & 0xF]);
}
return "0x" + s.toString();
}
private String toHex(long val) {
int width;
for (width = 8; width > 0; width--) {
if ((val >> (width - 1) * 8) != 0) {
break;
}
}
return toHex(val, width);
}
private void printByteHex(PrintWriter out, int b) {
out.print(hexTable[(b >> 4) & 0xF]);
out.print(hexTable[b & 0xF]);
}
private void printBytes(PrintWriter out, DataInputStream in, int len)
throws IOException {
try {
for (int i = 0; i < len; i++) {
if (i % 8 == 0) {
out_print("0x");
}
printByteHex(out, in.readByte());
if (i % 8 == 7) {
out.println(";");
}
}
} finally {
if (len % 8 != 0) {
out.println(";");
}
}
}
private void printRestOfBytes() {
for (int i = 0; ; i++) {
try {
byte b = in.readByte();
if (i % 8 == 0) {
out_print("0x");
}
printByteHex(out, b);
if (i % 8 == 7) {
out.print(";\n");
}
} catch (IOException e) {
return;
}
}
}
private void printUtf8InfoIndex(int index, String indexName) {
String name = (String) cpool[index];
out_print("#" + index + "; // ");
if (printDetails) {
out.println(String.format("%-16s",indexName) + " : " + name);
} else {
out.println(indexName);
}
}
/*========================================================*/
private int shift = 0;
private void out_begin(String s) {
for (int i = 0; i < shift; i++) {
out.print(" ");
}
out.println(s);
shift++;
}
private void out_print(String s) {
for (int i = 0; i < shift; i++) {
out.print(" ");
}
out.print(s);
}
private void out_println(String s) {
for (int i = 0; i < shift; i++) {
out.print(" ");
}
out.println(s);
}
private void out_end(String s) {
shift--;
for (int i = 0; i < shift; i++) {
out.print(" ");
}
out.println(s);
}
private String startArray(int length) {
return "[" + (printDetails ? Integer.toString(length) : "") + "]";
}
private void startArrayCmt(int length, String comment) {
out_begin(startArray(length) + format(" {%s", comment == null ? "" : " // " + comment));
}
private void startArrayCmtB(int length, String comment) {
out_begin(startArray(length) + format("b {%s", comment == null ? "" : " // " + comment));
}
/*========================================================*/
private void readCP(DataInputStream in) throws IOException {
int length = in.readUnsignedShort();
CPlen = length;
traceln(i18n.getString("jdec.trace.CP_len", length));
types = new byte[length];
cpool = new Object[length];
cpe_pos = new int[length];
for (int i = 1; i < length; i++) {
byte btag;
int v1;
long lv;
cpe_pos[i] = countedin.getPos();
btag = in.readByte();
traceln(i18n.getString("jdec.trace.CP_entry", i, btag));
types[i] = btag;
ConstType tg = tag(btag);
switch (tg) {
case CONSTANT_UTF8:
cpool[i] = in.readUTF();
break;
case CONSTANT_INTEGER:
v1 = in.readInt();
cpool[i] = v1;
break;
case CONSTANT_FLOAT:
v1 = Float.floatToIntBits(in.readFloat());
cpool[i] = v1;
break;
case CONSTANT_LONG:
lv = in.readLong();
cpool[i] = lv;
i++;
break;
case CONSTANT_DOUBLE:
lv = Double.doubleToLongBits(in.readDouble());
cpool[i] = lv;
i++;
break;
case CONSTANT_CLASS:
case CONSTANT_STRING:
case CONSTANT_MODULE:
case CONSTANT_PACKAGE:
v1 = in.readUnsignedShort();
cpool[i] = v1;
break;
case CONSTANT_INTERFACEMETHOD:
case CONSTANT_FIELD:
case CONSTANT_METHOD:
case CONSTANT_NAMEANDTYPE:
cpool[i] = "#" + in.readUnsignedShort() + " #" + in.readUnsignedShort();
break;
case CONSTANT_DYNAMIC:
case CONSTANT_INVOKEDYNAMIC:
cpool[i] = in.readUnsignedShort() + "s #" + in.readUnsignedShort();
break;
case CONSTANT_METHODHANDLE:
cpool[i] = in.readUnsignedByte() + "b #" + in.readUnsignedShort();
break;
case CONSTANT_METHODTYPE:
cpool[i] = "#" + in.readUnsignedShort();
break;
default:
CPlen = i;
printCP(out);
out_println(toHex(btag, 1) + "; // invalid constant type: " + (int) btag + " for element " + i);
throw new ClassFormatError();
}
}
}
private void printCP(PrintWriter out) {
int length = CPlen;
startArrayCmt(length, "Constant Pool");
out_println("; // first element is empty");
try {
int size;
for (int i = 1; i < length; i = i + size) {
size = 1;
byte btag = types[i];
ConstType tg = tag(btag);
int pos = cpe_pos[i];
String tagstr;
String valstr;
int v1;
long lv;
if (tg != null) {
tagstr = tg.parseKey();
} else {
throw new Error("Can't get a tg representing the type of Constant in the Constant Pool at: " + i);
}
switch (tg) {
case CONSTANT_UTF8: {
tagstr = "Utf8";
valstr = StringUtils.Utf8ToString((String) cpool[i]);
}
break;
case CONSTANT_FLOAT:
case CONSTANT_INTEGER:
v1 = (Integer) cpool[i];
valstr = toHex(v1, 4);
break;
case CONSTANT_DOUBLE:
case CONSTANT_LONG:
lv = (Long) cpool[i];
valstr = toHex(lv, 8) + ";";
size = 2;
break;
case CONSTANT_CLASS:
case CONSTANT_MODULE:
case CONSTANT_PACKAGE:
case CONSTANT_STRING:
v1 = (Integer) cpool[i];
valstr = "#" + v1;
break;
case CONSTANT_INTERFACEMETHOD:
case CONSTANT_FIELD:
case CONSTANT_METHOD:
case CONSTANT_NAMEANDTYPE:
case CONSTANT_METHODHANDLE:
case CONSTANT_METHODTYPE:
case CONSTANT_DYNAMIC:
case CONSTANT_INVOKEDYNAMIC:
valstr = (String) cpool[i];
break;
default:
throw new Error("invalid constant type: " + (int) btag);
}
out_print(tagstr + " " + valstr + "; // #" + i);
if (printDetails) {
out_println(" at " + toHex(pos));
} else {
out.println();
}
}
} finally {
out_end("} // Constant Pool");
out.println();
}
}
private String getStringPos() {
return " at " + toHex(countedin.getPos());
}
private String getCommentPosCond() {
if (printDetails) {
return " // " + getStringPos();
} else {
return "";
}
}
private void decodeCPXAttr(DataInputStream in, int len, String attrname, PrintWriter out) throws IOException {
decodeCPXAttrM(in, len, attrname, out, 1);
}
private void decodeCPXAttrM(DataInputStream in, int len, String attrname, PrintWriter out, int expectedIndices) throws IOException {
if (len != expectedIndices * 2) {
out_println("// invalid length of " + attrname + " attr: " + len + " (should be " + (expectedIndices * 2) + ") > ");
printBytes(out, in, len);
} else {
StringBuilder outputString = new StringBuilder();
for (int k = 1; k <= expectedIndices; k++) {
outputString.append("#").append(in.readUnsignedShort()).append("; ");
if (k % 16 == 0) {
out_println(outputString.toString().replaceAll("\\s+$",""));
outputString = new StringBuilder();
}
}
if (outputString.length() > 0) {
out_println(outputString.toString().replaceAll("\\s+$",""));
}
}
}
private void printStackMap(DataInputStream in, int elementsNum) throws IOException {
int num;
if (elementsNum > 0) {
num = elementsNum;
} else {
num = in.readUnsignedShort();
}
out.print(startArray(num) + (elementsNum > 0 ? "z" : "") + "{");
try {
for (int k = 0; k < num; k++) {
int maptype = in.readUnsignedByte();
StackMapType mptyp = stackMapType(maptype, out);
String maptypeImg;
if (printDetails) {
maptypeImg = maptype + "b";
} else {
try {
maptypeImg = mptyp.parsekey();
} catch (ArrayIndexOutOfBoundsException e) {
maptypeImg = "/* BAD TYPE: */ " + maptype + "b";
}
}
switch (mptyp) {
case ITEM_Object:
case ITEM_NewObject:
maptypeImg = maptypeImg + "," + in.readUnsignedShort();
break;
case ITEM_UNKNOWN:
maptypeImg = maptype + "b";
break;
default:
}
out.print(maptypeImg);
if (k < num - 1) {
out.print("; ");
}
}
} finally {
out.print("}");
}
}
/**
* Processes 4.7.20 The RuntimeVisibleTypeAnnotations Attribute, 4.7.21 The RuntimeInvisibleTypeAnnotations Attribute
* <code>type_annotation</code> structure.
*/
private void decodeTargetTypeAndRefInfo(DataInputStream in) throws IOException {
int tt = in.readUnsignedByte(); // [4.7.20] annotations[], type_annotation { u1 target_type; ...}
ETargetType targetType = ETargetType.getTargetType(tt);
if( targetType == null ) {
throw new Error("Type annotation: invalid target_type(u1) " + tt);
}
ETargetInfo targetInfo = targetType.targetInfo();
out_println(toHex(tt, 1) + "; // target_type: " + targetType.parseKey());
switch (targetInfo) {
case TYPEPARAM: //[3.3.1] meth_type_param, class_type_param:
out_println(toHex(in.readUnsignedByte(), 1) + "; // param_index");
break;
case SUPERTYPE: //[3.3.2] class_exts_impls
out_println(toHex(in.readUnsignedShort(), 2) + "; // type_index");
break;
case TYPEPARAM_BOUND: //[3.3.3] class_type_param_bnds, meth_type_param_bnds
out_println(toHex(in.readUnsignedByte(), 1) + "; // param_index");
out_println(toHex(in.readUnsignedByte(), 1) + "; // bound_index");
break;
case EMPTY: //[3.3.4] meth_receiver, meth_ret_type, field
// NOTE: reference_info is empty for this annotation's target
break;
case METHODPARAM: //[3.3.5] meth_formal_param:
out_println(toHex(in.readUnsignedByte(), 1) + "; // parameter_index");
break;
case EXCEPTION: //[3.3.61] throws_type
//KTL: Updated index to UShort for JSR308 change
out_println(in.readUnsignedShort() + "; // type_index");
break;
case LOCALVAR: //[3.3.7] local_var, resource_var
{
int lv_num = in.readUnsignedShort();
startArrayCmt(lv_num, "local_variables");
try {
for (int i = 0; i < lv_num; i++) {
out_println(in.readUnsignedShort() + " " + in.readUnsignedShort()
+ " " + in.readUnsignedShort() + ";" + getCommentPosCond());
}
} finally {
out_end("}");
}
}
break;
case CATCH: //[3.3.8] exception_param
out_println(in.readUnsignedShort() + "; // exception_table_index");
break;
case OFFSET: //[3.3.9] type_test (instanceof), obj_creat (new)
// constr_ref_receiver, meth_ref_receiver
out_println(in.readUnsignedShort() + "; // offset");
break;
case TYPEARG: //[3.3.10] cast, constr_ref_typearg, meth_invoc_typearg
// constr_invoc_typearg, meth_ref_typearg
out_println(in.readUnsignedShort() + "; // offset");
out_println(toHex(in.readUnsignedByte(), 1) + "; // type_index");
break;
default: // should never happen
out_println(toHex(tt, 1) + "; // invalid target_info: " + tt);
throw new ClassFormatError();
}
// [4.7.20.2]
int path_length = in.readUnsignedByte(); // type_path { u1 path_length; ...}
startArrayCmtB(path_length, "type_paths");
try {
for (int i = 0; i < path_length; i++) {
// print the type_path elements
out_println("{ " + toHex(in.readUnsignedByte(), 1) // { u1 type_path_kind;
+ "; " + toHex(in.readUnsignedByte(), 1) // u1 type_argument_index; }
+ "; } // type_path[" + i + "]"); // path[i]
}
} finally {
out_end("}");
}
}
private void decodeElementValue(DataInputStream in, PrintWriter out) throws IOException {
out_begin("{ // element_value");
try {
char tg = (char) in.readByte();
AnnotElemType tag = annotElemType(tg);
if (tag != AE_UNKNOWN) {
out_println("'" + tg + "';");
}
switch (tag) {
case AE_BYTE:
case AE_CHAR:
case AE_DOUBLE:
case AE_FLOAT:
case AE_INT:
case AE_LONG:
case AE_SHORT:
case AE_BOOLEAN:
case AE_STRING:
decodeCPXAttr(in, 2, "const_value_index", out);
break;
case AE_ENUM:
out_begin("{ // enum_const_value");
decodeCPXAttr(in, 2, "type_name_index", out);
decodeCPXAttr(in, 2, "const_name_index", out);
out_end("} // enum_const_value");
break;
case AE_CLASS:
decodeCPXAttr(in, 2, "class_info_index", out);
break;
case AE_ANNOTATION:
decodeAnnotation(in, out);
break;
case AE_ARRAY:
int ev_num = in.readUnsignedShort();
startArrayCmt(ev_num, "array_value");
try {
for (int i = 0; i < ev_num; i++) {
decodeElementValue(in, out);
if (i < ev_num - 1) {
out_println(";");
}
}
} finally {
out_end("} // array_value");
}
break;
case AE_UNKNOWN:
default:
String msg = "invalid element_value" + (isPrintableChar(tg) ? " tag type : " + tg : "");
out_println(toHex(tg, 1) + "; // " + msg);
throw new ClassFormatError(msg);
}
} finally {
out_end("} // element_value");
}
}
public boolean isPrintableChar(char c) {
Character.UnicodeBlock block = Character.UnicodeBlock.of(c);
return (!Character.isISOControl(c)) &&
c != KeyEvent.CHAR_UNDEFINED &&
block != null &&
block != Character.UnicodeBlock.SPECIALS;
}
private void decodeAnnotation(DataInputStream in, PrintWriter out) throws IOException {
out_begin("{ // annotation");
try {
decodeCPXAttr(in, 2, "field descriptor", out);
int evp_num = in.readUnsignedShort();
decodeElementValuePairs(evp_num, in, out);
} finally {
out_end("} // annotation");
}
}
private void decodeElementValuePairs(int count, DataInputStream in, PrintWriter out) throws IOException {
startArrayCmt(count, "element_value_pairs");
try {
for (int i = 0; i < count; i++) {
out_begin("{ // element value pair");
try {
decodeCPXAttr(in, 2, "name of the annotation type element", out);
decodeElementValue(in, out);
} finally {
out_end("} // element value pair");
if (i < count - 1) {
out_println(";");
}
}
}
} finally {
out_end("} // element_value_pairs");
}
}
/**
* component_info { JEP 359 Record(Preview): class file 58.65535
* u2 name_index;
* u2 descriptor_index;
* u2 attributes_count;
* attribute_info attributes[attributes_count];
* }
*
* or
* field_info {
* u2 access_flags;
* u2 name_index;
* u2 descriptor_index;
* u2 attributes_count;
* attribute_info attributes[attributes_count];
* }
* or
* method_info {
* u2 access_flags;
* u2 name_index;
* u2 descriptor_index;
* u2 attributes_count;
* attribute_info attributes[attributes_count];
* }
*
*/
private void decodeInfo(DataInputStream in, PrintWriter out, String elementName, boolean hasAccessFlag) throws IOException {
out_begin("{ // " + elementName + (printDetails ? getStringPos() : ""));
try {
if(hasAccessFlag) {
// u2 access_flags;
out_println(toHex(in.readShort(), 2) + "; // access");
}
// u2 name_index
printUtf8InfoIndex(in.readUnsignedShort(), "name_index");
// u2 descriptor_index
printUtf8InfoIndex(in.readUnsignedShort(), "descriptor_index");
// u2 attributes_count;
// attribute_info attributes[attributes_count]
decodeAttrs(in, out);
} finally {
out_end("}");
}
}
private void decodeTypeAnnotation(DataInputStream in, PrintWriter out) throws IOException {
out_begin("{ // type_annotation");
try {
decodeTargetTypeAndRefInfo(in);
decodeCPXAttr(in, 2, "field descriptor", out);
int evp_num = in.readUnsignedShort();
decodeElementValuePairs(evp_num, in, out);
} finally {
out_end("} // type_annotation");
}
}
private void decodeBootstrapMethod(DataInputStream in) throws IOException {
out_begin("{ // bootstrap_method");
try {
out_println("#" + in.readUnsignedShort() + "; // bootstrap_method_ref");
int bm_args_cnt = in.readUnsignedShort();
startArrayCmt(bm_args_cnt, "bootstrap_arguments");
try {
for (int i = 0; i < bm_args_cnt; i++) {
out_println("#" + in.readUnsignedShort() + ";" + getCommentPosCond());
}
} finally {
out_end("} // bootstrap_arguments");
}
} finally {
out_end("} // bootstrap_method");
}
}
private void decodeAttr(DataInputStream in, PrintWriter out) throws IOException {
// Read one attribute
String posComment = getStringPos();
int name_cpx = in.readUnsignedShort(), btag, len;
String AttrName = "";
try {
btag = types[name_cpx];
ConstType tag = tag(btag);
if (tag == ConstType.CONSTANT_UTF8) {
AttrName = (String) cpool[name_cpx];
}
} catch (ArrayIndexOutOfBoundsException ignored) {
}
AttrTag tg = attrtag(AttrName);
String endingComment = AttrName;
len = in.readInt();
countedin.enter(len);
try {
if (printDetails) {
out_begin("Attr(#" + name_cpx + ", " + len + ") { // " + AttrName + posComment);
} else {
out_begin("Attr(#" + name_cpx + ") { // " + AttrName);
}
switch (tg) {
case ATT_Code:
out_println(in.readUnsignedShort() + "; // max_stack");
out_println(in.readUnsignedShort() + "; // max_locals");
int code_len = in.readInt();
out_begin("Bytes" + startArray(code_len) + "{");
try {
printBytes(out, in, code_len);
} finally {
out_end("}");
}
int trap_num = in.readUnsignedShort();
startArrayCmt(trap_num, "Traps");
try {
for (int i = 0; i < trap_num; i++) {
out_println(in.readUnsignedShort() + " " +
in.readUnsignedShort() + " " +
in.readUnsignedShort() + " " +
in.readUnsignedShort() + ";" +
getCommentPosCond());
}
} finally {
out_end("} // end Traps");
}
// Read the attributes
decodeAttrs(in, out);
break;
case ATT_Exceptions:
int count = in.readUnsignedShort();
startArrayCmt(count, AttrName);
try {
for (int i = 0; i < count; i++) {
out_println("#" + in.readUnsignedShort() + ";" +
getCommentPosCond());
}
} finally {
out_end("}");
}
break;
case ATT_LineNumberTable:
int ll_num = in.readUnsignedShort();
startArrayCmt(ll_num, "line_number_table");
try {
for (int i = 0; i < ll_num; i++) {
out_println(in.readUnsignedShort() + " " +
in.readUnsignedShort() + ";" +
getCommentPosCond());
}
} finally {
out_end("}");
}
break;
case ATT_LocalVariableTable:
case ATT_LocalVariableTypeTable:
int lvt_num = in.readUnsignedShort();
startArrayCmt(lvt_num, AttrName);
try {
for (int i = 0; i < lvt_num; i++) {
out_println(in.readUnsignedShort() + " " +
in.readUnsignedShort() + " " +
in.readUnsignedShort() + " " +
in.readUnsignedShort() + " " +
in.readUnsignedShort() + ";" +
getCommentPosCond());
}
} finally {
out_end("}");
}
break;
case ATT_InnerClasses:
int ic_num = in.readUnsignedShort();
startArrayCmt(ic_num, "classes");
try {
for (int i = 0; i < ic_num; i++) {
out_println("#" + in.readUnsignedShort() + " #" +
in.readUnsignedShort() + " #" +
in.readUnsignedShort() + " " +
in.readUnsignedShort() + ";" + getCommentPosCond());
}
} finally {
out_end("}");
}
break;
case ATT_StackMap:
int e_num = in.readUnsignedShort();
startArrayCmt(e_num, "");
try {
for (int k = 0; k < e_num; k++) {
int start_pc = in.readUnsignedShort();
out_print("" + start_pc + ", ");
printStackMap(in, 0);
out.print(", ");
printStackMap(in, 0);
out.println(";");
}
} finally {
out_end("}");
}
break;
case ATT_StackMapTable:
int et_num = in.readUnsignedShort();
startArrayCmt(et_num, "");
try {
for (int k = 0; k < et_num; k++) {
int frame_type = in.readUnsignedByte();
StackMapFrameType ftype = stackMapFrameType(frame_type);
switch (ftype) {
case SAME_FRAME:
// type is same_frame;
out_print("" + frame_type + "b");
out.println("; // same_frame");
break;
case SAME_LOCALS_1_STACK_ITEM_FRAME:
// type is same_locals_1_stack_item_frame
out_print("" + frame_type + "b, ");
// read additional single stack element
printStackMap(in, 1);
out.println("; // same_locals_1_stack_item_frame");
break;
case SAME_LOCALS_1_STACK_ITEM_EXTENDED_FRAME:
// type is same_locals_1_stack_item_frame_extended
int noffset = in.readUnsignedShort();
out_print("" + frame_type + "b, " + noffset + ", ");
// read additional single stack element
printStackMap(in, 1);
out.println("; // same_locals_1_stack_item_frame_extended");
break;
case CHOP_1_FRAME:
case CHOP_2_FRAME:
case CHOP_3_FRAME:
// type is chop_frame
int coffset = in.readUnsignedShort();
out_print("" + frame_type + "b, " + coffset);
out.println("; // chop_frame " + (251 - frame_type));
break;
case SAME_FRAME_EX:
// type is same_frame_extended;
int xoffset = in.readUnsignedShort();
out_print("" + frame_type + "b, " + xoffset);
out.println("; // same_frame_extended");
break;
case APPEND_FRAME:
// type is append_frame
int aoffset = in.readUnsignedShort();
out_print("" + frame_type + "b, " + aoffset + ", ");
// read additional locals
printStackMap(in, frame_type - 251);
out.println("; // append_frame " + (frame_type - 251));
break;
case FULL_FRAME:
// type is full_frame
int foffset = in.readUnsignedShort();
out_print("" + frame_type + "b, " + foffset + ", ");
printStackMap(in, 0);
out.print(", ");
printStackMap(in, 0);
out.println("; // full_frame");
break;
}
}
} finally {
out_end("}");
}
break;
case ATT_EnclosingMethod:
decodeCPXAttrM(in, len, AttrName, out, 2);
break;
case ATT_AnnotationDefault:
decodeElementValue(in, out);
break;
case ATT_RuntimeInvisibleAnnotations:
case ATT_RuntimeVisibleAnnotations:
int an_num = in.readUnsignedShort();
startArrayCmt(an_num, "annotations");
try {
for (int i = 0; i < an_num; i++) {
decodeAnnotation(in, out);
if (i < an_num - 1) {
out_println(";");
}
}
} finally {
out_end("}");
}
break;
// 4.7.20 The RuntimeVisibleTypeAnnotations Attribute
// 4.7.21 The RuntimeInvisibleTypeAnnotations Attribute
case ATT_RuntimeInvisibleTypeAnnotations:
case ATT_RuntimeVisibleTypeAnnotations:
int ant_num = in.readUnsignedShort();
startArrayCmt(ant_num, "annotations");
try {
for (int i = 0; i < ant_num; i++) {
decodeTypeAnnotation(in, out);
if (i < ant_num - 1) {
out_println(";");
}
}
} finally {
out_end("}");
}
break;
case ATT_RuntimeInvisibleParameterAnnotations:
case ATT_RuntimeVisibleParameterAnnotations:
int pm_num = in.readUnsignedByte();
startArrayCmtB(pm_num, "parameters");
try {
for (int k = 0; k < pm_num; k++) {
int anp_num = in.readUnsignedShort();
startArrayCmt(anp_num, "annotations");
try {
for (int i = 0; i < anp_num; i++) {
decodeAnnotation(in, out);
if (k < anp_num - 1) {
out_println(";");
}
}
} finally {
out_end("}");
}
if (k < pm_num - 1) {
out_println(";");
}
}
} finally {
out_end("}");
}
break;
case ATT_BootstrapMethods:
int bm_num = in.readUnsignedShort();
startArrayCmt(bm_num, "bootstrap_methods");
try {
for (int i = 0; i < bm_num; i++) {
decodeBootstrapMethod(in);
if (i < bm_num - 1) {
out_println(";");
}
}
} finally {
out_end("}");
}
break;
case ATT_Module:
decodeModule(in);
break;
case ATT_TargetPlatform:
decodeCPXAttrM(in, len, AttrName, out, 3);
break;
case ATT_ModulePackages:
int p_num = in.readUnsignedShort();
startArrayCmt(p_num, null);
try {
decodeCPXAttrM(in, len - 2, AttrName, out, p_num);
} finally {
out_end("}");
}
break;
// MethodParameters_attribute {
// u2 attribute_name_index;
// u4 attribute_length;
// u1 parameters_count;
// { u2 name_index;
// u2 access_flags;
// } parameters[parameters_count];
// }
case ATT_MethodParameters:
int pcount = in.readUnsignedByte();
startArrayCmtB(pcount, AttrName);
try {
for (int i = 0; i < pcount; i++) {
out_println("#" + in.readUnsignedShort() + " " +
toHex(in.readUnsignedShort(), 2) + ";" +
getCommentPosCond());
}
} finally {
out_end("}");
}
break;
// JEP 359 Record(Preview): class file 58.65535
// Record_attribute {
// u2 attribute_name_index;
// u4 attribute_length;
// u2 components_count;
// component_info components[components_count];
// }
case ATT_Record:
int ncomps = in.readUnsignedShort();
startArrayCmt(ncomps, "components");
try {
for (int i = 0; i < ncomps; i++) {
decodeInfo(in,out,"component",false);
if (i < ncomps - 1) {
out_println(";");
}
}
} finally {
out_end("}");
}
break;
case ATT_ConstantValue:
case ATT_Signature:
case ATT_SourceFile:
decodeCPXAttr(in, len, AttrName, out);
break;
// JEP 181 (Nest-based Access Control): class file 55.0
// NestHost_attribute {
// u2 attribute_name_index;
// u4 attribute_length;
// u2 host_class_index;
// }
case ATT_NestHost:
decodeTypes(in, out, 1);
break;
// JEP 181 (Nest-based Access Control): class file 55.0
// NestMembers_attribute {
// u2 attribute_name_index;
// u4 attribute_length;
// u2 number_of_classes;
// u2 classes[number_of_classes];
// }
case ATT_NestMembers:
// JEP 360 (Sealed types): class file 59.65535
// PermittedSubtypes_attribute {
// u2 attribute_name_index;
// u4 attribute_length;
// u2 permitted_subtypes_count;
// u2 classes[permitted_subtypes_count];
// }
case ATT_PermittedSubtypes:
int nsubtypes = in.readUnsignedShort();
startArrayCmt(nsubtypes, "classes");
try {
decodeTypes(in, out, nsubtypes);
} finally {
out_end("}");
}
break;
default:
printBytes(out, in, len);
if (AttrName == null) {
endingComment = "Attr(#" + name_cpx + ")";
}
}
} catch (EOFException e) {
out.println("// ======== unexpected end of attribute array");
} finally {
int rest = countedin.available();
if (rest > 0) {
out.println("// ======== attribute array started " + posComment + " has " + rest + " bytes more:");
printBytes(out, in, rest);
}
out_end("} // end " + endingComment);
countedin.leave();
}
}
private void decodeModuleStatement(String statementName, DataInputStream in) throws IOException {
// u2 {exports|opens}_count
int count = in.readUnsignedShort();
startArrayCmt(count, statementName);
try {
for (int i = 0; i < count; i++) {
// u2 {exports|opens}_index; u2 {exports|opens}_flags
int index = in.readUnsignedShort();
int nFlags = in.readUnsignedShort();
String sFlags = printDetails ? Module.Modifier.getStatementFlags(nFlags) : "";
out_println("#" + index + " " + toHex(nFlags, 2) + (sFlags.isEmpty() ? "" : " // [ " + sFlags + " ]"));
int exports_to_count = in.readUnsignedShort();
startArrayCmt(exports_to_count, null);
try {
for (int j = 0; j < exports_to_count; j++) {
out_println("#" + in.readUnsignedShort() + ";");
}
} finally {
out_end("};");
}
}
} finally {
out_end("} // " + statementName + "\n");
}
}
private void decodeModule(DataInputStream in) throws IOException {
//u2 module_name_index
int index = in.readUnsignedShort();
entityName = (String) cpool[(Integer) cpool[index]];
out_print("#" + index + "; // ");
if (printDetails) {
out.println(String.format("%-16s","name_index") + " : " + entityName);
} else {
out.println("name_index");
}
// u2 module_flags
int moduleFlags = in.readUnsignedShort();
out_print(toHex(moduleFlags, 2) + "; // flags");
if (printDetails) {
out_print(" " + Module.Modifier.getModuleFlags(moduleFlags));
}
out.println();
//u2 module_version
int versionIndex = in.readUnsignedShort();
out_println("#" + versionIndex + "; // version");
// u2 requires_count
int count = in.readUnsignedShort();
startArrayCmt(count, "requires");
try {
for (int i = 0; i < count; i++) {
// u2 requires_index; u2 requires_flags; u2 requires_version_index
index = in.readUnsignedShort();
int nFlags = in.readUnsignedShort();
versionIndex = in.readUnsignedShort();
String sFlags = printDetails ? Module.Modifier.getStatementFlags(nFlags) : "";
out_println("#" + index + " " + toHex(nFlags, 2) + " #" + versionIndex + ";" + (sFlags.isEmpty() ? "" : " // " + sFlags));
}
} finally {
out_end("} // requires\n");
}
decodeModuleStatement("exports", in);
decodeModuleStatement("opens", in);
// u2 uses_count
count = in.readUnsignedShort();
startArrayCmt(count, "uses");
try {
for (int i = 0; i < count; i++) {
// u2 uses_index
out_println("#" + in.readUnsignedShort() + ";");
}
} finally {
out_end("} // uses\n");
}
count = in.readUnsignedShort(); // u2 provides_count
startArrayCmt(count, "provides");
try {
for (int i = 0; i < count; i++) {
// u2 provides_index
out_println("#" + in.readUnsignedShort());
int provides_with_count = in.readUnsignedShort();
// u2 provides_with_count
startArrayCmt(provides_with_count, null);
try {
for (int j = 0; j < provides_with_count; j++) {
// u2 provides_with_index;
out_println("#" + in.readUnsignedShort() + ";");
}
} finally {
out_end("};");
}
}
} finally {
out_end("} // provides\n");
}
}
private void decodeAttrs(DataInputStream in, PrintWriter out) throws IOException {
// Read the attributes
int attr_num = in.readUnsignedShort();
startArrayCmt(attr_num, "Attributes");
try {
for (int i = 0; i < attr_num; i++) {
decodeAttr(in, out);
if (i + 1 < attr_num) {
out_println(";");
}
}
} finally {
out_end("} // Attributes");
}
}
private void decodeMembers(DataInputStream in, PrintWriter out, String groupName, String elementName) throws IOException {
int count = in.readUnsignedShort();
traceln(groupName + "=" + count);
startArrayCmt(count, groupName);
try {
for (int i = 0; i < count; i++) {
decodeInfo(in,out,elementName,true);
if (i + 1 < count) {
out_println(";");
}
}
} finally {
out_end("} // " + groupName);
out.println();
}
}
void decodeClass(String fileName) throws IOException {
// Read the header
try {
int magic = in.readInt();
int min_version = in.readUnsignedShort();
int version = in.readUnsignedShort();
// Read the constant pool
readCP(in);
short access = in.readShort(); // don't care about sign
int this_cpx = in.readUnsignedShort();
try {
entityName = (String) cpool[(Integer) cpool[this_cpx]];
if (entityName.equals("module-info")) {
entityType = "module";
entityName = "";
} else {
entityType = "class";
}
if (!entityName.isEmpty() && (JcodTokens.keyword_token_ident(entityName) != JcodTokens.Token.IDENT || JcodTokens.constValue(entityName) != -1)) {
// JCod can't parse a entityName matching a keyword or a constant value,
// then use the filename instead:
out_begin(String.format("file \"%s.class\" {", entityName));
} else {
out_begin(format("%s %s {", entityType, entityName));
}
} catch (Exception e) {
entityName = fileName;
out.println("// " + e.getMessage() + " while accessing entityName");
out_begin(format("%s %s { // source file name", entityType, entityName));
}
out_print(toHex(magic, 4) + ";");
if (magic != JAVA_MAGIC) {
out.print(" // wrong magic: 0x" + Integer.toString(JAVA_MAGIC, 16) + " expected");
}
out.println();
out_println(min_version + "; // minor version");
out_println(version + "; // version");
// Print the constant pool
printCP(out);
out_println(toHex(access, 2) + "; // access" +
(printDetails ? " [" + (" " + Modifiers.accessString(access, CF_Context.CTX_CLASS).toUpperCase()).replaceAll(" (\\S)", " ACC_$1") + "]" : ""));
out_println("#" + this_cpx + ";// this_cpx");
int super_cpx = in.readUnsignedShort();
out_println("#" + super_cpx + ";// super_cpx");
traceln(i18n.getString("jdec.trace.access_thisCpx_superCpx", access, this_cpx, super_cpx));
out.println();
// Read the interfaces
int numinterfaces = in.readUnsignedShort();
traceln(i18n.getString("jdec.trace.numinterfaces", numinterfaces));
startArrayCmt(numinterfaces, "Interfaces");
try {
decodeTypes(in, out, numinterfaces);
} finally {
out_end("} // Interfaces\n");
}
// Read the fields
decodeMembers(in, out, "Fields", "field");
// Read the methods
decodeMembers(in, out, "Methods", "method");
// Read the attributes
decodeAttrs(in, out);
} catch (EOFException ignored) {
} catch (ClassFormatError err) {
String msg = err.getMessage();
out.println("//------- ClassFormatError" +
(msg == null || msg.isEmpty() ? "" : ": " + msg));
printRestOfBytes();
} finally {
out_end(format("} // end %s %s", entityType, entityName));
}
} // end decodeClass()
private void decodeTypes(DataInputStream in, PrintWriter out, int count) throws IOException {
for (int i = 0; i < count; i++) {
int type_cpx = in.readUnsignedShort();
traceln(i18n.getString("jdec.trace.type", i, type_cpx));
out_print("#" + type_cpx + ";");
if (printDetails) {
String name = (String) cpool[(int)cpool[type_cpx]];
out.println(" // " + name + getStringPos());
} else {
out.println();
}
}
}
/* ====================================================== */
boolean DebugFlag = false;
public void trace(String s) {
if (!DebugFlag) {
return;
}
System.out.print(s);
}
public void traceln(String s) {
if (!DebugFlag) {
return;
}
System.out.println(s);
}
}// end class ClassData