| /* |
| * 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" |
| }; |
| |
| } |