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