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 17package com.android.dx.merge; 18 19import com.android.dex.Annotation; 20import com.android.dex.util.ByteOutput; 21import com.android.dex.ClassDef; 22import com.android.dex.Dex; 23import com.android.dex.DexException; 24import com.android.dex.EncodedValue; 25import com.android.dex.EncodedValueReader; 26import static com.android.dex.EncodedValueReader.ENCODED_ANNOTATION; 27import static com.android.dex.EncodedValueReader.ENCODED_ARRAY; 28import static com.android.dex.EncodedValueReader.ENCODED_BOOLEAN; 29import static com.android.dex.EncodedValueReader.ENCODED_BYTE; 30import static com.android.dex.EncodedValueReader.ENCODED_CHAR; 31import static com.android.dex.EncodedValueReader.ENCODED_DOUBLE; 32import static com.android.dex.EncodedValueReader.ENCODED_ENUM; 33import static com.android.dex.EncodedValueReader.ENCODED_FIELD; 34import static com.android.dex.EncodedValueReader.ENCODED_FLOAT; 35import static com.android.dex.EncodedValueReader.ENCODED_INT; 36import static com.android.dex.EncodedValueReader.ENCODED_LONG; 37import static com.android.dex.EncodedValueReader.ENCODED_METHOD; 38import static com.android.dex.EncodedValueReader.ENCODED_NULL; 39import static com.android.dex.EncodedValueReader.ENCODED_SHORT; 40import static com.android.dex.EncodedValueReader.ENCODED_STRING; 41import static com.android.dex.EncodedValueReader.ENCODED_TYPE; 42import com.android.dex.EncodedValueCodec; 43import com.android.dex.FieldId; 44import com.android.dex.Leb128; 45import com.android.dex.MethodId; 46import com.android.dex.ProtoId; 47import com.android.dex.TableOfContents; 48import com.android.dex.TypeList; 49import com.android.dx.util.ByteArrayAnnotatedOutput; 50import 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 */ 57public 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 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 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 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 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 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 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 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 137 public int adjustString(int stringIndex) { 138 return stringIndex == ClassDef.NO_INDEX ? ClassDef.NO_INDEX : stringIds[stringIndex]; 139 } 140 141 public int adjustType(int typeIndex) { 142 return (typeIndex == ClassDef.NO_INDEX) ? ClassDef.NO_INDEX : (typeIds[typeIndex] & 0xffff); 143 } 144 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 156 public int adjustProto(int protoIndex) { 157 return protoIds[protoIndex] & 0xffff; 158 } 159 160 public int adjustField(int fieldIndex) { 161 return fieldIds[fieldIndex] & 0xffff; 162 } 163 164 public int adjustMethod(int methodIndex) { 165 return methodIds[methodIndex] & 0xffff; 166 } 167 168 public int adjustTypeListOffset(int typeListOffset) { 169 return typeListOffsets.get(typeListOffset); 170 } 171 172 public int adjustAnnotation(int annotationOffset) { 173 return annotationOffsets.get(annotationOffset); 174 } 175 176 public int adjustAnnotationSet(int annotationSetOffset) { 177 return annotationSetOffsets.get(annotationSetOffset); 178 } 179 180 public int adjustAnnotationSetRefList(int annotationSetRefListOffset) { 181 return annotationSetRefListOffsets.get(annotationSetRefListOffset); 182 } 183 184 public int adjustAnnotationDirectory(int annotationDirectoryOffset) { 185 return annotationDirectoryOffsets.get(annotationDirectoryOffset); 186 } 187 188 public int adjustStaticValues(int staticValuesOffset) { 189 return staticValuesOffsets.get(staticValuesOffset); 190 } 191 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 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 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 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 222 public SortableType adjust(SortableType sortableType) { 223 return new SortableType(sortableType.getDex(), adjust(sortableType.getClassDef())); 224 } 225 226 public EncodedValue adjustEncodedValue(EncodedValue encodedValue) { 227 ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput(32); 228 new EncodedValueTransformer(out).transform(new EncodedValueReader(encodedValue)); 229 return new EncodedValue(out.toByteArray()); 230 } 231 232 public EncodedValue adjustEncodedArray(EncodedValue encodedArray) { 233 ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput(32); 234 new EncodedValueTransformer(out).transformArray( 235 new EncodedValueReader(encodedArray, ENCODED_ARRAY)); 236 return new EncodedValue(out.toByteArray()); 237 } 238 239 public Annotation adjust(Annotation annotation) { 240 ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput(32); 241 new EncodedValueTransformer(out).transformAnnotation( 242 annotation.getReader()); 243 return new Annotation(target, annotation.getVisibility(), 244 new EncodedValue(out.toByteArray())); 245 } 246 247 /** 248 * Adjust an encoded value or array. 249 */ 250 private final class EncodedValueTransformer { 251 private final ByteOutput out; 252 253 public EncodedValueTransformer(ByteOutput out) { 254 this.out = out; 255 } 256 257 public void transform(EncodedValueReader reader) { 258 // TODO: extract this into a helper class, EncodedValueWriter 259 switch (reader.peek()) { 260 case ENCODED_BYTE: 261 EncodedValueCodec.writeSignedIntegralValue(out, ENCODED_BYTE, reader.readByte()); 262 break; 263 case ENCODED_SHORT: 264 EncodedValueCodec.writeSignedIntegralValue(out, ENCODED_SHORT, reader.readShort()); 265 break; 266 case ENCODED_INT: 267 EncodedValueCodec.writeSignedIntegralValue(out, ENCODED_INT, reader.readInt()); 268 break; 269 case ENCODED_LONG: 270 EncodedValueCodec.writeSignedIntegralValue(out, ENCODED_LONG, reader.readLong()); 271 break; 272 case ENCODED_CHAR: 273 EncodedValueCodec.writeUnsignedIntegralValue(out, ENCODED_CHAR, reader.readChar()); 274 break; 275 case ENCODED_FLOAT: 276 // Shift value left 32 so that right-zero-extension works. 277 long longBits = ((long) Float.floatToIntBits(reader.readFloat())) << 32; 278 EncodedValueCodec.writeRightZeroExtendedValue(out, ENCODED_FLOAT, longBits); 279 break; 280 case ENCODED_DOUBLE: 281 EncodedValueCodec.writeRightZeroExtendedValue( 282 out, ENCODED_DOUBLE, Double.doubleToLongBits(reader.readDouble())); 283 break; 284 case ENCODED_STRING: 285 EncodedValueCodec.writeUnsignedIntegralValue( 286 out, ENCODED_STRING, adjustString(reader.readString())); 287 break; 288 case ENCODED_TYPE: 289 EncodedValueCodec.writeUnsignedIntegralValue( 290 out, ENCODED_TYPE, adjustType(reader.readType())); 291 break; 292 case ENCODED_FIELD: 293 EncodedValueCodec.writeUnsignedIntegralValue( 294 out, ENCODED_FIELD, adjustField(reader.readField())); 295 break; 296 case ENCODED_ENUM: 297 EncodedValueCodec.writeUnsignedIntegralValue( 298 out, ENCODED_ENUM, adjustField(reader.readEnum())); 299 break; 300 case ENCODED_METHOD: 301 EncodedValueCodec.writeUnsignedIntegralValue( 302 out, ENCODED_METHOD, adjustMethod(reader.readMethod())); 303 break; 304 case ENCODED_ARRAY: 305 writeTypeAndArg(ENCODED_ARRAY, 0); 306 transformArray(reader); 307 break; 308 case ENCODED_ANNOTATION: 309 writeTypeAndArg(ENCODED_ANNOTATION, 0); 310 transformAnnotation(reader); 311 break; 312 case ENCODED_NULL: 313 reader.readNull(); 314 writeTypeAndArg(ENCODED_NULL, 0); 315 break; 316 case ENCODED_BOOLEAN: 317 boolean value = reader.readBoolean(); 318 writeTypeAndArg(ENCODED_BOOLEAN, value ? 1 : 0); 319 break; 320 default: 321 throw new DexException("Unexpected type: " + Integer.toHexString(reader.peek())); 322 } 323 } 324 325 private void transformAnnotation(EncodedValueReader reader) { 326 int fieldCount = reader.readAnnotation(); 327 Leb128.writeUnsignedLeb128(out, adjustType(reader.getAnnotationType())); 328 Leb128.writeUnsignedLeb128(out, fieldCount); 329 for (int i = 0; i < fieldCount; i++) { 330 Leb128.writeUnsignedLeb128(out, adjustString(reader.readAnnotationName())); 331 transform(reader); 332 } 333 } 334 335 private void transformArray(EncodedValueReader reader) { 336 int size = reader.readArray(); 337 Leb128.writeUnsignedLeb128(out, size); 338 for (int i = 0; i < size; i++) { 339 transform(reader); 340 } 341 } 342 343 private void writeTypeAndArg(int type, int arg) { 344 out.writeByte((arg << 5) | type); 345 } 346 } 347} 348