package jdk.nashorn.internal.ir.debug;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import jdk.internal.org.objectweb.asm.Attribute;
import jdk.internal.org.objectweb.asm.ClassReader;
import jdk.internal.org.objectweb.asm.ClassVisitor;
import jdk.internal.org.objectweb.asm.Label;
import jdk.nashorn.internal.ir.debug.NashornTextifier.NashornLabel;
public class NashornClassReader extends ClassReader {
private final Map<String, List<Label>> labelMap = new HashMap<>();
public NashornClassReader(final byte[] bytecode) {
super(bytecode);
parse(bytecode);
}
List<Label> (final String className, final String methodName, final String methodDesc) {
final String key = fullyQualifiedName(className, methodName, methodDesc);
return labelMap.get(key);
}
private static int readByte(final byte[] bytecode, final int index) {
return (byte)(bytecode[index] & 0xff);
}
private static int readShort(final byte[] bytecode, final int index) {
return (short)((bytecode[index] & 0xff) << 8) | (bytecode[index + 1] & 0xff);
}
private static int readInt(final byte[] bytecode, final int index) {
return ((bytecode[index] & 0xff) << 24) | ((bytecode[index + 1] & 0xff) << 16) | ((bytecode[index + 2] & 0xff) << 8) | (bytecode[index + 3] & 0xff);
}
private static long readLong(final byte[] bytecode, final int index) {
final int hi = readInt(bytecode, index);
final int lo = readInt(bytecode, index + 4);
return ((long)hi << 32) | lo;
}
private static String readUTF(final int index, final int utfLen, final byte[] bytecode) {
final int endIndex = index + utfLen;
final char buf[] = new char[utfLen * 2];
int strLen = 0;
int c;
int st = 0;
char cc = 0;
int i = index;
while (i < endIndex) {
c = bytecode[i++];
switch (st) {
case 0:
c &= 0xFF;
if (c < 0x80) {
buf[strLen++] = (char) c;
} else if (c < 0xE0 && c > 0xBF) {
cc = (char) (c & 0x1F);
st = 1;
} else {
cc = (char) (c & 0x0F);
st = 2;
}
break;
case 1:
buf[strLen++] = (char) ((cc << 6) | (c & 0x3F));
st = 0;
break;
case 2:
cc = (char) ((cc << 6) | (c & 0x3F));
st = 1;
break;
default:
break;
}
}
return new String(buf, 0, strLen);
}
private String parse(final byte[] bytecode) {
String thisClassName;
int u = 0;
final int magic = readInt(bytecode, u);
u += 4;
assert magic == 0xcafebabe : Integer.toHexString(magic);
readShort(bytecode, u);
u += 2;
readShort(bytecode, u);
u += 2;
final int cpc = readShort(bytecode, u);
u += 2;
final ArrayList<Constant> cp = new ArrayList<>(cpc);
cp.add(null);
for (int i = 1; i < cpc; i++) {
final int tag = readByte(bytecode, u);
u += 1;
switch (tag) {
case 7:
cp.add(new IndexInfo(cp, tag, readShort(bytecode, u)));
u += 2;
break;
case 9:
case 10:
case 11:
cp.add(new IndexInfo2(cp, tag, readShort(bytecode, u), readShort(bytecode, u + 2)));
u += 4;
break;
case 8:
cp.add(new IndexInfo(cp, tag, readShort(bytecode, u)));
u += 2;
break;
case 3:
cp.add(new DirectInfo<>(cp, tag, readInt(bytecode, u)));
u += 4;
break;
case 4:
cp.add(new DirectInfo<>(cp, tag, Float.intBitsToFloat(readInt(bytecode, u))));
u += 4;
break;
case 5:
cp.add(new DirectInfo<>(cp, tag, readLong(bytecode, u)));
cp.add(null);
i++;
u += 8;
break;
case 6:
cp.add(new DirectInfo<>(cp, tag, Double.longBitsToDouble(readLong(bytecode, u))));
cp.add(null);
i++;
u += 8;
break;
case 12:
cp.add(new IndexInfo2(cp, tag, readShort(bytecode, u), readShort(bytecode, u + 2)));
u += 4;
break;
case 1:
final int len = readShort(bytecode, u);
u += 2;
cp.add(new DirectInfo<>(cp, tag, readUTF(u, len, bytecode)));
u += len;
break;
case 16:
cp.add(new IndexInfo(cp, tag, readShort(bytecode, u)));
u += 2;
break;
case 18:
cp.add(new IndexInfo2(cp, tag, readShort(bytecode, u), readShort(bytecode, u + 2)) {
@Override
public String toString() {
return "#" + index + ' ' + cp.get(index2).toString();
}
});
u += 4;
break;
case 15:
final int kind = readByte(bytecode, u);
assert kind >= 1 && kind <= 9 : kind;
cp.add(new IndexInfo2(cp, tag, kind, readShort(bytecode, u + 1)) {
@Override
public String toString() {
return "#" + index + ' ' + cp.get(index2).toString();
}
});
u += 3;
break;
default:
assert false : tag;
break;
}
}
readShort(bytecode, u);
u += 2;
final int cls = readShort(bytecode, u);
u += 2;
thisClassName = cp.get(cls).toString();
u += 2;
final int ifc = readShort(bytecode, u);
u += 2;
u += ifc * 2;
final int fc = readShort(bytecode, u);
u += 2;
for (int i = 0 ; i < fc ; i++) {
u += 2;
readShort(bytecode, u);
u += 2;
u += 2;
final int ac = readShort(bytecode, u);
u += 2;
for (int j = 0; j < ac; j++) {
u += 2;
final int len = readInt(bytecode, u);
u += 4;
u += len;
}
}
final int mc = readShort(bytecode, u);
u += 2;
for (int i = 0 ; i < mc ; i++) {
readShort(bytecode, u);
u += 2;
final int methodNameIndex = readShort(bytecode, u);
u += 2;
final String methodName = cp.get(methodNameIndex).toString();
final int methodDescIndex = readShort(bytecode, u);
u += 2;
final String methodDesc = cp.get(methodDescIndex).toString();
final int ac = readShort(bytecode, u);
u += 2;
for (int j = 0; j < ac; j++) {
final int nameIndex = readShort(bytecode, u);
u += 2;
final String attrName = cp.get(nameIndex).toString();
final int attrLen = readInt(bytecode, u);
u += 4;
if ("Code".equals(attrName)) {
readShort(bytecode, u);
u += 2;
readShort(bytecode, u);
u += 2;
final int len = readInt(bytecode, u);
u += 4;
parseCode(bytecode, u, len, fullyQualifiedName(thisClassName, methodName, methodDesc));
u += len;
final int elen = readShort(bytecode, u);
u += 2;
u += elen * 8;
final int ac2 = readShort(bytecode, u);
u += 2;
for (int k = 0; k < ac2; k++) {
u += 2;
final int aclen = readInt(bytecode, u);
u += 4;
u += aclen;
}
} else {
u += attrLen;
}
}
}
final int ac = readShort(bytecode, u);
u += 2;
for (int i = 0 ; i < ac ; i++) {
readShort(bytecode, u);
u += 2;
final int len = readInt(bytecode, u);
u += 4;
u += len;
}
return thisClassName;
}
private static String fullyQualifiedName(final String className, final String methodName, final String methodDesc) {
return className + '.' + methodName + methodDesc;
}
private void parseCode(final byte[] bytecode, final int index, final int len, final String desc) {
final List<Label> labels = new ArrayList<>();
labelMap.put(desc, labels);
boolean wide = false;
for (int i = index; i < index + len;) {
final int opcode = bytecode[i];
labels.add(new NashornLabel(opcode, i - index));
switch (opcode & 0xff) {
case 0xc4:
wide = true;
i += 1;
break;
case 0xa9:
i += wide ? 4 : 2;
break;
case 0xab:
i += 1;
while (((i - index) & 3) != 0) {
i++;
}
readInt(bytecode, i);
i += 4;
final int npairs = readInt(bytecode, i);
i += 4;
i += 8 * npairs;
break;
case 0xaa:
i += 1;
while (((i - index) & 3) != 0) {
i++;
}
readInt(bytecode, i);
i += 4;
final int lo = readInt(bytecode, i);
i += 4;
final int hi = readInt(bytecode, i);
i += 4;
i += 4 * (hi - lo + 1);
break;
case 0xc5:
i += 4;
break;
case 0x19:
case 0x18:
case 0x17:
case 0x15:
case 0x16:
case 0x3a:
case 0x39:
case 0x38:
case 0x36:
case 0x37:
i += wide ? 3 : 2;
break;
case 0x10:
case 0x12:
case 0xbc:
i += 2;
break;
case 0xb4:
case 0xb2:
case 0xbd:
case 0xc0:
case 0xa5:
case 0xa6:
case 0x9f:
case 0xa0:
case 0xa1:
case 0xa2:
case 0xa3:
case 0xa4:
case 0x99:
case 0x9a:
case 0x9b:
case 0x9c:
case 0x9d:
case 0x9e:
case 0xc7:
case 0xc6:
case 0xc1:
case 0xa7:
case 0xb7:
case 0xb8:
case 0xb6:
case 0xa8:
case 0x13:
case 0x14:
case 0xbb:
case 0xb5:
case 0xb3:
case 0x11:
i += 3;
break;
case 0x84:
i += wide ? 5 : 3;
break;
case 0xba:
case 0xb9:
case 0xc8:
case 0xc9:
i += 5;
break;
default:
i++;
break;
}
if (wide) {
wide = false;
}
}
}
@Override
public void accept(final ClassVisitor classVisitor, final Attribute[] attrs, final int flags) {
super.accept(classVisitor, attrs, flags);
}
@Override
protected Label readLabel(final int offset, final Label[] labels) {
final Label label = super.readLabel(offset, labels);
label.info = offset;
return label;
}
private abstract static class Constant {
protected ArrayList<Constant> cp;
protected int tag;
protected Constant(final ArrayList<Constant> cp, final int tag) {
this.cp = cp;
this.tag = tag;
}
@SuppressWarnings("unused")
final String getType() {
String str = TYPE[tag];
while (str.length() < 16) {
str += " ";
}
return str;
}
}
private static class IndexInfo extends Constant {
protected final int index;
IndexInfo(final ArrayList<Constant> cp, final int tag, final int index) {
super(cp, tag);
this.index = index;
}
@Override
public String toString() {
return cp.get(index).toString();
}
}
private static class IndexInfo2 extends IndexInfo {
protected final int index2;
IndexInfo2(final ArrayList<Constant> cp, final int tag, final int index, final int index2) {
super(cp, tag, index);
this.index2 = index2;
}
@Override
public String toString() {
return super.toString() + ' ' + cp.get(index2).toString();
}
}
private static class DirectInfo<T> extends Constant {
protected final T info;
DirectInfo(final ArrayList<Constant> cp, final int tag, final T info) {
super(cp, tag);
this.info = info;
}
@Override
public String toString() {
return info.toString();
}
}
private static final String[] TYPE = {
"<error>",
"UTF8",
"<error>",
"Integer",
"Float",
"Long",
"Double",
"Class",
"String",
"Fieldref",
"Methodref",
"InterfaceMethodRef",
"NameAndType",
"<error>",
"<error>",
"MethodHandle",
"MethodType",
"<error>",
"Invokedynamic"
};
}