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
17package com.android.dx.cf.direct;
18
19import com.android.dx.cf.attrib.AttAnnotationDefault;
20import com.android.dx.cf.attrib.AttCode;
21import com.android.dx.cf.attrib.AttConstantValue;
22import com.android.dx.cf.attrib.AttDeprecated;
23import com.android.dx.cf.attrib.AttEnclosingMethod;
24import com.android.dx.cf.attrib.AttExceptions;
25import com.android.dx.cf.attrib.AttInnerClasses;
26import com.android.dx.cf.attrib.AttLineNumberTable;
27import com.android.dx.cf.attrib.AttLocalVariableTable;
28import com.android.dx.cf.attrib.AttLocalVariableTypeTable;
29import com.android.dx.cf.attrib.AttRuntimeInvisibleAnnotations;
30import com.android.dx.cf.attrib.AttRuntimeInvisibleParameterAnnotations;
31import com.android.dx.cf.attrib.AttRuntimeVisibleAnnotations;
32import com.android.dx.cf.attrib.AttRuntimeVisibleParameterAnnotations;
33import com.android.dx.cf.attrib.AttSignature;
34import com.android.dx.cf.attrib.AttSourceFile;
35import com.android.dx.cf.attrib.AttSynthetic;
36import com.android.dx.cf.attrib.InnerClassList;
37import com.android.dx.cf.code.ByteCatchList;
38import com.android.dx.cf.code.BytecodeArray;
39import com.android.dx.cf.code.LineNumberList;
40import com.android.dx.cf.code.LocalVariableList;
41import com.android.dx.cf.iface.Attribute;
42import com.android.dx.cf.iface.ParseException;
43import com.android.dx.cf.iface.ParseObserver;
44import com.android.dx.cf.iface.StdAttributeList;
45import com.android.dx.rop.annotation.AnnotationVisibility;
46import com.android.dx.rop.annotation.Annotations;
47import com.android.dx.rop.annotation.AnnotationsList;
48import com.android.dx.rop.code.AccessFlags;
49import com.android.dx.rop.cst.Constant;
50import com.android.dx.rop.cst.ConstantPool;
51import com.android.dx.rop.cst.CstNat;
52import com.android.dx.rop.cst.CstString;
53import com.android.dx.rop.cst.CstType;
54import com.android.dx.rop.cst.TypedConstant;
55import com.android.dx.rop.type.TypeList;
56import com.android.dx.util.ByteArray;
57import com.android.dx.util.Hex;
58import java.io.IOException;
59
60/**
61 * Standard subclass of {@link AttributeFactory}, which knows how to parse
62 * all the standard attribute types.
63 */
64public 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