1 /* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.dx.cf.direct; 18 19 import com.android.dx.cf.attrib.AttAnnotationDefault; 20 import com.android.dx.cf.attrib.AttCode; 21 import com.android.dx.cf.attrib.AttConstantValue; 22 import com.android.dx.cf.attrib.AttDeprecated; 23 import com.android.dx.cf.attrib.AttEnclosingMethod; 24 import com.android.dx.cf.attrib.AttExceptions; 25 import com.android.dx.cf.attrib.AttInnerClasses; 26 import com.android.dx.cf.attrib.AttLineNumberTable; 27 import com.android.dx.cf.attrib.AttLocalVariableTable; 28 import com.android.dx.cf.attrib.AttLocalVariableTypeTable; 29 import com.android.dx.cf.attrib.AttRuntimeInvisibleAnnotations; 30 import com.android.dx.cf.attrib.AttRuntimeInvisibleParameterAnnotations; 31 import com.android.dx.cf.attrib.AttRuntimeVisibleAnnotations; 32 import com.android.dx.cf.attrib.AttRuntimeVisibleParameterAnnotations; 33 import com.android.dx.cf.attrib.AttSignature; 34 import com.android.dx.cf.attrib.AttSourceFile; 35 import com.android.dx.cf.attrib.AttSynthetic; 36 import com.android.dx.cf.attrib.InnerClassList; 37 import com.android.dx.cf.code.ByteCatchList; 38 import com.android.dx.cf.code.BytecodeArray; 39 import com.android.dx.cf.code.LineNumberList; 40 import com.android.dx.cf.code.LocalVariableList; 41 import com.android.dx.cf.iface.Attribute; 42 import com.android.dx.cf.iface.ParseException; 43 import com.android.dx.cf.iface.ParseObserver; 44 import com.android.dx.cf.iface.StdAttributeList; 45 import com.android.dx.rop.annotation.AnnotationVisibility; 46 import com.android.dx.rop.annotation.Annotations; 47 import com.android.dx.rop.annotation.AnnotationsList; 48 import com.android.dx.rop.code.AccessFlags; 49 import com.android.dx.rop.cst.Constant; 50 import com.android.dx.rop.cst.ConstantPool; 51 import com.android.dx.rop.cst.CstNat; 52 import com.android.dx.rop.cst.CstString; 53 import com.android.dx.rop.cst.CstType; 54 import com.android.dx.rop.cst.TypedConstant; 55 import com.android.dx.rop.type.TypeList; 56 import com.android.dx.util.ByteArray; 57 import com.android.dx.util.Hex; 58 import java.io.IOException; 59 60 /** 61 * Standard subclass of {@link AttributeFactory}, which knows how to parse 62 * all the standard attribute types. 63 */ 64 public class StdAttributeFactory 65 extends AttributeFactory { 66 /** {@code non-null;} shared instance of this class */ 67 public static final StdAttributeFactory THE_ONE = 68 new StdAttributeFactory(); 69 70 /** 71 * Constructs an instance. 72 */ 73 public StdAttributeFactory() { 74 // This space intentionally left blank. 75 } 76 77 /** {@inheritDoc} */ 78 @Override 79 protected Attribute parse0(DirectClassFile cf, int context, String name, 80 int offset, int length, ParseObserver observer) { 81 switch (context) { 82 case CTX_CLASS: { 83 if (name == AttDeprecated.ATTRIBUTE_NAME) { 84 return deprecated(cf, offset, length, observer); 85 } 86 if (name == AttEnclosingMethod.ATTRIBUTE_NAME) { 87 return enclosingMethod(cf, offset, length, observer); 88 } 89 if (name == AttInnerClasses.ATTRIBUTE_NAME) { 90 return innerClasses(cf, offset, length, observer); 91 } 92 if (name == AttRuntimeInvisibleAnnotations.ATTRIBUTE_NAME) { 93 return runtimeInvisibleAnnotations(cf, offset, length, 94 observer); 95 } 96 if (name == AttRuntimeVisibleAnnotations.ATTRIBUTE_NAME) { 97 return runtimeVisibleAnnotations(cf, offset, length, 98 observer); 99 } 100 if (name == AttSynthetic.ATTRIBUTE_NAME) { 101 return synthetic(cf, offset, length, observer); 102 } 103 if (name == AttSignature.ATTRIBUTE_NAME) { 104 return signature(cf, offset, length, observer); 105 } 106 if (name == AttSourceFile.ATTRIBUTE_NAME) { 107 return sourceFile(cf, offset, length, observer); 108 } 109 break; 110 } 111 case CTX_FIELD: { 112 if (name == AttConstantValue.ATTRIBUTE_NAME) { 113 return constantValue(cf, offset, length, observer); 114 } 115 if (name == AttDeprecated.ATTRIBUTE_NAME) { 116 return deprecated(cf, offset, length, observer); 117 } 118 if (name == AttRuntimeInvisibleAnnotations.ATTRIBUTE_NAME) { 119 return runtimeInvisibleAnnotations(cf, offset, length, 120 observer); 121 } 122 if (name == AttRuntimeVisibleAnnotations.ATTRIBUTE_NAME) { 123 return runtimeVisibleAnnotations(cf, offset, length, 124 observer); 125 } 126 if (name == AttSignature.ATTRIBUTE_NAME) { 127 return signature(cf, offset, length, observer); 128 } 129 if (name == AttSynthetic.ATTRIBUTE_NAME) { 130 return synthetic(cf, offset, length, observer); 131 } 132 break; 133 } 134 case CTX_METHOD: { 135 if (name == AttAnnotationDefault.ATTRIBUTE_NAME) { 136 return annotationDefault(cf, offset, length, observer); 137 } 138 if (name == AttCode.ATTRIBUTE_NAME) { 139 return code(cf, offset, length, observer); 140 } 141 if (name == AttDeprecated.ATTRIBUTE_NAME) { 142 return deprecated(cf, offset, length, observer); 143 } 144 if (name == AttExceptions.ATTRIBUTE_NAME) { 145 return exceptions(cf, offset, length, observer); 146 } 147 if (name == AttRuntimeInvisibleAnnotations.ATTRIBUTE_NAME) { 148 return runtimeInvisibleAnnotations(cf, offset, length, 149 observer); 150 } 151 if (name == AttRuntimeVisibleAnnotations.ATTRIBUTE_NAME) { 152 return runtimeVisibleAnnotations(cf, offset, length, 153 observer); 154 } 155 if (name == AttRuntimeInvisibleParameterAnnotations. 156 ATTRIBUTE_NAME) { 157 return runtimeInvisibleParameterAnnotations( 158 cf, offset, length, observer); 159 } 160 if (name == AttRuntimeVisibleParameterAnnotations. 161 ATTRIBUTE_NAME) { 162 return runtimeVisibleParameterAnnotations( 163 cf, offset, length, observer); 164 } 165 if (name == AttSignature.ATTRIBUTE_NAME) { 166 return signature(cf, offset, length, observer); 167 } 168 if (name == AttSynthetic.ATTRIBUTE_NAME) { 169 return synthetic(cf, offset, length, observer); 170 } 171 break; 172 } 173 case CTX_CODE: { 174 if (name == AttLineNumberTable.ATTRIBUTE_NAME) { 175 return lineNumberTable(cf, offset, length, observer); 176 } 177 if (name == AttLocalVariableTable.ATTRIBUTE_NAME) { 178 return localVariableTable(cf, offset, length, observer); 179 } 180 if (name == AttLocalVariableTypeTable.ATTRIBUTE_NAME) { 181 return localVariableTypeTable(cf, offset, length, 182 observer); 183 } 184 break; 185 } 186 } 187 188 return super.parse0(cf, context, name, offset, length, observer); 189 } 190 191 /** 192 * Parses an {@code AnnotationDefault} attribute. 193 */ 194 private Attribute annotationDefault(DirectClassFile cf, 195 int offset, int length, ParseObserver observer) { 196 if (length < 2) { 197 throwSeverelyTruncated(); 198 } 199 200 AnnotationParser ap = 201 new AnnotationParser(cf, offset, length, observer); 202 Constant cst = ap.parseValueAttribute(); 203 204 return new AttAnnotationDefault(cst, length); 205 } 206 207 /** 208 * Parses a {@code Code} attribute. 209 */ 210 private Attribute code(DirectClassFile cf, int offset, int length, 211 ParseObserver observer) { 212 if (length < 12) { 213 return throwSeverelyTruncated(); 214 } 215 216 ByteArray bytes = cf.getBytes(); 217 ConstantPool pool = cf.getConstantPool(); 218 int maxStack = bytes.getUnsignedShort(offset); // u2 max_stack 219 int maxLocals = bytes.getUnsignedShort(offset + 2); // u2 max_locals 220 int codeLength = bytes.getInt(offset + 4); // u4 code_length 221 int origOffset = offset; 222 223 if (observer != null) { 224 observer.parsed(bytes, offset, 2, 225 "max_stack: " + Hex.u2(maxStack)); 226 observer.parsed(bytes, offset + 2, 2, 227 "max_locals: " + Hex.u2(maxLocals)); 228 observer.parsed(bytes, offset + 4, 4, 229 "code_length: " + Hex.u4(codeLength)); 230 } 231 232 offset += 8; 233 length -= 8; 234 235 if (length < (codeLength + 4)) { 236 return throwTruncated(); 237 } 238 239 int codeOffset = offset; 240 offset += codeLength; 241 length -= codeLength; 242 BytecodeArray code = 243 new BytecodeArray(bytes.slice(codeOffset, codeOffset + codeLength), 244 pool); 245 if (observer != null) { 246 code.forEach(new CodeObserver(code.getBytes(), observer)); 247 } 248 249 // u2 exception_table_length 250 int exceptionTableLength = bytes.getUnsignedShort(offset); 251 ByteCatchList catches = (exceptionTableLength == 0) ? 252 ByteCatchList.EMPTY : 253 new ByteCatchList(exceptionTableLength); 254 255 if (observer != null) { 256 observer.parsed(bytes, offset, 2, 257 "exception_table_length: " + 258 Hex.u2(exceptionTableLength)); 259 } 260 261 offset += 2; 262 length -= 2; 263 264 if (length < (exceptionTableLength * 8 + 2)) { 265 return throwTruncated(); 266 } 267 268 for (int i = 0; i < exceptionTableLength; i++) { 269 if (observer != null) { 270 observer.changeIndent(1); 271 } 272 273 int startPc = bytes.getUnsignedShort(offset); 274 int endPc = bytes.getUnsignedShort(offset + 2); 275 int handlerPc = bytes.getUnsignedShort(offset + 4); 276 int catchTypeIdx = bytes.getUnsignedShort(offset + 6); 277 CstType catchType = (CstType) pool.get0Ok(catchTypeIdx); 278 catches.set(i, startPc, endPc, handlerPc, catchType); 279 if (observer != null) { 280 observer.parsed(bytes, offset, 8, 281 Hex.u2(startPc) + ".." + Hex.u2(endPc) + 282 " -> " + Hex.u2(handlerPc) + " " + 283 ((catchType == null) ? "<any>" : 284 catchType.toHuman())); 285 } 286 offset += 8; 287 length -= 8; 288 289 if (observer != null) { 290 observer.changeIndent(-1); 291 } 292 } 293 294 catches.setImmutable(); 295 296 AttributeListParser parser = 297 new AttributeListParser(cf, CTX_CODE, offset, this); 298 parser.setObserver(observer); 299 300 StdAttributeList attributes = parser.getList(); 301 attributes.setImmutable(); 302 303 int attributeByteCount = parser.getEndOffset() - offset; 304 if (attributeByteCount != length) { 305 return throwBadLength(attributeByteCount + (offset - origOffset)); 306 } 307 308 return new AttCode(maxStack, maxLocals, code, catches, attributes); 309 } 310 311 /** 312 * Parses a {@code ConstantValue} attribute. 313 */ 314 private Attribute constantValue(DirectClassFile cf, int offset, int length, 315 ParseObserver observer) { 316 if (length != 2) { 317 return throwBadLength(2); 318 } 319 320 ByteArray bytes = cf.getBytes(); 321 ConstantPool pool = cf.getConstantPool(); 322 int idx = bytes.getUnsignedShort(offset); 323 TypedConstant cst = (TypedConstant) pool.get(idx); 324 Attribute result = new AttConstantValue(cst); 325 326 if (observer != null) { 327 observer.parsed(bytes, offset, 2, "value: " + cst); 328 } 329 330 return result; 331 } 332 333 /** 334 * Parses a {@code Deprecated} attribute. 335 */ 336 private Attribute deprecated(DirectClassFile cf, int offset, int length, 337 ParseObserver observer) { 338 if (length != 0) { 339 return throwBadLength(0); 340 } 341 342 return new AttDeprecated(); 343 } 344 345 /** 346 * Parses an {@code EnclosingMethod} attribute. 347 */ 348 private Attribute enclosingMethod(DirectClassFile cf, int offset, 349 int length, ParseObserver observer) { 350 if (length != 4) { 351 throwBadLength(4); 352 } 353 354 ByteArray bytes = cf.getBytes(); 355 ConstantPool pool = cf.getConstantPool(); 356 357 int idx = bytes.getUnsignedShort(offset); 358 CstType type = (CstType) pool.get(idx); 359 360 idx = bytes.getUnsignedShort(offset + 2); 361 CstNat method = (CstNat) pool.get0Ok(idx); 362 363 Attribute result = new AttEnclosingMethod(type, method); 364 365 if (observer != null) { 366 observer.parsed(bytes, offset, 2, "class: " + type); 367 observer.parsed(bytes, offset + 2, 2, "method: " + 368 DirectClassFile.stringOrNone(method)); 369 } 370 371 return result; 372 } 373 374 /** 375 * Parses an {@code Exceptions} attribute. 376 */ 377 private Attribute exceptions(DirectClassFile cf, int offset, int length, 378 ParseObserver observer) { 379 if (length < 2) { 380 return throwSeverelyTruncated(); 381 } 382 383 ByteArray bytes = cf.getBytes(); 384 int count = bytes.getUnsignedShort(offset); // number_of_exceptions 385 386 if (observer != null) { 387 observer.parsed(bytes, offset, 2, 388 "number_of_exceptions: " + Hex.u2(count)); 389 } 390 391 offset += 2; 392 length -= 2; 393 394 if (length != (count * 2)) { 395 throwBadLength((count * 2) + 2); 396 } 397 398 TypeList list = cf.makeTypeList(offset, count); 399 return new AttExceptions(list); 400 } 401 402 /** 403 * Parses an {@code InnerClasses} attribute. 404 */ 405 private Attribute innerClasses(DirectClassFile cf, int offset, int length, 406 ParseObserver observer) { 407 if (length < 2) { 408 return throwSeverelyTruncated(); 409 } 410 411 ByteArray bytes = cf.getBytes(); 412 ConstantPool pool = cf.getConstantPool(); 413 int count = bytes.getUnsignedShort(offset); // number_of_classes 414 415 if (observer != null) { 416 observer.parsed(bytes, offset, 2, 417 "number_of_classes: " + Hex.u2(count)); 418 } 419 420 offset += 2; 421 length -= 2; 422 423 if (length != (count * 8)) { 424 throwBadLength((count * 8) + 2); 425 } 426 427 InnerClassList list = new InnerClassList(count); 428 429 for (int i = 0; i < count; i++) { 430 int innerClassIdx = bytes.getUnsignedShort(offset); 431 int outerClassIdx = bytes.getUnsignedShort(offset + 2); 432 int nameIdx = bytes.getUnsignedShort(offset + 4); 433 int accessFlags = bytes.getUnsignedShort(offset + 6); 434 CstType innerClass = (CstType) pool.get(innerClassIdx); 435 CstType outerClass = (CstType) pool.get0Ok(outerClassIdx); 436 CstString name = (CstString) pool.get0Ok(nameIdx); 437 list.set(i, innerClass, outerClass, name, accessFlags); 438 if (observer != null) { 439 observer.parsed(bytes, offset, 2, 440 "inner_class: " + 441 DirectClassFile.stringOrNone(innerClass)); 442 observer.parsed(bytes, offset + 2, 2, 443 " outer_class: " + 444 DirectClassFile.stringOrNone(outerClass)); 445 observer.parsed(bytes, offset + 4, 2, 446 " name: " + 447 DirectClassFile.stringOrNone(name)); 448 observer.parsed(bytes, offset + 6, 2, 449 " access_flags: " + 450 AccessFlags.innerClassString(accessFlags)); 451 } 452 offset += 8; 453 } 454 455 list.setImmutable(); 456 return new AttInnerClasses(list); 457 } 458 459 /** 460 * Parses a {@code LineNumberTable} attribute. 461 */ 462 private Attribute lineNumberTable(DirectClassFile cf, int offset, 463 int length, ParseObserver observer) { 464 if (length < 2) { 465 return throwSeverelyTruncated(); 466 } 467 468 ByteArray bytes = cf.getBytes(); 469 int count = bytes.getUnsignedShort(offset); // line_number_table_length 470 471 if (observer != null) { 472 observer.parsed(bytes, offset, 2, 473 "line_number_table_length: " + Hex.u2(count)); 474 } 475 476 offset += 2; 477 length -= 2; 478 479 if (length != (count * 4)) { 480 throwBadLength((count * 4) + 2); 481 } 482 483 LineNumberList list = new LineNumberList(count); 484 485 for (int i = 0; i < count; i++) { 486 int startPc = bytes.getUnsignedShort(offset); 487 int lineNumber = bytes.getUnsignedShort(offset + 2); 488 list.set(i, startPc, lineNumber); 489 if (observer != null) { 490 observer.parsed(bytes, offset, 4, 491 Hex.u2(startPc) + " " + lineNumber); 492 } 493 offset += 4; 494 } 495 496 list.setImmutable(); 497 return new AttLineNumberTable(list); 498 } 499 500 /** 501 * Parses a {@code LocalVariableTable} attribute. 502 */ 503 private Attribute localVariableTable(DirectClassFile cf, int offset, 504 int length, ParseObserver observer) { 505 if (length < 2) { 506 return throwSeverelyTruncated(); 507 } 508 509 ByteArray bytes = cf.getBytes(); 510 int count = bytes.getUnsignedShort(offset); 511 512 if (observer != null) { 513 observer.parsed(bytes, offset, 2, 514 "local_variable_table_length: " + Hex.u2(count)); 515 } 516 517 LocalVariableList list = parseLocalVariables( 518 bytes.slice(offset + 2, offset + length), cf.getConstantPool(), 519 observer, count, false); 520 return new AttLocalVariableTable(list); 521 } 522 523 /** 524 * Parses a {@code LocalVariableTypeTable} attribute. 525 */ 526 private Attribute localVariableTypeTable(DirectClassFile cf, int offset, 527 int length, ParseObserver observer) { 528 if (length < 2) { 529 return throwSeverelyTruncated(); 530 } 531 532 ByteArray bytes = cf.getBytes(); 533 int count = bytes.getUnsignedShort(offset); 534 535 if (observer != null) { 536 observer.parsed(bytes, offset, 2, 537 "local_variable_type_table_length: " + Hex.u2(count)); 538 } 539 540 LocalVariableList list = parseLocalVariables( 541 bytes.slice(offset + 2, offset + length), cf.getConstantPool(), 542 observer, count, true); 543 return new AttLocalVariableTypeTable(list); 544 } 545 546 /** 547 * Parse the table part of either a {@code LocalVariableTable} 548 * or a {@code LocalVariableTypeTable}. 549 * 550 * @param bytes {@code non-null;} bytes to parse, which should <i>only</i> 551 * contain the table data (no header) 552 * @param pool {@code non-null;} constant pool to use 553 * @param count {@code >= 0;} the number of entries 554 * @param typeTable {@code true} iff this is for a type table 555 * @return {@code non-null;} the constructed list 556 */ 557 private LocalVariableList parseLocalVariables(ByteArray bytes, 558 ConstantPool pool, ParseObserver observer, int count, 559 boolean typeTable) { 560 if (bytes.size() != (count * 10)) { 561 // "+ 2" is for the count. 562 throwBadLength((count * 10) + 2); 563 } 564 565 ByteArray.MyDataInputStream in = bytes.makeDataInputStream(); 566 LocalVariableList list = new LocalVariableList(count); 567 568 try { 569 for (int i = 0; i < count; i++) { 570 int startPc = in.readUnsignedShort(); 571 int length = in.readUnsignedShort(); 572 int nameIdx = in.readUnsignedShort(); 573 int typeIdx = in.readUnsignedShort(); 574 int index = in.readUnsignedShort(); 575 CstString name = (CstString) pool.get(nameIdx); 576 CstString type = (CstString) pool.get(typeIdx); 577 CstString descriptor = null; 578 CstString signature = null; 579 580 if (typeTable) { 581 signature = type; 582 } else { 583 descriptor = type; 584 } 585 586 list.set(i, startPc, length, name, 587 descriptor, signature, index); 588 589 if (observer != null) { 590 observer.parsed(bytes, i * 10, 10, Hex.u2(startPc) + 591 ".." + Hex.u2(startPc + length) + " " + 592 Hex.u2(index) + " " + name.toHuman() + " " + 593 type.toHuman()); 594 } 595 } 596 } catch (IOException ex) { 597 throw new RuntimeException("shouldn't happen", ex); 598 } 599 600 list.setImmutable(); 601 return list; 602 } 603 604 /** 605 * Parses a {@code RuntimeInvisibleAnnotations} attribute. 606 */ 607 private Attribute runtimeInvisibleAnnotations(DirectClassFile cf, 608 int offset, int length, ParseObserver observer) { 609 if (length < 2) { 610 throwSeverelyTruncated(); 611 } 612 613 AnnotationParser ap = 614 new AnnotationParser(cf, offset, length, observer); 615 Annotations annotations = 616 ap.parseAnnotationAttribute(AnnotationVisibility.BUILD); 617 618 return new AttRuntimeInvisibleAnnotations(annotations, length); 619 } 620 621 /** 622 * Parses a {@code RuntimeVisibleAnnotations} attribute. 623 */ 624 private Attribute runtimeVisibleAnnotations(DirectClassFile cf, 625 int offset, int length, ParseObserver observer) { 626 if (length < 2) { 627 throwSeverelyTruncated(); 628 } 629 630 AnnotationParser ap = 631 new AnnotationParser(cf, offset, length, observer); 632 Annotations annotations = 633 ap.parseAnnotationAttribute(AnnotationVisibility.RUNTIME); 634 635 return new AttRuntimeVisibleAnnotations(annotations, length); 636 } 637 638 /** 639 * Parses a {@code RuntimeInvisibleParameterAnnotations} attribute. 640 */ 641 private Attribute runtimeInvisibleParameterAnnotations(DirectClassFile cf, 642 int offset, int length, ParseObserver observer) { 643 if (length < 2) { 644 throwSeverelyTruncated(); 645 } 646 647 AnnotationParser ap = 648 new AnnotationParser(cf, offset, length, observer); 649 AnnotationsList list = 650 ap.parseParameterAttribute(AnnotationVisibility.BUILD); 651 652 return new AttRuntimeInvisibleParameterAnnotations(list, length); 653 } 654 655 /** 656 * Parses a {@code RuntimeVisibleParameterAnnotations} attribute. 657 */ 658 private Attribute runtimeVisibleParameterAnnotations(DirectClassFile cf, 659 int offset, int length, ParseObserver observer) { 660 if (length < 2) { 661 throwSeverelyTruncated(); 662 } 663 664 AnnotationParser ap = 665 new AnnotationParser(cf, offset, length, observer); 666 AnnotationsList list = 667 ap.parseParameterAttribute(AnnotationVisibility.RUNTIME); 668 669 return new AttRuntimeVisibleParameterAnnotations(list, length); 670 } 671 672 /** 673 * Parses a {@code Signature} attribute. 674 */ 675 private Attribute signature(DirectClassFile cf, int offset, int length, 676 ParseObserver observer) { 677 if (length != 2) { 678 throwBadLength(2); 679 } 680 681 ByteArray bytes = cf.getBytes(); 682 ConstantPool pool = cf.getConstantPool(); 683 int idx = bytes.getUnsignedShort(offset); 684 CstString cst = (CstString) pool.get(idx); 685 Attribute result = new AttSignature(cst); 686 687 if (observer != null) { 688 observer.parsed(bytes, offset, 2, "signature: " + cst); 689 } 690 691 return result; 692 } 693 694 /** 695 * Parses a {@code SourceFile} attribute. 696 */ 697 private Attribute sourceFile(DirectClassFile cf, int offset, int length, 698 ParseObserver observer) { 699 if (length != 2) { 700 throwBadLength(2); 701 } 702 703 ByteArray bytes = cf.getBytes(); 704 ConstantPool pool = cf.getConstantPool(); 705 int idx = bytes.getUnsignedShort(offset); 706 CstString cst = (CstString) pool.get(idx); 707 Attribute result = new AttSourceFile(cst); 708 709 if (observer != null) { 710 observer.parsed(bytes, offset, 2, "source: " + cst); 711 } 712 713 return result; 714 } 715 716 /** 717 * Parses a {@code Synthetic} attribute. 718 */ 719 private Attribute synthetic(DirectClassFile cf, int offset, int length, 720 ParseObserver observer) { 721 if (length != 0) { 722 return throwBadLength(0); 723 } 724 725 return new AttSynthetic(); 726 } 727 728 /** 729 * Throws the right exception when a known attribute has a way too short 730 * length. 731 * 732 * @return never 733 * @throws ParseException always thrown 734 */ 735 private static Attribute throwSeverelyTruncated() { 736 throw new ParseException("severely truncated attribute"); 737 } 738 739 /** 740 * Throws the right exception when a known attribute has a too short 741 * length. 742 * 743 * @return never 744 * @throws ParseException always thrown 745 */ 746 private static Attribute throwTruncated() { 747 throw new ParseException("truncated attribute"); 748 } 749 750 /** 751 * Throws the right exception when an attribute has an unexpected length 752 * (given its contents). 753 * 754 * @param expected expected length 755 * @return never 756 * @throws ParseException always thrown 757 */ 758 private static Attribute throwBadLength(int expected) { 759 throw new ParseException("bad attribute length; expected length " + 760 Hex.u4(expected)); 761 } 762 } 763