NashornClassReader.java revision 1350:3cb11f4d617e
1/*
2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package jdk.nashorn.internal.ir.debug;
27
28import java.util.ArrayList;
29import java.util.HashMap;
30import java.util.List;
31import java.util.Map;
32import jdk.internal.org.objectweb.asm.Attribute;
33import jdk.internal.org.objectweb.asm.ClassReader;
34import jdk.internal.org.objectweb.asm.ClassVisitor;
35import jdk.internal.org.objectweb.asm.Label;
36import jdk.nashorn.internal.ir.debug.NashornTextifier.NashornLabel;
37
38/**
39 * Subclass of the ASM classs reader that retains more info, such
40 * as bytecode offsets
41 */
42public class NashornClassReader extends ClassReader {
43
44    private final Map<String, List<Label>> labelMap = new HashMap<>();
45
46    /**
47     * Constructor
48     * @param bytecode bytecode for class
49     */
50    public NashornClassReader(final byte[] bytecode) {
51        super(bytecode);
52        parse(bytecode);
53    }
54
55    List<Label> getExtraLabels(final String className, final String methodName, final String methodDesc) {
56        final String key = fullyQualifiedName(className, methodName, methodDesc);
57        return labelMap.get(key);
58    }
59
60    private static int readByte(final byte[] bytecode, final int index) {
61        return (byte)(bytecode[index] & 0xff);
62    }
63
64    private static int readShort(final byte[] bytecode, final int index) {
65        return (short)((bytecode[index] & 0xff) << 8) | (bytecode[index + 1] & 0xff);
66    }
67
68    private static int readInt(final byte[] bytecode, final int index) {
69        return ((bytecode[index] & 0xff) << 24) | ((bytecode[index + 1] & 0xff) << 16) | ((bytecode[index + 2] & 0xff) << 8) | (bytecode[index + 3] & 0xff);
70    }
71
72    private static long readLong(final byte[] bytecode, final int index) {
73        final int hi = readInt(bytecode, index);
74        final int lo = readInt(bytecode, index + 4);
75        return ((long)hi << 32) | lo;
76    }
77
78    private static String readUTF(final int index, final int utfLen, final byte[] bytecode) {
79        final int endIndex = index + utfLen;
80        final char buf[] = new char[utfLen * 2];
81        int strLen = 0;
82        int c;
83        int st = 0;
84        char cc = 0;
85        int i = index;
86
87        while (i < endIndex) {
88            c = bytecode[i++];
89            switch (st) {
90            case 0:
91                c &= 0xFF;
92                if (c < 0x80) { // 0xxxxxxx
93                    buf[strLen++] = (char) c;
94                } else if (c < 0xE0 && c > 0xBF) { // 110x xxxx 10xx xxxx
95                    cc = (char) (c & 0x1F);
96                    st = 1;
97                } else { // 1110 xxxx 10xx xxxx 10xx xxxx
98                    cc = (char) (c & 0x0F);
99                    st = 2;
100                }
101                break;
102
103            case 1: // byte 2 of 2-byte char or byte 3 of 3-byte char
104                buf[strLen++] = (char) ((cc << 6) | (c & 0x3F));
105                st = 0;
106                break;
107
108            case 2: // byte 2 of 3-byte char
109                cc = (char) ((cc << 6) | (c & 0x3F));
110                st = 1;
111                break;
112
113            default:
114                break;
115            }
116        }
117        return new String(buf, 0, strLen);
118    }
119
120    private String parse(final byte[] bytecode) {
121        String thisClassName;
122
123        int u = 0;
124
125        final int magic = readInt(bytecode, u);
126        u += 4; //magic
127        assert magic == 0xcafebabe : Integer.toHexString(magic);
128        readShort(bytecode, u); //minor
129        u += 2;
130        readShort(bytecode, u); //major
131        u += 2; //minor
132
133        final int cpc = readShort(bytecode, u);
134        u += 2;
135        final ArrayList<Constant> cp = new ArrayList<>(cpc);
136        cp.add(null);
137
138        for (int i = 1; i < cpc; i++) {
139            //constant pool entries
140            final int tag = readByte(bytecode, u);
141            u += 1;
142            switch (tag) {
143            case 7: //class
144                cp.add(new IndexInfo(cp, tag, readShort(bytecode, u)));
145                u += 2;
146                break;
147            case 9:  //fieldref
148            case 10: //methodref
149            case 11: //interfacemethodref
150                cp.add(new IndexInfo2(cp, tag, readShort(bytecode, u), readShort(bytecode, u + 2)));
151                u += 4;
152               break;
153            case 8: //string
154                cp.add(new IndexInfo(cp, tag, readShort(bytecode, u))); //string index
155                u += 2;
156                break;
157            case 3:  //int
158                cp.add(new DirectInfo<>(cp, tag, readInt(bytecode, u)));
159                u += 4;
160                break;
161            case 4:  //float
162                cp.add(new DirectInfo<>(cp, tag, Float.intBitsToFloat(readInt(bytecode, u))));
163                u += 4;
164                break;
165            case 5:  //long
166                cp.add(new DirectInfo<>(cp, tag, readLong(bytecode, u)));
167                cp.add(null);
168                i++;
169                u += 8;
170                break;
171            case 6:  //double
172                cp.add(new DirectInfo<>(cp, tag, Double.longBitsToDouble(readLong(bytecode, u))));
173                cp.add(null);
174                i++;
175                u += 8;
176                break;
177            case 12: //name and type
178                cp.add(new IndexInfo2(cp, tag, readShort(bytecode, u), readShort(bytecode, u + 2)));
179                u += 4;
180                break;
181            case 1:  //utf8
182                final int len = readShort(bytecode, u);
183                u += 2;
184                cp.add(new DirectInfo<>(cp, tag, readUTF(u, len, bytecode)));
185                u += len;
186                break;
187            case 16: //methodtype
188                cp.add(new IndexInfo(cp, tag, readShort(bytecode, u)));
189                u += 2;
190                break;
191            case 18: //indy
192                cp.add(new IndexInfo2(cp, tag, readShort(bytecode, u), readShort(bytecode, u + 2)) {
193                    @Override
194                    public String toString() {
195                        return "#" + index + ' ' + cp.get(index2).toString();
196                    }
197
198                });
199                u += 4;
200                break;
201            case 15: //methodhandle
202                final int kind = readByte(bytecode, u);
203                assert kind >= 1 && kind <= 9 : kind;
204                cp.add(new IndexInfo2(cp, tag, kind, readShort(bytecode, u + 1)) {
205                    @Override
206                    public String toString() {
207                        return "#" + index + ' ' + cp.get(index2).toString();
208                    }
209                });
210
211                u += 3;
212                break;
213            default:
214                assert false : tag;
215                break;
216            }
217        }
218
219        readShort(bytecode, u); //access flags
220        u += 2; //access
221        final int cls = readShort(bytecode, u);
222        u += 2; //this_class
223        thisClassName = cp.get(cls).toString();
224        u += 2; //super
225
226        final int ifc = readShort(bytecode, u);
227        u += 2;
228        u += ifc * 2;
229
230        final int fc = readShort(bytecode, u);
231        u += 2; //fields
232
233        for (int i = 0 ; i < fc ; i++) {
234            u += 2; //access
235            readShort(bytecode, u); //fieldname
236            u += 2; //name
237            u += 2; //descriptor
238            final int ac = readShort(bytecode, u);
239            u += 2;
240            //field attributes
241            for (int j = 0; j < ac; j++) {
242                u += 2; //attribute name
243                final int len = readInt(bytecode, u);
244                u += 4;
245                u += len;
246            }
247        }
248
249        final int mc = readShort(bytecode, u);
250        u += 2;
251        for (int i = 0 ; i < mc ; i++) {
252            readShort(bytecode, u);
253            u += 2; //access
254
255            final int methodNameIndex = readShort(bytecode, u);
256            u += 2;
257            final String methodName = cp.get(methodNameIndex).toString();
258
259            final int methodDescIndex = readShort(bytecode, u);
260            u += 2;
261            final String methodDesc = cp.get(methodDescIndex).toString();
262
263            final int ac = readShort(bytecode, u);
264            u += 2;
265
266            //method attributes
267            for (int j = 0; j < ac; j++) {
268                final int nameIndex = readShort(bytecode, u);
269                u += 2;
270                final String attrName = cp.get(nameIndex).toString();
271
272                final int attrLen = readInt(bytecode, u);
273                u += 4;
274
275                if ("Code".equals(attrName)) {
276                    readShort(bytecode, u);
277                    u += 2; //max stack
278                    readShort(bytecode, u);
279                    u += 2; //max locals
280                    final int len = readInt(bytecode, u);
281                    u += 4;
282                    parseCode(bytecode, u, len, fullyQualifiedName(thisClassName, methodName, methodDesc));
283                    u += len;
284                    final int elen = readShort(bytecode, u); //exception table length
285                    u += 2;
286                    u += elen * 8;
287
288                    //method attributes
289                    final int ac2 = readShort(bytecode, u);
290                    u += 2;
291                    for (int k = 0; k < ac2; k++) {
292                        u += 2; //name;
293                        final int aclen = readInt(bytecode, u);
294                        u += 4; //length
295                        u += aclen; //bytes;
296                    }
297                } else {
298                    u += attrLen;
299                }
300            }
301        }
302
303        final int ac = readShort(bytecode, u);
304        u += 2;
305        //other attributes
306        for (int i = 0 ; i < ac ; i++) {
307            readShort(bytecode, u); //name index
308            u += 2;
309            final int len = readInt(bytecode, u);
310            u += 4;
311            u += len;
312            //attribute
313        }
314
315        return thisClassName;
316    }
317
318    private static String fullyQualifiedName(final String className, final String methodName, final String methodDesc) {
319        return className + '.' + methodName + methodDesc;
320    }
321
322    private void parseCode(final byte[] bytecode, final int index, final int len, final String desc) {
323        final List<Label> labels = new ArrayList<>();
324        labelMap.put(desc, labels);
325
326        boolean wide = false;
327
328        for (int i = index; i < index + len;) {
329            final int opcode = bytecode[i];
330            labels.add(new NashornLabel(opcode, i - index));
331
332            switch (opcode & 0xff) {
333            case 0xc4: //wide
334                wide = true;
335                i += 1;
336                break;
337            case 0xa9: //ret
338                i += wide ? 4 : 2;
339                break;
340            case 0xab: //lookupswitch
341                i += 1;
342                while (((i - index) & 3) != 0) {
343                    i++;
344                }
345                readInt(bytecode, i);
346                i += 4; //defaultbyte
347                final int npairs = readInt(bytecode, i);
348                i += 4;
349                i += 8 * npairs;
350                break;
351            case 0xaa: //tableswitch
352                i += 1;
353                while (((i - index) & 3) != 0) {
354                    i++;
355                }
356                readInt(bytecode, i); //default
357                i += 4;
358                final int lo = readInt(bytecode, i);
359                i += 4;
360                final int hi = readInt(bytecode, i);
361                i += 4;
362                i += 4 * (hi - lo + 1);
363                break;
364            case 0xc5: //multianewarray
365                i += 4;
366                break;
367            case 0x19: //aload (wide)
368            case 0x18: //dload
369            case 0x17: //fload
370            case 0x15: //iload
371            case 0x16: //lload
372            case 0x3a: //astore wide
373            case 0x39: //dstore
374            case 0x38: //fstore
375            case 0x36: //istore
376            case 0x37: //lstore
377                i += wide ? 3 : 2;
378                break;
379            case 0x10: //bipush
380            case 0x12: //ldc
381            case 0xbc: //anewarrayu
382                i += 2;
383                break;
384            case 0xb4: //getfield
385            case 0xb2: //getstatic
386            case 0xbd: //anewarray
387            case 0xc0: //checkcast
388            case 0xa5: //ifacmp_eq
389            case 0xa6: //ifacmp_ne
390            case 0x9f: //all ifs and ifcmps
391            case 0xa0:
392            case 0xa1:
393            case 0xa2:
394            case 0xa3:
395            case 0xa4:
396            case 0x99:
397            case 0x9a:
398            case 0x9b:
399            case 0x9c:
400            case 0x9d:
401            case 0x9e:
402            case 0xc7:
403            case 0xc6:
404            case 0xc1: //instanceof
405            case 0xa7: //goto
406            case 0xb7: //special
407            case 0xb8: //static
408            case 0xb6: //virtual
409            case 0xa8: //jsr
410            case 0x13: //ldc_w
411            case 0x14: //ldc2_w
412            case 0xbb: //new
413            case 0xb5: //putfield
414            case 0xb3: //putstatic
415            case 0x11: //sipush
416                i += 3;
417                break;
418            case 0x84: //iinc (wide)
419                i += wide ? 5 : 3;
420                break;
421            case 0xba: //indy
422            case 0xb9: //interface
423            case 0xc8:
424            case 0xc9:  //jsr_w
425                i += 5; //goto_w
426                break;
427            default:
428                i++;
429                break;
430            }
431
432            if (wide) {
433                wide = false;
434            }
435        }
436    }
437
438    @Override
439    public void accept(final ClassVisitor classVisitor, final Attribute[] attrs, final int flags) {
440        super.accept(classVisitor, attrs, flags);
441    }
442
443    @Override
444    protected Label readLabel(final int offset, final Label[] labels) {
445        final Label label = super.readLabel(offset, labels);
446        label.info = offset;
447        return label;
448    }
449
450    private abstract static class Constant {
451        protected ArrayList<Constant> cp;
452        protected int tag;
453        protected Constant(final ArrayList<Constant> cp, final int tag) {
454            this.cp = cp;
455            this.tag = tag;
456        }
457
458        @SuppressWarnings("unused")
459        final String getType() {
460            String str = TYPE[tag];
461            while (str.length() < 16) {
462                str += " ";
463            }
464            return str;
465        }
466    }
467
468    private static class IndexInfo extends Constant {
469        protected final int index;
470
471        IndexInfo(final ArrayList<Constant> cp, final int tag, final int index) {
472            super(cp, tag);
473            this.index = index;
474        }
475
476        @Override
477        public String toString() {
478            return cp.get(index).toString();
479        }
480    }
481
482    private static class IndexInfo2 extends IndexInfo {
483        protected final int index2;
484
485        IndexInfo2(final ArrayList<Constant> cp, final int tag, final int index, final int index2) {
486            super(cp, tag, index);
487            this.index2 = index2;
488        }
489
490        @Override
491        public String toString() {
492            return super.toString() + ' ' + cp.get(index2).toString();
493        }
494    }
495
496    private static class DirectInfo<T> extends Constant {
497        protected final T info;
498
499        DirectInfo(final ArrayList<Constant> cp, final int tag, final T info) {
500            super(cp, tag);
501            this.info = info;
502        }
503
504        @Override
505        public String toString() {
506            return info.toString();// + " [class=" + info.getClass().getSimpleName() + ']';
507        }
508    }
509
510    private static final String[] TYPE = {
511        //0
512        "<error>",
513        //1
514        "UTF8",
515        //2
516        "<error>",
517        //3
518        "Integer",
519        //4
520        "Float",
521        //5
522        "Long",
523        //6
524        "Double",
525        //7
526        "Class",
527        //8
528        "String",
529        //9
530        "Fieldref",
531        //10
532        "Methodref",
533        //11
534        "InterfaceMethodRef",
535        //12
536        "NameAndType",
537        //13
538        "<error>",
539        //14
540        "<error>",
541        //15
542        "MethodHandle",
543        //16
544        "MethodType",
545        //17
546        "<error>",
547        //18
548        "Invokedynamic"
549    };
550
551}
552