Home | History | Annotate | Download | only in direct
      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