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