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