
org.apidesign.vm4brwsr.ByteCodeParser Maven / Gradle / Ivy
The newest version!
/*
* Copyright (c) 2002, 2004, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.apidesign.vm4brwsr;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import org.apidesign.bck2brwsr.core.JavaScriptBody;
import org.apidesign.bck2brwsr.core.JavaScriptPrototype;
/** This is a byte code parser heavily based on original code of JavaP utility.
* As such I decided to keep the original Oracle's GPLv2 header.
*
* @author Jaroslav Tulach
*/
final class ByteCodeParser {
private ByteCodeParser() {
}
/* Class File Constants */
public static final int JAVA_MAGIC = 0xcafebabe;
public static final int JAVA_VERSION = 45;
public static final int JAVA_MINOR_VERSION = 3;
/* Constant table */
public static final int CONSTANT_UTF8 = 1;
public static final int CONSTANT_UNICODE = 2;
public static final int CONSTANT_INTEGER = 3;
public static final int CONSTANT_FLOAT = 4;
public static final int CONSTANT_LONG = 5;
public static final int CONSTANT_DOUBLE = 6;
public static final int CONSTANT_CLASS = 7;
public static final int CONSTANT_STRING = 8;
public static final int CONSTANT_FIELD = 9;
public static final int CONSTANT_METHOD = 10;
public static final int CONSTANT_INTERFACEMETHOD = 11;
public static final int CONSTANT_NAMEANDTYPE = 12;
public static final int CONSTANT_METHODHANDLE = 15;
public static final int CONSTANT_METHODTYPE = 16;
public static final int CONSTANT_INVOKEDYNAMIC = 18;
/* Access Flags */
public static final int ACC_PUBLIC = 0x00000001;
public static final int ACC_PRIVATE = 0x00000002;
public static final int ACC_PROTECTED = 0x00000004;
public static final int ACC_STATIC = 0x00000008;
public static final int ACC_FINAL = 0x00000010;
public static final int ACC_SYNCHRONIZED = 0x00000020;
public static final int ACC_SUPER = 0x00000020;
public static final int ACC_VOLATILE = 0x00000040;
public static final int ACC_TRANSIENT = 0x00000080;
public static final int ACC_NATIVE = 0x00000100;
public static final int ACC_INTERFACE = 0x00000200;
public static final int ACC_ABSTRACT = 0x00000400;
public static final int ACC_STRICT = 0x00000800;
public static final int ACC_EXPLICIT = 0x00001000;
public static final int ACC_SYNTHETIC = 0x00010000; // actually, this is an attribute
private static final int ACC_ANNOTATION = 0x00020000;
/* Type codes for StackMap attribute */
public static final int ITEM_Bogus =0; // an unknown or uninitialized value
public static final int ITEM_Integer =1; // a 32-bit integer
public static final int ITEM_Float =2; // not used
public static final int ITEM_Double =3; // not used
public static final int ITEM_Long =4; // a 64-bit integer
public static final int ITEM_Null =5; // the type of null
public static final int ITEM_InitObject =6; // "this" in constructor
public static final int ITEM_Object =7; // followed by 2-byte index of class name
public static final int ITEM_NewObject =8; // followed by 2-byte ref to "new"
/* Constants used in StackMapTable attribute */
public static final int SAME_FRAME_BOUND = 64;
public static final int SAME_LOCALS_1_STACK_ITEM_BOUND = 128;
public static final int SAME_LOCALS_1_STACK_ITEM_EXTENDED = 247;
public static final int SAME_FRAME_EXTENDED = 251;
public static final int FULL_FRAME = 255;
/* Opcodes */
public static final int opc_dead = -2;
public static final int opc_label = -1;
public static final int opc_nop = 0;
public static final int opc_aconst_null = 1;
public static final int opc_iconst_m1 = 2;
public static final int opc_iconst_0 = 3;
public static final int opc_iconst_1 = 4;
public static final int opc_iconst_2 = 5;
public static final int opc_iconst_3 = 6;
public static final int opc_iconst_4 = 7;
public static final int opc_iconst_5 = 8;
public static final int opc_lconst_0 = 9;
public static final int opc_lconst_1 = 10;
public static final int opc_fconst_0 = 11;
public static final int opc_fconst_1 = 12;
public static final int opc_fconst_2 = 13;
public static final int opc_dconst_0 = 14;
public static final int opc_dconst_1 = 15;
public static final int opc_bipush = 16;
public static final int opc_sipush = 17;
public static final int opc_ldc = 18;
public static final int opc_ldc_w = 19;
public static final int opc_ldc2_w = 20;
public static final int opc_iload = 21;
public static final int opc_lload = 22;
public static final int opc_fload = 23;
public static final int opc_dload = 24;
public static final int opc_aload = 25;
public static final int opc_iload_0 = 26;
public static final int opc_iload_1 = 27;
public static final int opc_iload_2 = 28;
public static final int opc_iload_3 = 29;
public static final int opc_lload_0 = 30;
public static final int opc_lload_1 = 31;
public static final int opc_lload_2 = 32;
public static final int opc_lload_3 = 33;
public static final int opc_fload_0 = 34;
public static final int opc_fload_1 = 35;
public static final int opc_fload_2 = 36;
public static final int opc_fload_3 = 37;
public static final int opc_dload_0 = 38;
public static final int opc_dload_1 = 39;
public static final int opc_dload_2 = 40;
public static final int opc_dload_3 = 41;
public static final int opc_aload_0 = 42;
public static final int opc_aload_1 = 43;
public static final int opc_aload_2 = 44;
public static final int opc_aload_3 = 45;
public static final int opc_iaload = 46;
public static final int opc_laload = 47;
public static final int opc_faload = 48;
public static final int opc_daload = 49;
public static final int opc_aaload = 50;
public static final int opc_baload = 51;
public static final int opc_caload = 52;
public static final int opc_saload = 53;
public static final int opc_istore = 54;
public static final int opc_lstore = 55;
public static final int opc_fstore = 56;
public static final int opc_dstore = 57;
public static final int opc_astore = 58;
public static final int opc_istore_0 = 59;
public static final int opc_istore_1 = 60;
public static final int opc_istore_2 = 61;
public static final int opc_istore_3 = 62;
public static final int opc_lstore_0 = 63;
public static final int opc_lstore_1 = 64;
public static final int opc_lstore_2 = 65;
public static final int opc_lstore_3 = 66;
public static final int opc_fstore_0 = 67;
public static final int opc_fstore_1 = 68;
public static final int opc_fstore_2 = 69;
public static final int opc_fstore_3 = 70;
public static final int opc_dstore_0 = 71;
public static final int opc_dstore_1 = 72;
public static final int opc_dstore_2 = 73;
public static final int opc_dstore_3 = 74;
public static final int opc_astore_0 = 75;
public static final int opc_astore_1 = 76;
public static final int opc_astore_2 = 77;
public static final int opc_astore_3 = 78;
public static final int opc_iastore = 79;
public static final int opc_lastore = 80;
public static final int opc_fastore = 81;
public static final int opc_dastore = 82;
public static final int opc_aastore = 83;
public static final int opc_bastore = 84;
public static final int opc_castore = 85;
public static final int opc_sastore = 86;
public static final int opc_pop = 87;
public static final int opc_pop2 = 88;
public static final int opc_dup = 89;
public static final int opc_dup_x1 = 90;
public static final int opc_dup_x2 = 91;
public static final int opc_dup2 = 92;
public static final int opc_dup2_x1 = 93;
public static final int opc_dup2_x2 = 94;
public static final int opc_swap = 95;
public static final int opc_iadd = 96;
public static final int opc_ladd = 97;
public static final int opc_fadd = 98;
public static final int opc_dadd = 99;
public static final int opc_isub = 100;
public static final int opc_lsub = 101;
public static final int opc_fsub = 102;
public static final int opc_dsub = 103;
public static final int opc_imul = 104;
public static final int opc_lmul = 105;
public static final int opc_fmul = 106;
public static final int opc_dmul = 107;
public static final int opc_idiv = 108;
public static final int opc_ldiv = 109;
public static final int opc_fdiv = 110;
public static final int opc_ddiv = 111;
public static final int opc_irem = 112;
public static final int opc_lrem = 113;
public static final int opc_frem = 114;
public static final int opc_drem = 115;
public static final int opc_ineg = 116;
public static final int opc_lneg = 117;
public static final int opc_fneg = 118;
public static final int opc_dneg = 119;
public static final int opc_ishl = 120;
public static final int opc_lshl = 121;
public static final int opc_ishr = 122;
public static final int opc_lshr = 123;
public static final int opc_iushr = 124;
public static final int opc_lushr = 125;
public static final int opc_iand = 126;
public static final int opc_land = 127;
public static final int opc_ior = 128;
public static final int opc_lor = 129;
public static final int opc_ixor = 130;
public static final int opc_lxor = 131;
public static final int opc_iinc = 132;
public static final int opc_i2l = 133;
public static final int opc_i2f = 134;
public static final int opc_i2d = 135;
public static final int opc_l2i = 136;
public static final int opc_l2f = 137;
public static final int opc_l2d = 138;
public static final int opc_f2i = 139;
public static final int opc_f2l = 140;
public static final int opc_f2d = 141;
public static final int opc_d2i = 142;
public static final int opc_d2l = 143;
public static final int opc_d2f = 144;
public static final int opc_i2b = 145;
public static final int opc_int2byte = 145;
public static final int opc_i2c = 146;
public static final int opc_int2char = 146;
public static final int opc_i2s = 147;
public static final int opc_int2short = 147;
public static final int opc_lcmp = 148;
public static final int opc_fcmpl = 149;
public static final int opc_fcmpg = 150;
public static final int opc_dcmpl = 151;
public static final int opc_dcmpg = 152;
public static final int opc_ifeq = 153;
public static final int opc_ifne = 154;
public static final int opc_iflt = 155;
public static final int opc_ifge = 156;
public static final int opc_ifgt = 157;
public static final int opc_ifle = 158;
public static final int opc_if_icmpeq = 159;
public static final int opc_if_icmpne = 160;
public static final int opc_if_icmplt = 161;
public static final int opc_if_icmpge = 162;
public static final int opc_if_icmpgt = 163;
public static final int opc_if_icmple = 164;
public static final int opc_if_acmpeq = 165;
public static final int opc_if_acmpne = 166;
public static final int opc_goto = 167;
public static final int opc_jsr = 168;
public static final int opc_ret = 169;
public static final int opc_tableswitch = 170;
public static final int opc_lookupswitch = 171;
public static final int opc_ireturn = 172;
public static final int opc_lreturn = 173;
public static final int opc_freturn = 174;
public static final int opc_dreturn = 175;
public static final int opc_areturn = 176;
public static final int opc_return = 177;
public static final int opc_getstatic = 178;
public static final int opc_putstatic = 179;
public static final int opc_getfield = 180;
public static final int opc_putfield = 181;
public static final int opc_invokevirtual = 182;
public static final int opc_invokenonvirtual = 183;
public static final int opc_invokespecial = 183;
public static final int opc_invokestatic = 184;
public static final int opc_invokeinterface = 185;
public static final int opc_invokedynamic = 186;
public static final int opc_new = 187;
public static final int opc_newarray = 188;
public static final int opc_anewarray = 189;
public static final int opc_arraylength = 190;
public static final int opc_athrow = 191;
public static final int opc_checkcast = 192;
public static final int opc_instanceof = 193;
public static final int opc_monitorenter = 194;
public static final int opc_monitorexit = 195;
public static final int opc_wide = 196;
public static final int opc_multianewarray = 197;
public static final int opc_ifnull = 198;
public static final int opc_ifnonnull = 199;
public static final int opc_goto_w = 200;
public static final int opc_jsr_w = 201;
/* Pseudo-instructions */
public static final int opc_bytecode = 203;
public static final int opc_try = 204;
public static final int opc_endtry = 205;
public static final int opc_catch = 206;
public static final int opc_var = 207;
public static final int opc_endvar = 208;
public static final int opc_localsmap = 209;
public static final int opc_stackmap = 210;
/* PicoJava prefixes */
public static final int opc_nonpriv = 254;
public static final int opc_priv = 255;
/* Wide instructions *
public static final int opc_iload_w = (opc_wide<<8)|opc_iload;
public static final int opc_lload_w = (opc_wide<<8)|opc_lload;
public static final int opc_fload_w = (opc_wide<<8)|opc_fload;
public static final int opc_dload_w = (opc_wide<<8)|opc_dload;
public static final int opc_aload_w = (opc_wide<<8)|opc_aload;
public static final int opc_istore_w = (opc_wide<<8)|opc_istore;
public static final int opc_lstore_w = (opc_wide<<8)|opc_lstore;
public static final int opc_fstore_w = (opc_wide<<8)|opc_fstore;
public static final int opc_dstore_w = (opc_wide<<8)|opc_dstore;
public static final int opc_astore_w = (opc_wide<<8)|opc_astore;
public static final int opc_ret_w = (opc_wide<<8)|opc_ret;
public static final int opc_iinc_w = (opc_wide<<8)|opc_iinc;
*/
static class AnnotationParser {
private final boolean textual;
private final boolean iterateArray;
protected AnnotationParser(boolean textual, boolean iterateArray) {
this.textual = textual;
this.iterateArray = iterateArray;
}
protected void visitAnnotationStart(String type, boolean top) throws IOException {
}
protected void visitAnnotationEnd(String type, boolean top) throws IOException {
}
protected void visitValueStart(String attrName, char type) throws IOException {
}
protected void visitValueEnd(String attrName, char type) throws IOException {
}
protected void visitAttr(
String annoType, String attr, String attrType, String value
) throws IOException {
}
protected void visitEnumAttr(
String annoType, String attr, String attrType, String value
) throws IOException {
visitAttr(annoType, attr, attrType, value);
}
protected void visitClassAttr(
String annoType, String attr, String className
) throws IOException {
visitAttr(annoType, attr, className, className);
}
/**
* Initialize the parsing with constant pool from
* cd
.
*
* @param attr the attribute defining annotations
* @param cd constant pool
* @throws IOException in case I/O fails
*/
public final void parse(byte[] attr, ClassData cd) throws IOException {
ByteArrayInputStream is = new ByteArrayInputStream(attr);
DataInputStream dis = new DataInputStream(is);
try {
read(dis, cd);
} finally {
is.close();
}
}
private void read(DataInputStream dis, ClassData cd) throws IOException {
int cnt = dis.readUnsignedShort();
for (int i = 0; i < cnt; i++) {
readAnno(dis, cd, true);
}
}
private void readAnno(DataInputStream dis, ClassData cd, boolean top) throws IOException {
int type = dis.readUnsignedShort();
String typeName = cd.StringValue(type);
visitAnnotationStart(typeName, top);
int cnt = dis.readUnsignedShort();
for (int i = 0; i < cnt; i++) {
String attrName = cd.StringValue(dis.readUnsignedShort());
readValue(dis, cd, typeName, attrName);
}
visitAnnotationEnd(typeName, top);
if (cnt == 0) {
visitAttr(typeName, null, null, null);
}
}
public void parseDefault(byte[] defaultAttribute, ClassData cd) throws IOException {
ByteArrayInputStream is = new ByteArrayInputStream(defaultAttribute);
DataInputStream dis = new DataInputStream(is);
try {
readValue(dis, cd, null, null);
} finally {
is.close();
}
}
private void readValue(
DataInputStream dis, ClassData cd, String typeName, String attrName) throws IOException {
char type = (char) dis.readByte();
visitValueStart(attrName, type);
if (type == '@') {
readAnno(dis, cd, false);
} else if ("CFJZsSIDB".indexOf(type) >= 0) { // NOI18N
int primitive = dis.readUnsignedShort();
String val = cd.stringValue(primitive, textual);
String attrType;
if (type == 's') {
attrType = "Ljava_lang_String_2";
if (textual) {
val = '"' + val + '"';
}
} else {
attrType = "" + type;
}
visitAttr(typeName, attrName, attrType, val);
} else if (type == 'c') {
int cls = dis.readUnsignedShort();
String attrType = cd.stringValue(cls, textual);
visitClassAttr(typeName, attrName, attrType);
} else if (type == '[') {
int cnt = dis.readUnsignedShort();
for (int i = 0; i < cnt; i++) {
readValue(dis, cd, typeName, iterateArray ? attrName : null);
}
} else if (type == 'e') {
int enumT = dis.readUnsignedShort();
String attrType = cd.stringValue(enumT, textual);
int enumN = dis.readUnsignedShort();
String val = cd.stringValue(enumN, textual);
visitEnumAttr(typeName, attrName, attrType, val);
} else {
throw new IOException("Unknown type " + type);
}
visitValueEnd(attrName, type);
}
}
/**
* Reads and stores attribute information.
*
* @author Sucheta Dambalkar (Adopted code from jdis)
*/
private static class AttrData {
ClassData cls;
int name_cpx;
int datalen;
byte data[];
public AttrData(ClassData cls) {
this.cls = cls;
}
/**
* Reads unknown attribute.
*/
public void read(int name_cpx, DataInputStream in) throws IOException {
this.name_cpx = name_cpx;
datalen = in.readInt();
data = new byte[datalen];
in.readFully(data);
}
/**
* Reads just the name of known attribute.
*/
public void read(int name_cpx) {
this.name_cpx = name_cpx;
}
/**
* Returns attribute name.
*/
public String getAttrName() {
return cls.getString(name_cpx);
}
/**
* Returns attribute data.
*/
public byte[] getData() {
return data;
}
}
/**
* Stores constant pool entry information with one field.
*
* @author Sucheta Dambalkar (Adopted code from jdis)
*/
static class CPX {
final int cpx;
CPX(int cpx) {
this.cpx = cpx;
}
}
/**
* Stores constant pool entry information with two fields.
*
* @author Sucheta Dambalkar (Adopted code from jdis)
*/
static class CPX2 {
final int cpx1, cpx2;
CPX2(int cpx1, int cpx2) {
this.cpx1 = cpx1;
this.cpx2 = cpx2;
}
}
/**
* Central data repository of the Java Disassembler. Stores all the
* information in java class file.
*
* @author Sucheta Dambalkar (Adopted code from jdis)
*/
static final class ClassData {
private int magic;
private int minor_version;
private int major_version;
private int cpool_count;
private Object cpool[];
private int access;
private int this_class = 0;
private int super_class;
private int interfaces_count;
private int[] interfaces = new int[0];
private FieldData[] fields;
private MethodData[] methods;
private InnerClassData[] innerClasses;
private BootMethodData[] bootMethods;
private int attributes_count;
private AttrData[] attrs;
private int source_cpx = 0;
private byte tags[];
private Hashtable indexHashAscii = new Hashtable();
private String pkgPrefix = "";
private int pkgPrefixLen = 0;
private boolean hasEnclosingMethod;
/**
* Read classfile to disassemble.
*/
public ClassData(InputStream infile) throws IOException {
this.read(new DataInputStream(infile));
}
/**
* Reads and stores class file information.
*/
public void read(DataInputStream in) throws IOException {
// Read the header
magic = in.readInt();
if (magic != JAVA_MAGIC) {
throw new ClassFormatError("wrong magic: "
+ toHex(magic) + ", expected "
+ toHex(JAVA_MAGIC));
}
minor_version = in.readShort();
major_version = in.readShort();
if (major_version != JAVA_VERSION) {
}
// Read the constant pool
readCP(in);
access = in.readUnsignedShort();
this_class = in.readUnsignedShort();
super_class = in.readUnsignedShort();
//Read interfaces.
interfaces_count = in.readUnsignedShort();
if (interfaces_count > 0) {
interfaces = new int[interfaces_count];
}
for (int i = 0; i < interfaces_count; i++) {
interfaces[i] = in.readShort();
}
// Read the fields
readFields(in);
// Read the methods
readMethods(in);
// Read the attributes
attributes_count = in.readUnsignedShort();
attrs = new AttrData[attributes_count];
for (int k = 0; k < attributes_count; k++) {
int name_cpx = in.readUnsignedShort();
if (getTag(name_cpx) == CONSTANT_UTF8) {
final String attrName = getString(name_cpx);
if (attrName.equals("SourceFile")) {
if (in.readInt() != 2) {
throw new ClassFormatError("invalid attr length");
}
source_cpx = in.readUnsignedShort();
AttrData attr = new AttrData(this);
attr.read(name_cpx);
attrs[k] = attr;
} else if (attrName.equals("InnerClasses")) {
int length = in.readInt();
int num = in.readUnsignedShort();
if (2 + num * 8 != length) {
throw new ClassFormatError("invalid attr length");
}
innerClasses = new InnerClassData[num];
for (int j = 0; j < num; j++) {
InnerClassData innerClass = new InnerClassData(this);
innerClass.read(in);
innerClasses[j] = innerClass;
}
AttrData attr = new AttrData(this);
attr.read(name_cpx);
attrs[k] = attr;
} else if (attrName.equals("BootstrapMethods")) {
AttrData attr = new AttrData(this);
bootMethods = readBootstrapMethods(in);
attr.read(name_cpx);
attrs[k] = attr;
} else {
if (attrName.equals("EnclosingMethod")) {
hasEnclosingMethod = true;
}
AttrData attr = new AttrData(this);
attr.read(name_cpx, in);
attrs[k] = attr;
}
}
}
in.close();
} // end ClassData.read()
BootMethodData[] readBootstrapMethods(DataInputStream in) throws IOException {
int attr_len = in.readInt(); //attr_lengt
int number = in.readShort();
BootMethodData[] arr = new BootMethodData[number];
for (int i = 0; i < number; i++) {
int ref = in.readShort();
int len = in.readShort();
int[] args = new int[len];
for (int j = 0; j < len; j++) {
args[j] = in.readShort();
}
arr[i] = new BootMethodData(this, ref, args);
}
return arr;
}
/**
* Reads and stores constant pool info.
*/
void readCP(DataInputStream in) throws IOException {
cpool_count = in.readUnsignedShort();
tags = new byte[cpool_count];
cpool = new Object[cpool_count];
for (int i = 1; i < cpool_count; i++) {
byte tag = in.readByte();
switch (tags[i] = tag) {
case CONSTANT_UTF8:
String str = in.readUTF();
indexHashAscii.put(cpool[i] = str, new Integer(i));
break;
case CONSTANT_INTEGER:
cpool[i] = new Integer(in.readInt());
break;
case CONSTANT_FLOAT:
cpool[i] = new Float(in.readFloat());
break;
case CONSTANT_LONG:
cpool[i++] = new Long(in.readLong());
break;
case CONSTANT_DOUBLE:
cpool[i++] = new Double(in.readDouble());
break;
case CONSTANT_CLASS:
case CONSTANT_STRING:
cpool[i] = new CPX(in.readUnsignedShort());
break;
case CONSTANT_FIELD:
case CONSTANT_METHOD:
case CONSTANT_INTERFACEMETHOD:
case CONSTANT_NAMEANDTYPE:
cpool[i] = new CPX2(in.readUnsignedShort(), in.readUnsignedShort());
break;
case CONSTANT_METHODHANDLE:
cpool[i] = new CPX2(in.readByte(), in.readUnsignedShort());
break;
case CONSTANT_METHODTYPE:
cpool[i] = new CPX(in.readUnsignedShort());
break;
case CONSTANT_INVOKEDYNAMIC:
cpool[i] = new CPX2(in.readUnsignedShort(), in.readUnsignedShort());
break;
case 0:
default:
throw new IOException("invalid constant type: " + (int) tags[i]);
}
}
}
/**
* Reads and strores field info.
*/
protected void readFields(DataInputStream in) throws IOException {
int fields_count = in.readUnsignedShort();
fields = new FieldData[fields_count];
for (int k = 0; k < fields_count; k++) {
FieldData field = new FieldData(this);
field.read(in);
fields[k] = field;
}
}
/**
* Reads and strores Method info.
*/
protected void readMethods(DataInputStream in) throws IOException {
int methods_count = in.readUnsignedShort();
methods = new MethodData[methods_count];
for (int k = 0; k < methods_count; k++) {
MethodData method = new MethodData(this);
method.read(in);
methods[k] = method;
}
}
/**
* get a string
*/
public String getString(int n) {
if (n == 0) {
return null;
} else {
return (String) cpool[n];
}
}
/**
* get the type of constant given an index
*/
public byte getTag(int n) {
try {
return tags[n];
} catch (ArrayIndexOutOfBoundsException e) {
return (byte) 100;
}
}
static final String hexString = "0123456789ABCDEF";
public static char hexTable[] = hexString.toCharArray();
static String toHex(long val, int width) {
StringBuffer s = new StringBuffer();
for (int i = width - 1; i >= 0; i--) {
s.append(hexTable[((int) (val >> (4 * i))) & 0xF]);
}
return "0x" + s.toString();
}
static String toHex(long val) {
int width;
for (width = 16; width > 0; width--) {
if ((val >> (width - 1) * 4) != 0) {
break;
}
}
return toHex(val, width);
}
static String toHex(int val) {
int width;
for (width = 8; width > 0; width--) {
if ((val >> (width - 1) * 4) != 0) {
break;
}
}
return toHex(val, width);
}
/**
* Returns the name of this class.
*/
public String getClassName() {
String res = null;
if (this_class == 0) {
return res;
}
int tcpx;
try {
if (tags[this_class] != CONSTANT_CLASS) {
return res; //" ";
}
tcpx = ((CPX) cpool[this_class]).cpx;
} catch (ArrayIndexOutOfBoundsException e) {
return res; // "#"+cpx+"// invalid constant pool index";
} catch (Throwable e) {
return res; // "#"+cpx+"// ERROR IN DISASSEMBLER";
}
try {
return (String) (cpool[tcpx]);
} catch (ArrayIndexOutOfBoundsException e) {
return res; // "class #"+scpx+"// invalid constant pool index";
} catch (ClassCastException e) {
return res; // "class #"+scpx+"// invalid constant pool reference";
} catch (Throwable e) {
return res; // "#"+cpx+"// ERROR IN DISASSEMBLER";
}
}
/**
* Returns the name of class at perticular index.
*/
public String getClassName(int cpx) {
String res = "#" + cpx;
if (cpx == 0) {
return res;
}
int scpx;
try {
if (tags[cpx] != CONSTANT_CLASS) {
return res; //" ";
}
scpx = ((CPX) cpool[cpx]).cpx;
} catch (ArrayIndexOutOfBoundsException e) {
return res; // "#"+cpx+"// invalid constant pool index";
} catch (Throwable e) {
return res; // "#"+cpx+"// ERROR IN DISASSEMBLER";
}
res = "#" + scpx;
try {
return (String) (cpool[scpx]);
} catch (ArrayIndexOutOfBoundsException e) {
return res; // "class #"+scpx+"// invalid constant pool index";
} catch (ClassCastException e) {
return res; // "class #"+scpx+"// invalid constant pool reference";
} catch (Throwable e) {
return res; // "#"+cpx+"// ERROR IN DISASSEMBLER";
}
}
public int getAccessFlags() {
return access;
}
public boolean hasEnclosingMethod() {
return hasEnclosingMethod;
}
/**
* Returns true if it is a class
*/
public boolean isClass() {
if ((access & ACC_INTERFACE) == 0) {
return true;
}
return false;
}
/**
* Returns true if it is a interface.
*/
public boolean isInterface() {
if ((access & ACC_INTERFACE) != 0) {
return true;
}
return false;
}
public boolean isAnnotation() {
return (access & ACC_ANNOTATION) != 0;
}
/**
* Returns true if this member is public, false otherwise.
*/
public boolean isPublic() {
return (access & ACC_PUBLIC) != 0;
}
/**
* Returns the access of this class or interface.
*/
public String[] getAccess() {
Vector v = new Vector();
if ((access & ACC_PUBLIC) != 0) {
v.addElement("public");
}
if ((access & ACC_FINAL) != 0) {
v.addElement("final");
}
if ((access & ACC_ABSTRACT) != 0) {
v.addElement("abstract");
}
String[] accflags = new String[v.size()];
v.copyInto(accflags);
return accflags;
}
/**
* Returns list of innerclasses.
*/
public InnerClassData[] getInnerClasses() {
return innerClasses;
}
/**
* Returns list of attributes.
*/
final AttrData[] getAttributes() {
return attrs;
}
public byte[] findAnnotationData(boolean classRetention) {
String n = classRetention
? "RuntimeInvisibleAnnotations" : // NOI18N
"RuntimeVisibleAnnotations"; // NOI18N
return findAttr(n, attrs);
}
/**
* Returns true if superbit is set.
*/
public boolean isSuperSet() {
if ((access & ACC_SUPER) != 0) {
return true;
}
return false;
}
/**
* Returns super class name.
*/
public String getSuperClassName() {
String res = null;
if (super_class == 0) {
return res;
}
int scpx;
try {
if (tags[super_class] != CONSTANT_CLASS) {
return res; //" ";
}
scpx = ((CPX) cpool[super_class]).cpx;
} catch (ArrayIndexOutOfBoundsException e) {
return res; // "#"+cpx+"// invalid constant pool index";
} catch (Throwable e) {
return res; // "#"+cpx+"// ERROR IN DISASSEMBLER";
}
try {
return (String) (cpool[scpx]);
} catch (ArrayIndexOutOfBoundsException e) {
return res; // "class #"+scpx+"// invalid constant pool index";
} catch (ClassCastException e) {
return res; // "class #"+scpx+"// invalid constant pool reference";
} catch (Throwable e) {
return res; // "#"+cpx+"// ERROR IN DISASSEMBLER";
}
}
/**
* Returns list of super interfaces.
*/
public String[] getSuperInterfaces() {
String interfacenames[] = new String[interfaces.length];
int interfacecpx = -1;
for (int i = 0; i < interfaces.length; i++) {
interfacecpx = ((CPX) cpool[interfaces[i]]).cpx;
interfacenames[i] = (String) (cpool[interfacecpx]);
}
return interfacenames;
}
/**
* Returns string at prticular constant pool index.
*/
public String getStringValue(int cpoolx) {
try {
return ((String) cpool[cpoolx]);
} catch (ArrayIndexOutOfBoundsException e) {
return "//invalid constant pool index:" + cpoolx;
} catch (ClassCastException e) {
return "//invalid constant pool ref:" + cpoolx;
}
}
/**
* Returns list of field info.
*/
public FieldData[] getFields() {
return fields;
}
/**
* Returns list of method info.
*/
public MethodData[] getMethods() {
return methods;
}
/**
* Returns constant pool entry at that index.
*/
public CPX2 getCpoolEntry(int cpx) {
return ((CPX2) (cpool[cpx]));
}
public Object getCpoolEntryobj(int cpx) {
return (cpool[cpx]);
}
/**
* Returns index of this class.
*/
public int getthis_cpx() {
return this_class;
}
/**
* Returns string at that index.
*/
public String StringValue(int cpx) {
return stringValue(cpx, false);
}
public String stringValue(int cpx, boolean textual) {
return stringValue(cpx, textual, null);
}
public String stringValue(int cpx, String[] classRefs) {
return stringValue(cpx, true, classRefs);
}
private String stringValue(int cpx, boolean textual, String[] refs) {
if (cpx == 0) {
return "#0";
}
int tag;
Object x;
String suffix = "";
try {
tag = tags[cpx];
x = cpool[cpx];
} catch (IndexOutOfBoundsException e) {
return "";
}
if (x == null) {
return "";
}
switch (tag) {
case CONSTANT_UTF8: {
if (!textual) {
return (String) x;
}
StringBuilder sb = new StringBuilder();
String s = (String) x;
for (int k = 0; k < s.length(); k++) {
char c = s.charAt(k);
switch (c) {
case '\\':
sb.append('\\').append('\\');
break;
case '\t':
sb.append('\\').append('t');
break;
case '\n':
sb.append('\\').append('n');
break;
case '\r':
sb.append('\\').append('r');
break;
case '\"':
sb.append('\\').append('\"');
break;
case '\u2028':
sb.append("\\u2028");
break;
case '\u2029':
sb.append("\\u2029");
break;
default:
sb.append(c);
}
}
return sb.toString();
}
case CONSTANT_DOUBLE: {
Double d = (Double) x;
String sd = d.toString();
if (textual) {
return sd;
}
return sd + "d";
}
case CONSTANT_FLOAT: {
Float f = (Float) x;
String sf = (f).toString();
if (textual) {
return sf;
}
return sf + "f";
}
case CONSTANT_LONG: {
Long ln = (Long) x;
if (textual) {
return ln.toString();
}
return ln.toString() + 'l';
}
case CONSTANT_INTEGER: {
Integer in = (Integer) x;
return in.toString();
}
case CONSTANT_CLASS:
String jn = getClassName(cpx);
if (textual) {
if (refs != null) {
refs[0] = jn;
}
return jn;
}
return javaName(jn);
case CONSTANT_STRING:
String sv = stringValue(((CPX) x).cpx, textual);
if (textual) {
return '"' + sv + '"';
} else {
return sv;
}
case CONSTANT_FIELD:
case CONSTANT_METHOD:
case CONSTANT_INTERFACEMETHOD:
//return getShortClassName(((CPX2)x).cpx1)+"."+StringValue(((CPX2)x).cpx2);
return javaName(getClassName(((CPX2) x).cpx1)) + "." + StringValue(((CPX2) x).cpx2);
case CONSTANT_NAMEANDTYPE:
return getName(((CPX2) x).cpx1) + ":" + StringValue(((CPX2) x).cpx2);
case CONSTANT_METHODHANDLE:
return "K" + ((CPX2)x).cpx1 + "@" + stringValue(((CPX2)x).cpx2, textual);
case CONSTANT_METHODTYPE:
return stringValue(((CPX)x).cpx, true);
default:
return "UnknownTag" + tag; //TBD
}
}
/**
* Returns resolved java type name.
*/
public String javaName(String name) {
if (name == null) {
return "null";
}
int len = name.length();
if (len == 0) {
return "\"\"";
}
int cc = '/';
fullname:
{ // xxx/yyy/zzz
int cp;
for (int k = 0; k < len; k += Character.charCount(cp)) {
cp = name.codePointAt(k);
if (cc == '/') {
if (!isJavaIdentifierStart(cp)) {
break fullname;
}
} else if (cp != '/') {
if (!isJavaIdentifierPart(cp)) {
break fullname;
}
}
cc = cp;
}
return name;
}
return "\"" + name + "\"";
}
public String getName(int cpx) {
String res;
try {
return javaName((String) cpool[cpx]); //.replace('/','.');
} catch (ArrayIndexOutOfBoundsException e) {
return "";
} catch (ClassCastException e) {
return "";
}
}
/**
* Returns unqualified class name.
*/
public String getShortClassName(int cpx) {
String classname = javaName(getClassName(cpx));
pkgPrefixLen = classname.lastIndexOf("/") + 1;
if (pkgPrefixLen != 0) {
pkgPrefix = classname.substring(0, pkgPrefixLen);
if (classname.startsWith(pkgPrefix)) {
return classname.substring(pkgPrefixLen);
}
}
return classname;
}
/**
* Returns source file name.
*/
public String getSourceName() {
return getName(source_cpx);
}
/**
* Returns package name.
*/
public String getPkgName() {
String classname = getClassName(this_class);
pkgPrefixLen = classname.lastIndexOf("/") + 1;
if (pkgPrefixLen != 0) {
pkgPrefix = classname.substring(0, pkgPrefixLen);
return /* ("package " + */ pkgPrefix.substring(0, pkgPrefixLen - 1) /* + ";\n") */;
} else {
return null;
}
}
public BootMethodData getBootMethod(int indx) {
return bootMethods != null ? bootMethods[indx] : null;
}
/**
* Returns total constant pool entry count.
*/
public int getCpoolCount() {
return cpool_count;
}
/**
* Returns minor version of class file.
*/
public int getMinor_version() {
return minor_version;
}
/**
* Returns major version of class file.
*/
public int getMajor_version() {
return major_version;
}
private boolean isJavaIdentifierStart(int cp) {
return ('a' <= cp && cp <= 'z') || ('A' <= cp && cp <= 'Z');
}
private boolean isJavaIdentifierPart(int cp) {
return isJavaIdentifierStart(cp) || ('0' <= cp && cp <= '9');
}
public String[] getNameAndType(int indx) {
return getNameAndType(indx, 0, new String[2]);
}
private String[] getNameAndType(int indx, int at, String[] arr) {
CPX2 c2 = getCpoolEntry(indx);
arr[at] = StringValue(c2.cpx1);
arr[at + 1] = StringValue(c2.cpx2);
return arr;
}
public String[] getFieldInfoName(int indx) {
CPX2 c2 = getCpoolEntry(indx);
String[] arr = new String[3];
arr[0] = getClassName(c2.cpx1);
return getNameAndType(c2.cpx2, 1, arr);
}
public MethodData findMethod(String name, String signature) {
for (MethodData md: methods) {
if (md.getName().equals(name)
&& md.getInternalSig().equals(signature)) {
return md;
}
}
// not found
return null;
}
public FieldData findField(String name, String signature) {
for (FieldData fd: fields) {
if (fd.getName().equals(name)
&& fd.getInternalSig().equals(signature)) {
return fd;
}
}
// not found
return null;
}
static byte[] findAttr(String n, AttrData[] attrs) {
for (AttrData ad : attrs) {
if (n.equals(ad.getAttrName())) {
return ad.getData();
}
}
return null;
}
}
/**
* Strores field data informastion.
*
* @author Sucheta Dambalkar (Adopted code from jdis)
*/
static class FieldData {
ClassData cls;
int access;
int name_index;
int descriptor_index;
int attributes_count;
int value_cpx = -1;
boolean isSynthetic = false;
boolean isDeprecated = false;
Vector attrs;
public FieldData(ClassData cls) {
this.cls = cls;
}
/**
* Read and store field info.
*/
public void read(DataInputStream in) throws IOException {
access = in.readUnsignedShort();
name_index = in.readUnsignedShort();
descriptor_index = in.readUnsignedShort();
// Read the attributes
int attributes_count = in.readUnsignedShort();
attrs = new Vector(attributes_count);
for (int i = 0; i < attributes_count; i++) {
int attr_name_index = in.readUnsignedShort();
if (cls.getTag(attr_name_index) != CONSTANT_UTF8) {
continue;
}
String attr_name = cls.getString(attr_name_index);
if (attr_name.equals("ConstantValue")) {
if (in.readInt() != 2) {
throw new ClassFormatError("invalid ConstantValue attr length");
}
value_cpx = in.readUnsignedShort();
AttrData attr = new AttrData(cls);
attr.read(attr_name_index);
attrs.addElement(attr);
} else if (attr_name.equals("Synthetic")) {
if (in.readInt() != 0) {
throw new ClassFormatError("invalid Synthetic attr length");
}
isSynthetic = true;
AttrData attr = new AttrData(cls);
attr.read(attr_name_index);
attrs.addElement(attr);
} else if (attr_name.equals("Deprecated")) {
if (in.readInt() != 0) {
throw new ClassFormatError("invalid Synthetic attr length");
}
isDeprecated = true;
AttrData attr = new AttrData(cls);
attr.read(attr_name_index);
attrs.addElement(attr);
} else {
AttrData attr = new AttrData(cls);
attr.read(attr_name_index, in);
attrs.addElement(attr);
}
}
} // end read
public boolean isStatic() {
return (access & ACC_STATIC) != 0;
}
/**
* Returns access of a field.
*/
public String[] getAccess() {
Vector v = new Vector();
if ((access & ACC_PUBLIC) != 0) {
v.addElement("public");
}
if ((access & ACC_PRIVATE) != 0) {
v.addElement("private");
}
if ((access & ACC_PROTECTED) != 0) {
v.addElement("protected");
}
if ((access & ACC_STATIC) != 0) {
v.addElement("static");
}
if ((access & ACC_FINAL) != 0) {
v.addElement("final");
}
if ((access & ACC_VOLATILE) != 0) {
v.addElement("volatile");
}
if ((access & ACC_TRANSIENT) != 0) {
v.addElement("transient");
}
String[] accflags = new String[v.size()];
v.copyInto(accflags);
return accflags;
}
/**
* Returns name of a field.
*/
public String getName() {
return cls.getStringValue(name_index);
}
/**
* Returns internal signature of a field
*/
public String getInternalSig() {
return cls.getStringValue(descriptor_index);
}
/**
* Returns true if field is synthetic.
*/
public boolean isSynthetic() {
return isSynthetic;
}
/**
* Returns true if field is deprecated.
*/
public boolean isDeprecated() {
return isDeprecated;
}
public boolean hasConstantValue() {
return value_cpx != -1;
}
/**
* Returns list of attributes of field.
*/
public Vector getAttributes() {
return attrs;
}
public byte[] findAnnotationData(boolean classRetention) {
String n = classRetention
? "RuntimeInvisibleAnnotations" : // NOI18N
"RuntimeVisibleAnnotations"; // NOI18N
AttrData[] arr = new AttrData[attrs.size()];
attrs.copyInto(arr);
return ClassData.findAttr(n, arr);
}
}
/**
* A JavaScript optimized replacement for Hashtable.
*
* @author Jaroslav Tulach
*/
private static final class Hashtable {
private Object[] keys;
private Object[] values;
Hashtable(int i) {
this();
}
Hashtable(int i, double d) {
this();
}
Hashtable() {
}
synchronized void put(Object key, Object val) {
int[] where = {-1, -1};
Object found = get(key, where);
if (where[0] != -1) {
// key exists
values[where[0]] = val;
} else {
if (where[1] != -1) {
// null found
keys[where[1]] = key;
values[where[1]] = val;
} else {
if (keys == null) {
keys = new Object[11];
values = new Object[11];
keys[0] = key;
values[0] = val;
} else {
Object[] newKeys = new Object[keys.length * 2];
Object[] newValues = new Object[values.length * 2];
for (int i = 0; i < keys.length; i++) {
newKeys[i] = keys[i];
newValues[i] = values[i];
}
newKeys[keys.length] = key;
newValues[keys.length] = val;
keys = newKeys;
values = newValues;
}
}
}
}
Object get(Object key) {
return get(key, null);
}
private synchronized Object get(Object key, int[] foundAndNull) {
if (keys == null) {
return null;
}
for (int i = 0; i < keys.length; i++) {
if (keys[i] == null) {
if (foundAndNull != null) {
foundAndNull[1] = i;
}
} else if (keys[i].equals(key)) {
if (foundAndNull != null) {
foundAndNull[0] = i;
}
return values[i];
}
}
return null;
}
}
/**
* Strores InnerClass data informastion.
*
* @author Sucheta Dambalkar (Adopted code from jdis)
*/
private static class InnerClassData {
ClassData cls;
int inner_class_info_index, outer_class_info_index, inner_name_index, access;
public InnerClassData(ClassData cls) {
this.cls = cls;
}
/**
* Read Innerclass attribute data.
*/
public void read(DataInputStream in) throws IOException {
inner_class_info_index = in.readUnsignedShort();
outer_class_info_index = in.readUnsignedShort();
inner_name_index = in.readUnsignedShort();
access = in.readUnsignedShort();
} // end read
/**
* Returns the access of this class or interface.
*/
public String[] getAccess() {
Vector v = new Vector();
if ((access & ACC_PUBLIC) != 0) {
v.addElement("public");
}
if ((access & ACC_FINAL) != 0) {
v.addElement("final");
}
if ((access & ACC_ABSTRACT) != 0) {
v.addElement("abstract");
}
String[] accflags = new String[v.size()];
v.copyInto(accflags);
return accflags;
}
} // end InnerClassData
static class BootMethodData {
final ClassData clazz;
final int method;
final int[] args;
private BootMethodData(ClassData clazz, int method, int[] args) {
this.clazz = clazz;
this.method = method;
this.args = args;
}
int getArguments() {
return args.length;
}
String getArgument(int index) {
return clazz.stringValue(args[index], false);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(clazz.stringValue(method, true));
sb.append('(');
for (int i = 0; i < getArguments(); i++) {
sb.append("\n ");
sb.append(getArgument(i));
}
sb.append(')');
return sb.toString();
}
}
/**
* Strores LineNumberTable data information.
*
* @author Sucheta Dambalkar (Adopted code from jdis)
*/
private static class LineNumData {
short start_pc, line_number;
public LineNumData() {
}
/**
* Read LineNumberTable attribute.
*/
public LineNumData(DataInputStream in) throws IOException {
start_pc = in.readShort();
line_number = in.readShort();
}
}
/**
* Strores LocalVariableTable data information.
*
* @author Sucheta Dambalkar (Adopted code from jdis)
*/
private static class LocVarData {
short start_pc, length, name_cpx, sig_cpx, slot;
public LocVarData() {
}
/**
* Read LocalVariableTable attribute.
*/
public LocVarData(DataInputStream in) throws IOException {
start_pc = in.readShort();
length = in.readShort();
name_cpx = in.readShort();
sig_cpx = in.readShort();
slot = in.readShort();
}
}
/**
* Strores method data informastion.
*
* @author Sucheta Dambalkar (Adopted code from jdis)
*/
static class MethodData {
ClassData cls;
int access;
int name_index;
int descriptor_index;
int attributes_count;
byte[] code;
Vector exception_table = new Vector(0);
Vector lin_num_tb = new Vector(0);
Vector loc_var_tb = new Vector(0);
StackMapTableData[] stackMapTable;
StackMapData[] stackMap;
int[] exc_index_table = null;
Vector attrs = new Vector(0);
Vector code_attrs = new Vector(0);
int max_stack, max_locals;
boolean isSynthetic = false;
boolean isDeprecated = false;
private AttrData annotationDefault;
public MethodData(ClassData cls) {
this.cls = cls;
}
/**
* Read method info.
*/
public void read(DataInputStream in) throws IOException {
access = in.readUnsignedShort();
name_index = in.readUnsignedShort();
descriptor_index = in.readUnsignedShort();
int attributes_count = in.readUnsignedShort();
for (int i = 0; i < attributes_count; i++) {
int attr_name_index = in.readUnsignedShort();
readAttr:
{
if (cls.getTag(attr_name_index) == CONSTANT_UTF8) {
String attr_name = cls.getString(attr_name_index);
if (attr_name.equals("Code")) {
readCode(in);
AttrData attr = new AttrData(cls);
attr.read(attr_name_index);
attrs.addElement(attr);
break readAttr;
} else if (attr_name.equals("Exceptions")) {
readExceptions(in);
AttrData attr = new AttrData(cls);
attr.read(attr_name_index);
attrs.addElement(attr);
break readAttr;
} else if (attr_name.equals("Synthetic")) {
if (in.readInt() != 0) {
throw new ClassFormatError("invalid Synthetic attr length");
}
isSynthetic = true;
AttrData attr = new AttrData(cls);
attr.read(attr_name_index);
attrs.addElement(attr);
break readAttr;
} else if (attr_name.equals("Deprecated")) {
if (in.readInt() != 0) {
throw new ClassFormatError("invalid Synthetic attr length");
}
isDeprecated = true;
AttrData attr = new AttrData(cls);
attr.read(attr_name_index);
attrs.addElement(attr);
break readAttr;
} else if (attr_name.equals("AnnotationDefault")) {
AttrData attr = new AttrData(cls);
attr.read(attr_name_index, in);
attrs.addElement(attr);
annotationDefault = attr;
break readAttr;
}
}
AttrData attr = new AttrData(cls);
attr.read(attr_name_index, in);
attrs.addElement(attr);
}
}
}
/**
* Read code attribute info.
*/
public void readCode(DataInputStream in) throws IOException {
int attr_length = in.readInt();
max_stack = in.readUnsignedShort();
max_locals = in.readUnsignedShort();
int codelen = in.readInt();
code = new byte[codelen];
int totalread = 0;
while (totalread < codelen) {
totalread += in.read(code, totalread, codelen - totalread);
}
// in.read(code, 0, codelen);
int clen = 0;
readExceptionTable(in);
int code_attributes_count = in.readUnsignedShort();
for (int k = 0; k < code_attributes_count; k++) {
int table_name_index = in.readUnsignedShort();
int table_name_tag = cls.getTag(table_name_index);
AttrData attr = new AttrData(cls);
if (table_name_tag == CONSTANT_UTF8) {
String table_name_tstr = cls.getString(table_name_index);
if (table_name_tstr.equals("LineNumberTable")) {
readLineNumTable(in);
attr.read(table_name_index);
} else if (table_name_tstr.equals("LocalVariableTable")) {
readLocVarTable(in);
attr.read(table_name_index);
} else if (table_name_tstr.equals("StackMapTable")) {
readStackMapTable(in);
attr.read(table_name_index);
} else if (table_name_tstr.equals("StackMap")) {
readStackMap(in);
attr.read(table_name_index);
} else {
attr.read(table_name_index, in);
}
code_attrs.addElement(attr);
continue;
}
attr.read(table_name_index, in);
code_attrs.addElement(attr);
}
}
/**
* Read exception table info.
*/
void readExceptionTable(DataInputStream in) throws IOException {
int exception_table_len = in.readUnsignedShort();
exception_table = new Vector(exception_table_len);
for (int l = 0; l < exception_table_len; l++) {
exception_table.addElement(new TrapData(in, l));
}
}
/**
* Read LineNumberTable attribute info.
*/
void readLineNumTable(DataInputStream in) throws IOException {
int attr_len = in.readInt(); // attr_length
int lin_num_tb_len = in.readUnsignedShort();
lin_num_tb = new Vector(lin_num_tb_len);
for (int l = 0; l < lin_num_tb_len; l++) {
lin_num_tb.addElement(new LineNumData(in));
}
}
/**
* Read LocalVariableTable attribute info.
*/
void readLocVarTable(DataInputStream in) throws IOException {
int attr_len = in.readInt(); // attr_length
int loc_var_tb_len = in.readUnsignedShort();
loc_var_tb = new Vector(loc_var_tb_len);
for (int l = 0; l < loc_var_tb_len; l++) {
loc_var_tb.addElement(new LocVarData(in));
}
}
/**
* Read Exception attribute info.
*/
public void readExceptions(DataInputStream in) throws IOException {
int attr_len = in.readInt(); // attr_length in prog
int num_exceptions = in.readUnsignedShort();
exc_index_table = new int[num_exceptions];
for (int l = 0; l < num_exceptions; l++) {
int exc = in.readShort();
exc_index_table[l] = exc;
}
}
/**
* Read StackMapTable attribute info.
*/
void readStackMapTable(DataInputStream in) throws IOException {
int attr_len = in.readInt(); //attr_length
int stack_map_tb_len = in.readUnsignedShort();
stackMapTable = new StackMapTableData[stack_map_tb_len];
for (int i = 0; i < stack_map_tb_len; i++) {
stackMapTable[i] = StackMapTableData.getInstance(in, this);
}
}
/**
* Read StackMap attribute info.
*/
void readStackMap(DataInputStream in) throws IOException {
int attr_len = in.readInt(); //attr_length
int stack_map_len = in.readUnsignedShort();
stackMap = new StackMapData[stack_map_len];
for (int i = 0; i < stack_map_len; i++) {
stackMap[i] = new StackMapData(in, this);
}
}
/**
* Return access of the method.
*/
public int getAccess() {
return access;
}
/**
* Return name of the method.
*/
public String getName() {
return cls.getStringValue(name_index);
}
/**
* Return internal siganature of the method.
*/
public String getInternalSig() {
return cls.getStringValue(descriptor_index);
}
/**
* Return code attribute data of a method.
*/
public byte[] getCode() {
return code;
}
/**
* Return LineNumberTable size.
*/
public int getnumlines() {
return lin_num_tb.size();
}
/**
* Return LineNumberTable
*/
public Vector getlin_num_tb() {
return lin_num_tb;
}
/**
* Return LocalVariableTable size.
*/
public int getloc_var_tbsize() {
return loc_var_tb.size();
}
/**
* Return LocalVariableTable.
*/
public Vector getloc_var_tb() {
return loc_var_tb;
}
/**
* Return StackMap.
*/
public StackMapData[] getStackMap() {
return stackMap;
}
/**
* Return StackMapTable.
*/
public StackMapTableData[] getStackMapTable() {
return stackMapTable;
}
public StackMapIterator createStackMapIterator() {
return new StackMapIterator(this);
}
/**
* Return true if method is static
*/
public boolean isStatic() {
if ((access & ACC_STATIC) != 0) {
return true;
}
return false;
}
/**
* Return max depth of operand stack.
*/
public int getMaxStack() {
return max_stack;
}
/**
* Return number of local variables.
*/
public int getMaxLocals() {
return max_locals;
}
/**
* Return exception index table in Exception attribute.
*/
public int[] get_exc_index_table() {
return exc_index_table;
}
/**
* Return exception table in code attributre.
*/
public TrapDataIterator getTrapDataIterator() {
return new TrapDataIterator(exception_table);
}
/**
* Return method attributes.
*/
public Vector getAttributes() {
return attrs;
}
/**
* Return code attributes.
*/
public Vector getCodeAttributes() {
return code_attrs;
}
byte[] getDefaultAttribute() {
return annotationDefault == null ? null : annotationDefault.getData();
}
/**
* Return true if method id synthetic.
*/
public boolean isSynthetic() {
return isSynthetic;
}
/**
* Return true if method is deprecated.
*/
public boolean isDeprecated() {
return isDeprecated;
}
public byte[] findAnnotationData(boolean classRetention) {
String n = classRetention
? "RuntimeInvisibleAnnotations" : // NOI18N
"RuntimeVisibleAnnotations"; // NOI18N
AttrData[] arr = new AttrData[attrs.size()];
attrs.copyInto(arr);
return ClassData.findAttr(n, arr);
}
public boolean isConstructor() {
return "".equals(getName());
}
}
/* represents one entry of StackMap attribute
*/
private static class StackMapData {
final int offset;
final int[] locals;
final int[] stack;
StackMapData(int offset, int[] locals, int[] stack) {
this.offset = offset;
this.locals = locals;
this.stack = stack;
}
StackMapData(DataInputStream in, MethodData method) throws IOException {
offset = in.readUnsignedShort();
int local_size = in.readUnsignedShort();
locals = readTypeArray(in, local_size, method);
int stack_size = in.readUnsignedShort();
stack = readTypeArray(in, stack_size, method);
}
static final int[] readTypeArray(DataInputStream in, int length, MethodData method) throws IOException {
int[] types = new int[length];
for (int i = 0; i < length; i++) {
types[i] = readType(in, method);
}
return types;
}
static final int readType(DataInputStream in, MethodData method) throws IOException {
int type = in.readUnsignedByte();
if (type == ITEM_Object || type == ITEM_NewObject) {
type = type | (in.readUnsignedShort() << 8);
}
return type;
}
}
static final class StackMapIterator {
private final StackMapTableData[] stackMapTable;
private final TypeArray argTypes;
private final TypeArray localTypes;
private final TypeArray stackTypes;
private int nextFrameIndex;
private int lastFrameByteCodeOffset;
private int byteCodeOffset;
StackMapIterator(final MethodData methodData) {
this(methodData.getStackMapTable(),
methodData.getInternalSig(),
methodData.isStatic());
}
StackMapIterator(final StackMapTableData[] stackMapTable,
final String methodSignature,
final boolean isStaticMethod) {
this.stackMapTable = (stackMapTable != null)
? stackMapTable
: new StackMapTableData[0];
argTypes = getArgTypes(methodSignature, isStaticMethod);
localTypes = new TypeArray();
stackTypes = new TypeArray();
localTypes.addAll(argTypes);
lastFrameByteCodeOffset = -1;
advanceBy(0);
}
public boolean isEmpty() {
return stackMapTable.length == 0;
}
public String getFrameAsString() {
return (nextFrameIndex == 0)
? StackMapTableData.toString("INITIAL", 0, null, null)
: stackMapTable[nextFrameIndex - 1].toString();
}
public int getFrameIndex() {
return nextFrameIndex;
}
public TypeArray getFrameStack() {
return stackTypes;
}
public TypeArray getFrameLocals() {
return localTypes;
}
public TypeArray getArguments() {
return argTypes;
}
public void advanceBy(final int numByteCodes) {
if (numByteCodes < 0) {
throw new IllegalStateException("Forward only iterator");
}
byteCodeOffset += numByteCodes;
while ((nextFrameIndex < stackMapTable.length)
&& ((byteCodeOffset - lastFrameByteCodeOffset)
>= (stackMapTable[nextFrameIndex].offsetDelta
+ 1))) {
final StackMapTableData nextFrame = stackMapTable[nextFrameIndex];
lastFrameByteCodeOffset += nextFrame.offsetDelta + 1;
nextFrame.applyTo(localTypes, stackTypes);
++nextFrameIndex;
}
}
public void advanceTo(final int nextByteCodeOffset) {
advanceBy(nextByteCodeOffset - byteCodeOffset);
}
private static TypeArray getArgTypes(final String methodSignature,
final boolean isStaticMethod) {
final TypeArray argTypes = new TypeArray();
if (!isStaticMethod) {
argTypes.add(ITEM_Object);
}
if (methodSignature.charAt(0) != '(') {
throw new IllegalArgumentException("Invalid method signature");
}
final int length = methodSignature.length();
boolean skipType = false;
int argType;
for (int i = 1; i < length; ++i) {
switch (methodSignature.charAt(i)) {
case 'B':
case 'C':
case 'S':
case 'Z':
case 'I':
argType = ITEM_Integer;
break;
case 'J':
argType = ITEM_Long;
break;
case 'F':
argType = ITEM_Float;
break;
case 'D':
argType = ITEM_Double;
break;
case 'L': {
i = methodSignature.indexOf(';', i + 1);
if (i == -1) {
throw new IllegalArgumentException(
"Invalid method signature");
}
argType = ITEM_Object;
break;
}
case ')':
// not interested in the return value type
return argTypes;
case '[':
if (!skipType) {
argTypes.add(ITEM_Object);
skipType = true;
}
continue;
default:
throw new IllegalArgumentException(
"Invalid method signature");
}
if (!skipType) {
argTypes.add(argType);
} else {
skipType = false;
}
}
return argTypes;
}
}
/* represents one entry of StackMapTable attribute
*/
private static abstract class StackMapTableData {
final int frameType;
int offsetDelta;
StackMapTableData(int frameType) {
this.frameType = frameType;
}
abstract void applyTo(TypeArray localTypes, TypeArray stackTypes);
protected static String toString(
final String frameType,
final int offset,
final int[] localTypes,
final int[] stackTypes) {
final StringBuilder sb = new StringBuilder(frameType);
sb.append("(off: +").append(offset);
if (localTypes != null) {
sb.append(", locals: ");
appendTypes(sb, localTypes);
}
if (stackTypes != null) {
sb.append(", stack: ");
appendTypes(sb, stackTypes);
}
sb.append(')');
return sb.toString();
}
private static void appendTypes(final StringBuilder sb, final int[] types) {
sb.append('[');
if (types.length > 0) {
sb.append(TypeArray.typeString(types[0]));
for (int i = 1; i < types.length; ++i) {
sb.append(", ");
sb.append(TypeArray.typeString(types[i]));
}
}
sb.append(']');
}
private static class SameFrame extends StackMapTableData {
SameFrame(int frameType, int offsetDelta) {
super(frameType);
this.offsetDelta = offsetDelta;
}
@Override
void applyTo(TypeArray localTypes, TypeArray stackTypes) {
stackTypes.clear();
}
@Override
public String toString() {
return toString("SAME" + ((frameType == SAME_FRAME_EXTENDED)
? "_FRAME_EXTENDED" : ""),
offsetDelta,
null, null);
}
}
private static class SameLocals1StackItem extends StackMapTableData {
final int[] stack;
SameLocals1StackItem(int frameType, int offsetDelta, int[] stack) {
super(frameType);
this.offsetDelta = offsetDelta;
this.stack = stack;
}
@Override
void applyTo(TypeArray localTypes, TypeArray stackTypes) {
stackTypes.setAll(stack);
}
@Override
public String toString() {
return toString(
"SAME_LOCALS_1_STACK_ITEM"
+ ((frameType == SAME_LOCALS_1_STACK_ITEM_EXTENDED)
? "_EXTENDED" : ""),
offsetDelta,
null, stack);
}
}
private static class ChopFrame extends StackMapTableData {
ChopFrame(int frameType, int offsetDelta) {
super(frameType);
this.offsetDelta = offsetDelta;
}
@Override
void applyTo(TypeArray localTypes, TypeArray stackTypes) {
localTypes.setSize(localTypes.getSize()
- (SAME_FRAME_EXTENDED - frameType));
stackTypes.clear();
}
@Override
public String toString() {
return toString("CHOP", offsetDelta, null, null);
}
}
private static class AppendFrame extends StackMapTableData {
final int[] locals;
AppendFrame(int frameType, int offsetDelta, int[] locals) {
super(frameType);
this.offsetDelta = offsetDelta;
this.locals = locals;
}
@Override
void applyTo(TypeArray localTypes, TypeArray stackTypes) {
localTypes.addAll(locals);
stackTypes.clear();
}
@Override
public String toString() {
return toString("APPEND", offsetDelta, locals, null);
}
}
private static class FullFrame extends StackMapTableData {
final int[] locals;
final int[] stack;
FullFrame(int offsetDelta, int[] locals, int[] stack) {
super(FULL_FRAME);
this.offsetDelta = offsetDelta;
this.locals = locals;
this.stack = stack;
}
@Override
void applyTo(TypeArray localTypes, TypeArray stackTypes) {
localTypes.setAll(locals);
stackTypes.setAll(stack);
}
@Override
public String toString() {
return toString("FULL", offsetDelta, locals, stack);
}
}
static StackMapTableData getInstance(DataInputStream in, MethodData method)
throws IOException {
int frameType = in.readUnsignedByte();
if (frameType < SAME_FRAME_BOUND) {
// same_frame
return new SameFrame(frameType, frameType);
} else if (SAME_FRAME_BOUND <= frameType && frameType < SAME_LOCALS_1_STACK_ITEM_BOUND) {
// same_locals_1_stack_item_frame
// read additional single stack element
return new SameLocals1StackItem(frameType,
(frameType - SAME_FRAME_BOUND),
StackMapData.readTypeArray(in, 1, method));
} else if (frameType == SAME_LOCALS_1_STACK_ITEM_EXTENDED) {
// same_locals_1_stack_item_extended
return new SameLocals1StackItem(frameType,
in.readUnsignedShort(),
StackMapData.readTypeArray(in, 1, method));
} else if (SAME_LOCALS_1_STACK_ITEM_EXTENDED < frameType && frameType < SAME_FRAME_EXTENDED) {
// chop_frame or same_frame_extended
return new ChopFrame(frameType, in.readUnsignedShort());
} else if (frameType == SAME_FRAME_EXTENDED) {
// chop_frame or same_frame_extended
return new SameFrame(frameType, in.readUnsignedShort());
} else if (SAME_FRAME_EXTENDED < frameType && frameType < FULL_FRAME) {
// append_frame
return new AppendFrame(frameType, in.readUnsignedShort(),
StackMapData.readTypeArray(in, frameType - SAME_FRAME_EXTENDED, method));
} else if (frameType == FULL_FRAME) {
// full_frame
int offsetDelta = in.readUnsignedShort();
int locals_size = in.readUnsignedShort();
int[] locals = StackMapData.readTypeArray(in, locals_size, method);
int stack_size = in.readUnsignedShort();
int[] stack = StackMapData.readTypeArray(in, stack_size, method);
return new FullFrame(offsetDelta, locals, stack);
} else {
throw new ClassFormatError("unrecognized frame_type in StackMapTable");
}
}
}
/**
* Stores exception table data in code attribute.
*
* @author Sucheta Dambalkar (Adopted code from jdis)
*/
static final class TrapData {
public final short start_pc;
public final short end_pc;
public final short handler_pc;
public final short catch_cpx;
final int num;
/**
* Read and store exception table data in code attribute.
*/
TrapData(DataInputStream in, int num) throws IOException {
this.num = num;
start_pc = in.readShort();
end_pc = in.readShort();
handler_pc = in.readShort();
catch_cpx = in.readShort();
}
/**
* returns recommended identifier
*/
public String ident() {
return "t" + num;
}
}
/**
*
* @author Jaroslav Tulach
*/
static final class TrapDataIterator {
private final Hashtable exStart = new Hashtable();
private final Hashtable exStop = new Hashtable();
private TrapData[] current = new TrapData[10];
private int currentCount;
TrapDataIterator(Vector exceptionTable) {
for (int i = 0; i < exceptionTable.size(); i++) {
final TrapData td = (TrapData) exceptionTable.elementAt(i);
put(exStart, td.start_pc, td);
put(exStop, td.end_pc, td);
}
}
private static void put(Hashtable h, short key, TrapData td) {
Short s = Short.valueOf((short) key);
Vector v = (Vector) h.get(s);
if (v == null) {
v = new Vector(1);
h.put(s, v);
}
v.add(td);
}
private boolean processAll(Hashtable h, Short key, boolean add) {
boolean change = false;
Vector v = (Vector) h.get(key);
if (v != null) {
int s = v.size();
for (int i = 0; i < s; i++) {
TrapData td = (TrapData) v.elementAt(i);
if (add) {
add(td);
change = true;
} else {
remove(td);
change = true;
}
}
}
return change;
}
public boolean advanceTo(int i) {
Short s = Short.valueOf((short) i);
boolean ch1 = processAll(exStart, s, true);
boolean ch2 = processAll(exStop, s, false);
return ch1 || ch2;
}
public boolean useTry() {
return currentCount > 0;
}
public TrapData[] current() {
TrapData[] copy = new TrapData[currentCount];
for (int i = 0; i < currentCount; i++) {
copy[i] = current[i];
}
return copy;
}
private void add(TrapData e) {
if (currentCount == current.length) {
TrapData[] data = new TrapData[currentCount * 2];
for (int i = 0; i < currentCount; i++) {
data[i] = current[i];
}
current = data;
}
current[currentCount++] = e;
}
private void remove(TrapData e) {
if (currentCount == 0) {
return;
}
int from = 0;
while (from < currentCount) {
if (e == current[from++]) {
break;
}
}
while (from < currentCount) {
current[from - 1] = current[from];
current[from] = null;
from++;
}
currentCount--;
}
}
static final class TypeArray {
private static final int CAPACITY_INCREMENT = 16;
private int[] types;
private int size;
public TypeArray() {
}
public TypeArray(final TypeArray initialTypes) {
setAll(initialTypes);
}
public void add(final int newType) {
ensureCapacity(size + 1);
types[size++] = newType;
}
public void addAll(final TypeArray newTypes) {
addAll(newTypes.types, 0, newTypes.size);
}
public void addAll(final int[] newTypes) {
addAll(newTypes, 0, newTypes.length);
}
public void addAll(final int[] newTypes,
final int offset,
final int count) {
if (count > 0) {
ensureCapacity(size + count);
arraycopy(newTypes, offset, types, size, count);
size += count;
}
}
public void set(final int index, final int newType) {
types[index] = newType;
}
public void setAll(final TypeArray newTypes) {
setAll(newTypes.types, 0, newTypes.size);
}
public void setAll(final int[] newTypes) {
setAll(newTypes, 0, newTypes.length);
}
public void setAll(final int[] newTypes,
final int offset,
final int count) {
if (count > 0) {
ensureCapacity(count);
arraycopy(newTypes, offset, types, 0, count);
size = count;
} else {
clear();
}
}
public void setSize(final int newSize) {
if (size != newSize) {
ensureCapacity(newSize);
for (int i = size; i < newSize; ++i) {
types[i] = 0;
}
size = newSize;
}
}
public void clear() {
size = 0;
}
public int getSize() {
return size;
}
public int get(final int index) {
return types[index];
}
public static String typeString(final int type) {
switch (type & 0xff) {
case ITEM_Bogus:
return "_top_";
case ITEM_Integer:
return "_int_";
case ITEM_Float:
return "_float_";
case ITEM_Double:
return "_double_";
case ITEM_Long:
return "_long_";
case ITEM_Null:
return "_null_";
case ITEM_InitObject: // UninitializedThis
return "_init_";
case ITEM_Object:
return "_object_";
case ITEM_NewObject: // Uninitialized
return "_new_";
default:
throw new IllegalArgumentException("Unknown type");
}
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("[");
String sep = "";
for (int i = 0; i < size; ++i) {
sb.append(sep).append(VarType.toString(types[i] & 0xff));
sep = ", ";
}
return sb.append(']').toString();
}
private void ensureCapacity(final int minCapacity) {
if ((minCapacity == 0)
|| (types != null) && (minCapacity <= types.length)) {
return;
}
final int newCapacity =
((minCapacity + CAPACITY_INCREMENT - 1) / CAPACITY_INCREMENT)
* CAPACITY_INCREMENT;
final int[] newTypes = new int[newCapacity];
if (size > 0) {
arraycopy(types, 0, newTypes, 0, size);
}
types = newTypes;
}
// no System.arraycopy
private void arraycopy(final int[] src, final int srcPos,
final int[] dest, final int destPos,
final int length) {
for (int i = 0; i < length; ++i) {
dest[destPos + i] = src[srcPos + i];
}
}
}
/**
* A JavaScript ready replacement for java.util.Vector
*
* @author Jaroslav Tulach
*/
@JavaScriptPrototype(prototype = "new Array")
private static final class Vector {
private Object[] arr;
Vector() {
}
Vector(int i) {
}
void add(Object objectType) {
addElement(objectType);
}
@JavaScriptBody(args = {"obj"}, body =
"this.push(obj);")
void addElement(Object obj) {
final int s = size();
setSize(s + 1);
setElementAt(obj, s);
}
@JavaScriptBody(args = {}, body =
"return this.length;")
int size() {
return arr == null ? 0 : arr.length;
}
@JavaScriptBody(args = {"newArr"}, body =
"for (var i = 0; i < this.length; i++) {\n"
+ " newArr[i] = this[i];\n"
+ "}\n")
void copyInto(Object[] newArr) {
if (arr == null) {
return;
}
int min = Math.min(newArr.length, arr.length);
for (int i = 0; i < min; i++) {
newArr[i] = arr[i];
}
}
@JavaScriptBody(args = {"index"}, body =
"return this[index];")
Object elementAt(int index) {
return arr[index];
}
private void setSize(int len) {
Object[] newArr = new Object[len];
copyInto(newArr);
arr = newArr;
}
@JavaScriptBody(args = {"val", "index"}, body =
"this[index] = val;")
void setElementAt(Object val, int index) {
arr[index] = val;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy