1 /* 2 * Copyright (C) 2011 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.merge; 18 19 import com.android.dex.Annotation; 20 import com.android.dex.util.ByteOutput; 21 import com.android.dex.ClassDef; 22 import com.android.dex.Dex; 23 import com.android.dex.DexException; 24 import com.android.dex.EncodedValue; 25 import com.android.dex.EncodedValueReader; 26 import static com.android.dex.EncodedValueReader.ENCODED_ANNOTATION; 27 import static com.android.dex.EncodedValueReader.ENCODED_ARRAY; 28 import static com.android.dex.EncodedValueReader.ENCODED_BOOLEAN; 29 import static com.android.dex.EncodedValueReader.ENCODED_BYTE; 30 import static com.android.dex.EncodedValueReader.ENCODED_CHAR; 31 import static com.android.dex.EncodedValueReader.ENCODED_DOUBLE; 32 import static com.android.dex.EncodedValueReader.ENCODED_ENUM; 33 import static com.android.dex.EncodedValueReader.ENCODED_FIELD; 34 import static com.android.dex.EncodedValueReader.ENCODED_FLOAT; 35 import static com.android.dex.EncodedValueReader.ENCODED_INT; 36 import static com.android.dex.EncodedValueReader.ENCODED_LONG; 37 import static com.android.dex.EncodedValueReader.ENCODED_METHOD; 38 import static com.android.dex.EncodedValueReader.ENCODED_NULL; 39 import static com.android.dex.EncodedValueReader.ENCODED_SHORT; 40 import static com.android.dex.EncodedValueReader.ENCODED_STRING; 41 import static com.android.dex.EncodedValueReader.ENCODED_TYPE; 42 import com.android.dex.EncodedValueCodec; 43 import com.android.dex.FieldId; 44 import com.android.dex.Leb128; 45 import com.android.dex.MethodId; 46 import com.android.dex.ProtoId; 47 import com.android.dex.TableOfContents; 48 import com.android.dex.TypeList; 49 import com.android.dx.util.ByteArrayAnnotatedOutput; 50 import java.util.HashMap; 51 52 /** 53 * Maps the index offsets from one dex file to those in another. For example, if 54 * you have string #5 in the old dex file, its position in the new dex file is 55 * {@code strings[5]}. 56 */ 57 public final class IndexMap { 58 private final Dex target; 59 public final int[] stringIds; 60 public final short[] typeIds; 61 public final short[] protoIds; 62 public final short[] fieldIds; 63 public final short[] methodIds; 64 private final HashMap<Integer, Integer> typeListOffsets; 65 private final HashMap<Integer, Integer> annotationOffsets; 66 private final HashMap<Integer, Integer> annotationSetOffsets; 67 private final HashMap<Integer, Integer> annotationSetRefListOffsets; 68 private final HashMap<Integer, Integer> annotationDirectoryOffsets; 69 private final HashMap<Integer, Integer> staticValuesOffsets; 70 IndexMap(Dex target, TableOfContents tableOfContents)71 public IndexMap(Dex target, TableOfContents tableOfContents) { 72 this.target = target; 73 this.stringIds = new int[tableOfContents.stringIds.size]; 74 this.typeIds = new short[tableOfContents.typeIds.size]; 75 this.protoIds = new short[tableOfContents.protoIds.size]; 76 this.fieldIds = new short[tableOfContents.fieldIds.size]; 77 this.methodIds = new short[tableOfContents.methodIds.size]; 78 this.typeListOffsets = new HashMap<Integer, Integer>(); 79 this.annotationOffsets = new HashMap<Integer, Integer>(); 80 this.annotationSetOffsets = new HashMap<Integer, Integer>(); 81 this.annotationSetRefListOffsets = new HashMap<Integer, Integer>(); 82 this.annotationDirectoryOffsets = new HashMap<Integer, Integer>(); 83 this.staticValuesOffsets = new HashMap<Integer, Integer>(); 84 85 /* 86 * A type list, annotation set, annotation directory, or static value at 87 * offset 0 is always empty. Always map offset 0 to 0. 88 */ 89 this.typeListOffsets.put(0, 0); 90 this.annotationSetOffsets.put(0, 0); 91 this.annotationDirectoryOffsets.put(0, 0); 92 this.staticValuesOffsets.put(0, 0); 93 } 94 putTypeListOffset(int oldOffset, int newOffset)95 public void putTypeListOffset(int oldOffset, int newOffset) { 96 if (oldOffset <= 0 || newOffset <= 0) { 97 throw new IllegalArgumentException(); 98 } 99 typeListOffsets.put(oldOffset, newOffset); 100 } 101 putAnnotationOffset(int oldOffset, int newOffset)102 public void putAnnotationOffset(int oldOffset, int newOffset) { 103 if (oldOffset <= 0 || newOffset <= 0) { 104 throw new IllegalArgumentException(); 105 } 106 annotationOffsets.put(oldOffset, newOffset); 107 } 108 putAnnotationSetOffset(int oldOffset, int newOffset)109 public void putAnnotationSetOffset(int oldOffset, int newOffset) { 110 if (oldOffset <= 0 || newOffset <= 0) { 111 throw new IllegalArgumentException(); 112 } 113 annotationSetOffsets.put(oldOffset, newOffset); 114 } 115 putAnnotationSetRefListOffset(int oldOffset, int newOffset)116 public void putAnnotationSetRefListOffset(int oldOffset, int newOffset) { 117 if (oldOffset <= 0 || newOffset <= 0) { 118 throw new IllegalArgumentException(); 119 } 120 annotationSetRefListOffsets.put(oldOffset, newOffset); 121 } 122 putAnnotationDirectoryOffset(int oldOffset, int newOffset)123 public void putAnnotationDirectoryOffset(int oldOffset, int newOffset) { 124 if (oldOffset <= 0 || newOffset <= 0) { 125 throw new IllegalArgumentException(); 126 } 127 annotationDirectoryOffsets.put(oldOffset, newOffset); 128 } 129 putStaticValuesOffset(int oldOffset, int newOffset)130 public void putStaticValuesOffset(int oldOffset, int newOffset) { 131 if (oldOffset <= 0 || newOffset <= 0) { 132 throw new IllegalArgumentException(); 133 } 134 staticValuesOffsets.put(oldOffset, newOffset); 135 } 136 adjustString(int stringIndex)137 public int adjustString(int stringIndex) { 138 return stringIndex == ClassDef.NO_INDEX ? ClassDef.NO_INDEX : stringIds[stringIndex]; 139 } 140 adjustType(int typeIndex)141 public int adjustType(int typeIndex) { 142 return (typeIndex == ClassDef.NO_INDEX) ? ClassDef.NO_INDEX : (typeIds[typeIndex] & 0xffff); 143 } 144 adjustTypeList(TypeList typeList)145 public TypeList adjustTypeList(TypeList typeList) { 146 if (typeList == TypeList.EMPTY) { 147 return typeList; 148 } 149 short[] types = typeList.getTypes().clone(); 150 for (int i = 0; i < types.length; i++) { 151 types[i] = (short) adjustType(types[i]); 152 } 153 return new TypeList(target, types); 154 } 155 adjustProto(int protoIndex)156 public int adjustProto(int protoIndex) { 157 return protoIds[protoIndex] & 0xffff; 158 } 159 adjustField(int fieldIndex)160 public int adjustField(int fieldIndex) { 161 return fieldIds[fieldIndex] & 0xffff; 162 } 163 adjustMethod(int methodIndex)164 public int adjustMethod(int methodIndex) { 165 return methodIds[methodIndex] & 0xffff; 166 } 167 adjustTypeListOffset(int typeListOffset)168 public int adjustTypeListOffset(int typeListOffset) { 169 return typeListOffsets.get(typeListOffset); 170 } 171 adjustAnnotation(int annotationOffset)172 public int adjustAnnotation(int annotationOffset) { 173 return annotationOffsets.get(annotationOffset); 174 } 175 adjustAnnotationSet(int annotationSetOffset)176 public int adjustAnnotationSet(int annotationSetOffset) { 177 return annotationSetOffsets.get(annotationSetOffset); 178 } 179 adjustAnnotationSetRefList(int annotationSetRefListOffset)180 public int adjustAnnotationSetRefList(int annotationSetRefListOffset) { 181 return annotationSetRefListOffsets.get(annotationSetRefListOffset); 182 } 183 adjustAnnotationDirectory(int annotationDirectoryOffset)184 public int adjustAnnotationDirectory(int annotationDirectoryOffset) { 185 return annotationDirectoryOffsets.get(annotationDirectoryOffset); 186 } 187 adjustStaticValues(int staticValuesOffset)188 public int adjustStaticValues(int staticValuesOffset) { 189 return staticValuesOffsets.get(staticValuesOffset); 190 } 191 adjust(MethodId methodId)192 public MethodId adjust(MethodId methodId) { 193 return new MethodId(target, 194 adjustType(methodId.getDeclaringClassIndex()), 195 adjustProto(methodId.getProtoIndex()), 196 adjustString(methodId.getNameIndex())); 197 } 198 adjust(FieldId fieldId)199 public FieldId adjust(FieldId fieldId) { 200 return new FieldId(target, 201 adjustType(fieldId.getDeclaringClassIndex()), 202 adjustType(fieldId.getTypeIndex()), 203 adjustString(fieldId.getNameIndex())); 204 205 } 206 adjust(ProtoId protoId)207 public ProtoId adjust(ProtoId protoId) { 208 return new ProtoId(target, 209 adjustString(protoId.getShortyIndex()), 210 adjustType(protoId.getReturnTypeIndex()), 211 adjustTypeListOffset(protoId.getParametersOffset())); 212 } 213 adjust(ClassDef classDef)214 public ClassDef adjust(ClassDef classDef) { 215 return new ClassDef(target, classDef.getOffset(), adjustType(classDef.getTypeIndex()), 216 classDef.getAccessFlags(), adjustType(classDef.getSupertypeIndex()), 217 adjustTypeListOffset(classDef.getInterfacesOffset()), classDef.getSourceFileIndex(), 218 classDef.getAnnotationsOffset(), classDef.getClassDataOffset(), 219 classDef.getStaticValuesOffset()); 220 } 221 adjust(SortableType sortableType)222 public SortableType adjust(SortableType sortableType) { 223 return new SortableType(sortableType.getDex(), 224 sortableType.getIndexMap(), adjust(sortableType.getClassDef())); 225 } 226 adjustEncodedValue(EncodedValue encodedValue)227 public EncodedValue adjustEncodedValue(EncodedValue encodedValue) { 228 ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput(32); 229 new EncodedValueTransformer(out).transform(new EncodedValueReader(encodedValue)); 230 return new EncodedValue(out.toByteArray()); 231 } 232 adjustEncodedArray(EncodedValue encodedArray)233 public EncodedValue adjustEncodedArray(EncodedValue encodedArray) { 234 ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput(32); 235 new EncodedValueTransformer(out).transformArray( 236 new EncodedValueReader(encodedArray, ENCODED_ARRAY)); 237 return new EncodedValue(out.toByteArray()); 238 } 239 adjust(Annotation annotation)240 public Annotation adjust(Annotation annotation) { 241 ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput(32); 242 new EncodedValueTransformer(out).transformAnnotation( 243 annotation.getReader()); 244 return new Annotation(target, annotation.getVisibility(), 245 new EncodedValue(out.toByteArray())); 246 } 247 248 /** 249 * Adjust an encoded value or array. 250 */ 251 private final class EncodedValueTransformer { 252 private final ByteOutput out; 253 EncodedValueTransformer(ByteOutput out)254 public EncodedValueTransformer(ByteOutput out) { 255 this.out = out; 256 } 257 transform(EncodedValueReader reader)258 public void transform(EncodedValueReader reader) { 259 // TODO: extract this into a helper class, EncodedValueWriter 260 switch (reader.peek()) { 261 case ENCODED_BYTE: 262 EncodedValueCodec.writeSignedIntegralValue(out, ENCODED_BYTE, reader.readByte()); 263 break; 264 case ENCODED_SHORT: 265 EncodedValueCodec.writeSignedIntegralValue(out, ENCODED_SHORT, reader.readShort()); 266 break; 267 case ENCODED_INT: 268 EncodedValueCodec.writeSignedIntegralValue(out, ENCODED_INT, reader.readInt()); 269 break; 270 case ENCODED_LONG: 271 EncodedValueCodec.writeSignedIntegralValue(out, ENCODED_LONG, reader.readLong()); 272 break; 273 case ENCODED_CHAR: 274 EncodedValueCodec.writeUnsignedIntegralValue(out, ENCODED_CHAR, reader.readChar()); 275 break; 276 case ENCODED_FLOAT: 277 // Shift value left 32 so that right-zero-extension works. 278 long longBits = ((long) Float.floatToIntBits(reader.readFloat())) << 32; 279 EncodedValueCodec.writeRightZeroExtendedValue(out, ENCODED_FLOAT, longBits); 280 break; 281 case ENCODED_DOUBLE: 282 EncodedValueCodec.writeRightZeroExtendedValue( 283 out, ENCODED_DOUBLE, Double.doubleToLongBits(reader.readDouble())); 284 break; 285 case ENCODED_STRING: 286 EncodedValueCodec.writeUnsignedIntegralValue( 287 out, ENCODED_STRING, adjustString(reader.readString())); 288 break; 289 case ENCODED_TYPE: 290 EncodedValueCodec.writeUnsignedIntegralValue( 291 out, ENCODED_TYPE, adjustType(reader.readType())); 292 break; 293 case ENCODED_FIELD: 294 EncodedValueCodec.writeUnsignedIntegralValue( 295 out, ENCODED_FIELD, adjustField(reader.readField())); 296 break; 297 case ENCODED_ENUM: 298 EncodedValueCodec.writeUnsignedIntegralValue( 299 out, ENCODED_ENUM, adjustField(reader.readEnum())); 300 break; 301 case ENCODED_METHOD: 302 EncodedValueCodec.writeUnsignedIntegralValue( 303 out, ENCODED_METHOD, adjustMethod(reader.readMethod())); 304 break; 305 case ENCODED_ARRAY: 306 writeTypeAndArg(ENCODED_ARRAY, 0); 307 transformArray(reader); 308 break; 309 case ENCODED_ANNOTATION: 310 writeTypeAndArg(ENCODED_ANNOTATION, 0); 311 transformAnnotation(reader); 312 break; 313 case ENCODED_NULL: 314 reader.readNull(); 315 writeTypeAndArg(ENCODED_NULL, 0); 316 break; 317 case ENCODED_BOOLEAN: 318 boolean value = reader.readBoolean(); 319 writeTypeAndArg(ENCODED_BOOLEAN, value ? 1 : 0); 320 break; 321 default: 322 throw new DexException("Unexpected type: " + Integer.toHexString(reader.peek())); 323 } 324 } 325 transformAnnotation(EncodedValueReader reader)326 private void transformAnnotation(EncodedValueReader reader) { 327 int fieldCount = reader.readAnnotation(); 328 Leb128.writeUnsignedLeb128(out, adjustType(reader.getAnnotationType())); 329 Leb128.writeUnsignedLeb128(out, fieldCount); 330 for (int i = 0; i < fieldCount; i++) { 331 Leb128.writeUnsignedLeb128(out, adjustString(reader.readAnnotationName())); 332 transform(reader); 333 } 334 } 335 transformArray(EncodedValueReader reader)336 private void transformArray(EncodedValueReader reader) { 337 int size = reader.readArray(); 338 Leb128.writeUnsignedLeb128(out, size); 339 for (int i = 0; i < size; i++) { 340 transform(reader); 341 } 342 } 343 writeTypeAndArg(int type, int arg)344 private void writeTypeAndArg(int type, int arg) { 345 out.writeByte((arg << 5) | type); 346 } 347 } 348 } 349