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.AnnotatedOutput; 25 import com.android.dx.util.ByteArrayAnnotatedOutput; 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} */ 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 */ 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 * @param dexFile {@code non-null;} dex output 100 */ 101 public AnnotationItem(Annotation annotation, DexFile dexFile) { 102 /* 103 * The write size isn't known up-front because (the variable-lengthed) 104 * leb128 type is used to represent some things. 105 */ 106 super(ALIGNMENT, -1); 107 108 if (annotation == null) { 109 throw new NullPointerException("annotation == null"); 110 } 111 112 this.annotation = annotation; 113 this.type = null; 114 this.encodedForm = null; 115 addContents(dexFile); 116 } 117 118 /** {@inheritDoc} */ 119 @Override 120 public ItemType itemType() { 121 return ItemType.TYPE_ANNOTATION_ITEM; 122 } 123 124 /** {@inheritDoc} */ 125 @Override 126 public int hashCode() { 127 return annotation.hashCode(); 128 } 129 130 /** {@inheritDoc} */ 131 @Override 132 protected int compareTo0(OffsettedItem other) { 133 AnnotationItem otherAnnotation = (AnnotationItem) other; 134 135 return annotation.compareTo(otherAnnotation.annotation); 136 } 137 138 /** {@inheritDoc} */ 139 @Override 140 public String toHuman() { 141 return annotation.toHuman(); 142 } 143 144 /** {@inheritDoc} */ 145 public void addContents(DexFile file) { 146 type = file.getTypeIds().intern(annotation.getType()); 147 ValueEncoder.addContents(file, annotation); 148 } 149 150 /** {@inheritDoc} */ 151 @Override 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 */ 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 CstString 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 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