blob: 26d4dc97909b7ea63b138d62325809e83ab30de9 [file] [log] [blame]
/*
* Copyright (c) 2010, 2013, 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 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;
/**
* Subclass of the ASM class reader that retains more info, such
* as bytecode offsets
*/
public class NashornClassReader extends ClassReader {
private final Map<String, List<Label>> labelMap = new HashMap<>();
/**
* Constructor
* @param bytecode bytecode for class
*/
public NashornClassReader(final byte[] bytecode) {
super(bytecode);
parse(bytecode);
}
List<Label> getExtraLabels(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 = c & 0xFF;
if (c < 0x80) { // 0xxxxxxx
buf[strLen++] = (char) c;
} else if (c < 0xE0 && c > 0xBF) { // 110x xxxx 10xx xxxx
cc = (char) (c & 0x1F);
st = 1;
} else { // 1110 xxxx 10xx xxxx 10xx xxxx
cc = (char) (c & 0x0F);
st = 2;
}
break;
case 1: // byte 2 of 2-byte char or byte 3 of 3-byte char
buf[strLen++] = (char) ((cc << 6) | (c & 0x3F));
st = 0;
break;
case 2: // byte 2 of 3-byte char
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; //magic
assert magic == 0xcafebabe : Integer.toHexString(magic);
readShort(bytecode, u); //minor
u += 2;
readShort(bytecode, u); //major
u += 2; //minor
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++) {
//constant pool entries
final int tag = readByte(bytecode, u);
u += 1;
switch (tag) {
case 7: //class
cp.add(new IndexInfo(cp, tag, readShort(bytecode, u)));
u += 2;
break;
case 9: //fieldref
case 10: //methodref
case 11: //interfacemethodref
cp.add(new IndexInfo2(cp, tag, readShort(bytecode, u), readShort(bytecode, u + 2)));
u += 4;
break;
case 8: //string
cp.add(new IndexInfo(cp, tag, readShort(bytecode, u))); //string index
u += 2;
break;
case 3: //int
cp.add(new DirectInfo<>(cp, tag, readInt(bytecode, u)));
u += 4;
break;
case 4: //float
cp.add(new DirectInfo<>(cp, tag, Float.intBitsToFloat(readInt(bytecode, u))));
u += 4;
break;
case 5: //long
cp.add(new DirectInfo<>(cp, tag, readLong(bytecode, u)));
cp.add(null);
i++;
u += 8;
break;
case 6: //double
cp.add(new DirectInfo<>(cp, tag, Double.longBitsToDouble(readLong(bytecode, u))));
cp.add(null);
i++;
u += 8;
break;
case 12: //name and type
cp.add(new IndexInfo2(cp, tag, readShort(bytecode, u), readShort(bytecode, u + 2)));
u += 4;
break;
case 1: //utf8
final int len = readShort(bytecode, u);
u += 2;
cp.add(new DirectInfo<>(cp, tag, readUTF(u, len, bytecode)));
u += len;
break;
case 16: //methodtype
cp.add(new IndexInfo(cp, tag, readShort(bytecode, u)));
u += 2;
break;
case 18: //indy
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: //methodhandle
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); //access flags
u += 2; //access
final int cls = readShort(bytecode, u);
u += 2; //this_class
thisClassName = cp.get(cls).toString();
u += 2; //super
final int ifc = readShort(bytecode, u);
u += 2;
u += ifc * 2;
final int fc = readShort(bytecode, u);
u += 2; //fields
for (int i = 0 ; i < fc ; i++) {
u += 2; //access
readShort(bytecode, u); //fieldname
u += 2; //name
u += 2; //descriptor
final int ac = readShort(bytecode, u);
u += 2;
//field attributes
for (int j = 0; j < ac; j++) {
u += 2; //attribute name
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; //access
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;
//method attributes
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; //max stack
readShort(bytecode, u);
u += 2; //max locals
final int len = readInt(bytecode, u);
u += 4;
parseCode(bytecode, u, len, fullyQualifiedName(thisClassName, methodName, methodDesc));
u += len;
final int elen = readShort(bytecode, u); //exception table length
u += 2;
u += elen * 8;
//method attributes
final int ac2 = readShort(bytecode, u);
u += 2;
for (int k = 0; k < ac2; k++) {
u += 2; //name;
final int aclen = readInt(bytecode, u);
u += 4; //length
u += aclen; //bytes;
}
} else {
u += attrLen;
}
}
}
final int ac = readShort(bytecode, u);
u += 2;
//other attributes
for (int i = 0 ; i < ac ; i++) {
readShort(bytecode, u); //name index
u += 2;
final int len = readInt(bytecode, u);
u += 4;
u += len;
//attribute
}
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
wide = true;
i += 1;
break;
case 0xa9: //ret
i += wide ? 4 : 2;
break;
case 0xab: //lookupswitch
i += 1;
while (((i - index) & 3) != 0) {
i++;
}
readInt(bytecode, i);
i += 4; //defaultbyte
final int npairs = readInt(bytecode, i);
i += 4;
i += 8 * npairs;
break;
case 0xaa: //tableswitch
i += 1;
while (((i - index) & 3) != 0) {
i++;
}
readInt(bytecode, i); //default
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: //multianewarray
i += 4;
break;
case 0x19: //aload (wide)
case 0x18: //dload
case 0x17: //fload
case 0x15: //iload
case 0x16: //lload
case 0x3a: //astore wide
case 0x39: //dstore
case 0x38: //fstore
case 0x36: //istore
case 0x37: //lstore
i += wide ? 3 : 2;
break;
case 0x10: //bipush
case 0x12: //ldc
case 0xbc: //anewarrayu
i += 2;
break;
case 0xb4: //getfield
case 0xb2: //getstatic
case 0xbd: //anewarray
case 0xc0: //checkcast
case 0xa5: //ifacmp_eq
case 0xa6: //ifacmp_ne
case 0x9f: //all ifs and ifcmps
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: //instanceof
case 0xa7: //goto
case 0xb7: //special
case 0xb8: //static
case 0xb6: //virtual
case 0xa8: //jsr
case 0x13: //ldc_w
case 0x14: //ldc2_w
case 0xbb: //new
case 0xb5: //putfield
case 0xb3: //putstatic
case 0x11: //sipush
i += 3;
break;
case 0x84: //iinc (wide)
i += wide ? 5 : 3;
break;
case 0xba: //indy
case 0xb9: //interface
case 0xc8:
case 0xc9: //jsr_w
i += 5; //goto_w
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();// + " [class=" + info.getClass().getSimpleName() + ']';
}
}
private static String type[] = {
//0
"<error>",
//1
"UTF8",
//2
"<error>",
//3
"Integer",
//4
"Float",
//5
"Long",
//6
"Double",
//7
"Class",
//8
"String",
//9
"Fieldref",
//10
"Methodref",
//11
"InterfaceMethodRef",
//12
"NameAndType",
//13
"<error>",
//14
"<error>",
//15
"MethodHandle",
//16
"MethodType",
//17
"<error>",
//18
"Invokedynamic"
};
}