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