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