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 
  26 package jdk.nashorn.internal.ir.debug;
  27 
  28 import java.util.ArrayList;
  29 import java.util.HashMap;
  30 import java.util.List;
  31 import java.util.Map;
  32 import jdk.internal.org.objectweb.asm.Attribute;
  33 import jdk.internal.org.objectweb.asm.ClassReader;
  34 import jdk.internal.org.objectweb.asm.ClassVisitor;
  35 import jdk.internal.org.objectweb.asm.Label;
  36 import jdk.nashorn.internal.ir.debug.NashornTextifier.NashornLabel;
  37 
  38 /**
  39  * Subclass of the ASM class reader that retains more info, such
  40  * as bytecode offsets
  41  */
  42 public 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 }