• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 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.dex.file;
18 
19 import com.android.dx.rop.annotation.Annotations;
20 import com.android.dx.rop.annotation.AnnotationsList;
21 import com.android.dx.rop.cst.CstFieldRef;
22 import com.android.dx.rop.cst.CstMethodRef;
23 import com.android.dx.util.AnnotatedOutput;
24 import com.android.dx.util.Hex;
25 import java.io.PrintWriter;
26 import java.util.ArrayList;
27 import java.util.Collections;
28 
29 /**
30  * Per-class directory of annotations.
31  */
32 public final class AnnotationsDirectoryItem extends OffsettedItem {
33     /** the required alignment for instances of this class */
34     private static final int ALIGNMENT = 4;
35 
36     /** write size of this class's header, in bytes */
37     private static final int HEADER_SIZE = 16;
38 
39     /** write size of a list element, in bytes */
40     private static final int ELEMENT_SIZE = 8;
41 
42     /** {@code null-ok;} the class-level annotations, if any */
43     private AnnotationSetItem classAnnotations;
44 
45     /** {@code null-ok;} the annotated fields, if any */
46     private ArrayList<FieldAnnotationStruct> fieldAnnotations;
47 
48     /** {@code null-ok;} the annotated methods, if any */
49     private ArrayList<MethodAnnotationStruct> methodAnnotations;
50 
51     /** {@code null-ok;} the annotated parameters, if any */
52     private ArrayList<ParameterAnnotationStruct> parameterAnnotations;
53 
54     /**
55      * Constructs an empty instance.
56      */
AnnotationsDirectoryItem()57     public AnnotationsDirectoryItem() {
58         super(ALIGNMENT, -1);
59 
60         classAnnotations = null;
61         fieldAnnotations = null;
62         methodAnnotations = null;
63         parameterAnnotations = null;
64     }
65 
66     /** {@inheritDoc} */
67     @Override
itemType()68     public ItemType itemType() {
69         return ItemType.TYPE_ANNOTATIONS_DIRECTORY_ITEM;
70     }
71 
72     /**
73      * Returns whether this item is empty (has no contents).
74      *
75      * @return {@code true} if this item is empty, or {@code false}
76      * if not
77      */
isEmpty()78     public boolean isEmpty() {
79         return (classAnnotations == null) &&
80             (fieldAnnotations == null) &&
81             (methodAnnotations == null) &&
82             (parameterAnnotations == null);
83     }
84 
85     /**
86      * Returns whether this item is a candidate for interning. The only
87      * interning candidates are ones that <i>only</i> have a non-null
88      * set of class annotations, with no other lists.
89      *
90      * @return {@code true} if this is an interning candidate, or
91      * {@code false} if not
92      */
isInternable()93     public boolean isInternable() {
94         return (classAnnotations != null) &&
95             (fieldAnnotations == null) &&
96             (methodAnnotations == null) &&
97             (parameterAnnotations == null);
98     }
99 
100     /** {@inheritDoc} */
101     @Override
hashCode()102     public int hashCode() {
103         if (classAnnotations == null) {
104             return 0;
105         }
106 
107         return classAnnotations.hashCode();
108     }
109 
110     /**
111      * {@inheritDoc}
112      *
113      * <p><b>Note:</b>: This throws an exception if this item is not
114      * internable.</p>
115      *
116      * @see #isInternable
117      */
118     @Override
compareTo0(OffsettedItem other)119     public int compareTo0(OffsettedItem other) {
120         if (! isInternable()) {
121             throw new UnsupportedOperationException("uninternable instance");
122         }
123 
124         AnnotationsDirectoryItem otherDirectory =
125             (AnnotationsDirectoryItem) other;
126         return classAnnotations.compareTo(otherDirectory.classAnnotations);
127     }
128 
129     /**
130      * Sets the direct annotations on this instance. These are annotations
131      * made on the class, per se, as opposed to on one of its members.
132      * It is only valid to call this method at most once per instance.
133      *
134      * @param annotations {@code non-null;} annotations to set for this class
135      * @param dexFile {@code non-null;} dex output
136      */
setClassAnnotations(Annotations annotations, DexFile dexFile)137     public void setClassAnnotations(Annotations annotations, DexFile dexFile) {
138         if (annotations == null) {
139             throw new NullPointerException("annotations == null");
140         }
141 
142         if (classAnnotations != null) {
143             throw new UnsupportedOperationException(
144                     "class annotations already set");
145         }
146 
147         classAnnotations = new AnnotationSetItem(annotations, dexFile);
148     }
149 
150     /**
151      * Adds a field annotations item to this instance.
152      *
153      * @param field {@code non-null;} field in question
154      * @param annotations {@code non-null;} associated annotations to add
155      * @param dexFile {@code non-null;} dex output
156      */
addFieldAnnotations(CstFieldRef field, Annotations annotations, DexFile dexFile)157     public void addFieldAnnotations(CstFieldRef field,
158             Annotations annotations, DexFile dexFile) {
159         if (fieldAnnotations == null) {
160             fieldAnnotations = new ArrayList<FieldAnnotationStruct>();
161         }
162 
163         fieldAnnotations.add(new FieldAnnotationStruct(field,
164                         new AnnotationSetItem(annotations, dexFile)));
165     }
166 
167     /**
168      * Adds a method annotations item to this instance.
169      *
170      * @param method {@code non-null;} method in question
171      * @param annotations {@code non-null;} associated annotations to add
172      * @param dexFile {@code non-null;} dex output
173      */
addMethodAnnotations(CstMethodRef method, Annotations annotations, DexFile dexFile)174     public void addMethodAnnotations(CstMethodRef method,
175             Annotations annotations, DexFile dexFile) {
176         if (methodAnnotations == null) {
177             methodAnnotations = new ArrayList<MethodAnnotationStruct>();
178         }
179 
180         methodAnnotations.add(new MethodAnnotationStruct(method,
181                         new AnnotationSetItem(annotations, dexFile)));
182     }
183 
184     /**
185      * Adds a parameter annotations item to this instance.
186      *
187      * @param method {@code non-null;} method in question
188      * @param list {@code non-null;} associated list of annotation sets to add
189      * @param dexFile {@code non-null;} dex output
190      */
addParameterAnnotations(CstMethodRef method, AnnotationsList list, DexFile dexFile)191     public void addParameterAnnotations(CstMethodRef method,
192             AnnotationsList list, DexFile dexFile) {
193         if (parameterAnnotations == null) {
194             parameterAnnotations = new ArrayList<ParameterAnnotationStruct>();
195         }
196 
197         parameterAnnotations.add(new ParameterAnnotationStruct(method, list, dexFile));
198     }
199 
200     /**
201      * Gets the method annotations for a given method, if any. This is
202      * meant for use by debugging / dumping code.
203      *
204      * @param method {@code non-null;} the method
205      * @return {@code null-ok;} the method annotations, if any
206      */
getMethodAnnotations(CstMethodRef method)207     public Annotations getMethodAnnotations(CstMethodRef method) {
208         if (methodAnnotations == null) {
209             return null;
210         }
211 
212         for (MethodAnnotationStruct item : methodAnnotations) {
213             if (item.getMethod().equals(method)) {
214                 return item.getAnnotations();
215             }
216         }
217 
218         return null;
219     }
220 
221     /**
222      * Gets the parameter annotations for a given method, if any. This is
223      * meant for use by debugging / dumping code.
224      *
225      * @param method {@code non-null;} the method
226      * @return {@code null-ok;} the parameter annotations, if any
227      */
getParameterAnnotations(CstMethodRef method)228     public AnnotationsList getParameterAnnotations(CstMethodRef method) {
229         if (parameterAnnotations == null) {
230             return null;
231         }
232 
233         for (ParameterAnnotationStruct item : parameterAnnotations) {
234             if (item.getMethod().equals(method)) {
235                 return item.getAnnotationsList();
236             }
237         }
238 
239         return null;
240     }
241 
242     /** {@inheritDoc} */
243     @Override
addContents(DexFile file)244     public void addContents(DexFile file) {
245         MixedItemSection wordData = file.getWordData();
246 
247         if (classAnnotations != null) {
248             classAnnotations = wordData.intern(classAnnotations);
249         }
250 
251         if (fieldAnnotations != null) {
252             for (FieldAnnotationStruct item : fieldAnnotations) {
253                 item.addContents(file);
254             }
255         }
256 
257         if (methodAnnotations != null) {
258             for (MethodAnnotationStruct item : methodAnnotations) {
259                 item.addContents(file);
260             }
261         }
262 
263         if (parameterAnnotations != null) {
264             for (ParameterAnnotationStruct item : parameterAnnotations) {
265                 item.addContents(file);
266             }
267         }
268     }
269 
270     /** {@inheritDoc} */
271     @Override
toHuman()272     public String toHuman() {
273         throw new RuntimeException("unsupported");
274     }
275 
276     /** {@inheritDoc} */
277     @Override
place0(Section addedTo, int offset)278     protected void place0(Section addedTo, int offset) {
279         // We just need to set the write size here.
280 
281         int elementCount = listSize(fieldAnnotations)
282             + listSize(methodAnnotations) + listSize(parameterAnnotations);
283         setWriteSize(HEADER_SIZE + (elementCount * ELEMENT_SIZE));
284     }
285 
286     /** {@inheritDoc} */
287     @Override
writeTo0(DexFile file, AnnotatedOutput out)288     protected void writeTo0(DexFile file, AnnotatedOutput out) {
289         boolean annotates = out.annotates();
290         int classOff = OffsettedItem.getAbsoluteOffsetOr0(classAnnotations);
291         int fieldsSize = listSize(fieldAnnotations);
292         int methodsSize = listSize(methodAnnotations);
293         int parametersSize = listSize(parameterAnnotations);
294 
295         if (annotates) {
296             out.annotate(0, offsetString() + " annotations directory");
297             out.annotate(4, "  class_annotations_off: " + Hex.u4(classOff));
298             out.annotate(4, "  fields_size:           " +
299                     Hex.u4(fieldsSize));
300             out.annotate(4, "  methods_size:          " +
301                     Hex.u4(methodsSize));
302             out.annotate(4, "  parameters_size:       " +
303                     Hex.u4(parametersSize));
304         }
305 
306         out.writeInt(classOff);
307         out.writeInt(fieldsSize);
308         out.writeInt(methodsSize);
309         out.writeInt(parametersSize);
310 
311         if (fieldsSize != 0) {
312             Collections.sort(fieldAnnotations);
313             if (annotates) {
314                 out.annotate(0, "  fields:");
315             }
316             for (FieldAnnotationStruct item : fieldAnnotations) {
317                 item.writeTo(file, out);
318             }
319         }
320 
321         if (methodsSize != 0) {
322             Collections.sort(methodAnnotations);
323             if (annotates) {
324                 out.annotate(0, "  methods:");
325             }
326             for (MethodAnnotationStruct item : methodAnnotations) {
327                 item.writeTo(file, out);
328             }
329         }
330 
331         if (parametersSize != 0) {
332             Collections.sort(parameterAnnotations);
333             if (annotates) {
334                 out.annotate(0, "  parameters:");
335             }
336             for (ParameterAnnotationStruct item : parameterAnnotations) {
337                 item.writeTo(file, out);
338             }
339         }
340     }
341 
342     /**
343      * Gets the list size of the given list, or {@code 0} if given
344      * {@code null}.
345      *
346      * @param list {@code null-ok;} the list in question
347      * @return {@code >= 0;} its size
348      */
listSize(ArrayList<?> list)349     private static int listSize(ArrayList<?> list) {
350         if (list == null) {
351             return 0;
352         }
353 
354         return list.size();
355     }
356 
357     /**
358      * Prints out the contents of this instance, in a debugging-friendly
359      * way. This is meant to be called from {@link ClassDefItem#debugPrint}.
360      *
361      * @param out {@code non-null;} where to output to
362      */
debugPrint(PrintWriter out)363     /*package*/ void debugPrint(PrintWriter out) {
364         if (classAnnotations != null) {
365             out.println("  class annotations: " + classAnnotations);
366         }
367 
368         if (fieldAnnotations != null) {
369             out.println("  field annotations:");
370             for (FieldAnnotationStruct item : fieldAnnotations) {
371                 out.println("    " + item.toHuman());
372             }
373         }
374 
375         if (methodAnnotations != null) {
376             out.println("  method annotations:");
377             for (MethodAnnotationStruct item : methodAnnotations) {
378                 out.println("    " + item.toHuman());
379             }
380         }
381 
382         if (parameterAnnotations != null) {
383             out.println("  parameter annotations:");
384             for (ParameterAnnotationStruct item : parameterAnnotations) {
385                 out.println("    " + item.toHuman());
386             }
387         }
388     }
389 }
390