• Home
  • 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.Annotation;
20 import com.android.dx.rop.annotation.AnnotationVisibility;
21 import com.android.dx.rop.annotation.NameValuePair;
22 import com.android.dx.rop.cst.Constant;
23 import com.android.dx.rop.cst.CstAnnotation;
24 import com.android.dx.rop.cst.CstArray;
25 import com.android.dx.rop.cst.CstUtf8;
26 import com.android.dx.util.ByteArrayAnnotatedOutput;
27 import com.android.dx.util.AnnotatedOutput;
28 
29 import java.util.Arrays;
30 import java.util.Comparator;
31 
32 /**
33  * Single annotation, which consists of a type and a set of name-value
34  * element pairs.
35  */
36 public final class AnnotationItem extends OffsettedItem {
37     /** annotation visibility constant: visible at build time only */
38     private static final int VISIBILITY_BUILD = 0;
39 
40     /** annotation visibility constant: visible at runtime */
41     private static final int VISIBILITY_RUNTIME = 1;
42 
43     /** annotation visibility constant: visible at runtime only to system */
44     private static final int VISIBILITY_SYSTEM = 2;
45 
46     /** the required alignment for instances of this class */
47     private static final int ALIGNMENT = 1;
48 
49     /** {@code non-null;} unique instance of {@link #TypeIdSorter} */
50     private static final TypeIdSorter TYPE_ID_SORTER = new TypeIdSorter();
51 
52     /** {@code non-null;} the annotation to represent */
53     private final Annotation annotation;
54 
55     /**
56      * {@code null-ok;} type reference for the annotation type; set during
57      * {@link #addContents}
58      */
59     private TypeIdItem type;
60 
61     /**
62      * {@code null-ok;} encoded form, ready for writing to a file; set during
63      * {@link #place0}
64      */
65     private byte[] encodedForm;
66 
67     /**
68      * Comparator that sorts (outer) instances by type id index.
69      */
70     private static class TypeIdSorter implements Comparator<AnnotationItem> {
71         /** {@inheritDoc} */
compare(AnnotationItem item1, AnnotationItem item2)72         public int compare(AnnotationItem item1, AnnotationItem item2) {
73             int index1 = item1.type.getIndex();
74             int index2 = item2.type.getIndex();
75 
76             if (index1 < index2) {
77                 return -1;
78             } else if (index1 > index2) {
79                 return 1;
80             }
81 
82             return 0;
83         }
84     }
85 
86     /**
87      * Sorts an array of instances, in place, by type id index,
88      * ignoring all other aspects of the elements. This is only valid
89      * to use after type id indices are known.
90      *
91      * @param array {@code non-null;} array to sort
92      */
sortByTypeIdIndex(AnnotationItem[] array)93     public static void sortByTypeIdIndex(AnnotationItem[] array) {
94         Arrays.sort(array, TYPE_ID_SORTER);
95     }
96 
97     /**
98      * Constructs an instance.
99      *
100      * @param annotation {@code non-null;} annotation to represent
101      */
AnnotationItem(Annotation annotation)102     public AnnotationItem(Annotation annotation) {
103         /*
104          * The write size isn't known up-front because (the variable-lengthed)
105          * leb128 type is used to represent some things.
106          */
107         super(ALIGNMENT, -1);
108 
109         if (annotation == null) {
110             throw new NullPointerException("annotation == null");
111         }
112 
113         this.annotation = annotation;
114         this.type = null;
115         this.encodedForm = null;
116     }
117 
118     /** {@inheritDoc} */
119     @Override
itemType()120     public ItemType itemType() {
121         return ItemType.TYPE_ANNOTATION_ITEM;
122     }
123 
124     /** {@inheritDoc} */
125     @Override
hashCode()126     public int hashCode() {
127         return annotation.hashCode();
128     }
129 
130     /** {@inheritDoc} */
131     @Override
compareTo0(OffsettedItem other)132     protected int compareTo0(OffsettedItem other) {
133         AnnotationItem otherAnnotation = (AnnotationItem) other;
134 
135         return annotation.compareTo(otherAnnotation.annotation);
136     }
137 
138     /** {@inheritDoc} */
139     @Override
toHuman()140     public String toHuman() {
141         return annotation.toHuman();
142     }
143 
144     /** {@inheritDoc} */
addContents(DexFile file)145     public void addContents(DexFile file) {
146         type = file.getTypeIds().intern(annotation.getType());
147         ValueEncoder.addContents(file, annotation);
148     }
149 
150     /** {@inheritDoc} */
151     @Override
place0(Section addedTo, int offset)152     protected void place0(Section addedTo, int offset) {
153         // Encode the data and note the size.
154 
155         ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput();
156         ValueEncoder encoder = new ValueEncoder(addedTo.getFile(), out);
157 
158         encoder.writeAnnotation(annotation, false);
159         encodedForm = out.toByteArray();
160 
161         // Add one for the visibility byte in front of the encoded annotation.
162         setWriteSize(encodedForm.length + 1);
163     }
164 
165     /**
166      * Write a (listing file) annotation for this instance to the given
167      * output, that consumes no bytes of output. This is for annotating
168      * a reference to this instance at the point of the reference.
169      *
170      * @param out {@code non-null;} where to output to
171      * @param prefix {@code non-null;} prefix for each line of output
172      */
annotateTo(AnnotatedOutput out, String prefix)173     public void annotateTo(AnnotatedOutput out, String prefix) {
174         out.annotate(0, prefix + "visibility: " +
175                 annotation.getVisibility().toHuman());
176         out.annotate(0, prefix + "type: " + annotation.getType().toHuman());
177 
178         for (NameValuePair pair : annotation.getNameValuePairs()) {
179             CstUtf8 name = pair.getName();
180             Constant value = pair.getValue();
181 
182             out.annotate(0, prefix + name.toHuman() + ": " +
183                     ValueEncoder.constantToHuman(value));
184         }
185     }
186 
187     /** {@inheritDoc} */
188     @Override
writeTo0(DexFile file, AnnotatedOutput out)189     protected void writeTo0(DexFile file, AnnotatedOutput out) {
190         boolean annotates = out.annotates();
191         AnnotationVisibility visibility = annotation.getVisibility();
192 
193         if (annotates) {
194             out.annotate(0, offsetString() + " annotation");
195             out.annotate(1, "  visibility: VISBILITY_" + visibility);
196         }
197 
198         switch (visibility) {
199             case BUILD:   out.writeByte(VISIBILITY_BUILD); break;
200             case RUNTIME: out.writeByte(VISIBILITY_RUNTIME); break;
201             case SYSTEM:  out.writeByte(VISIBILITY_SYSTEM); break;
202             default: {
203                 // EMBEDDED shouldn't appear at the top level.
204                 throw new RuntimeException("shouldn't happen");
205             }
206         }
207 
208         if (annotates) {
209             /*
210              * The output is to be annotated, so redo the work previously
211              * done by place0(), except this time annotations will actually
212              * get emitted.
213              */
214             ValueEncoder encoder = new ValueEncoder(file, out);
215             encoder.writeAnnotation(annotation, true);
216         } else {
217             out.write(encodedForm);
218         }
219     }
220 }
221