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.dx.dex.TableOfContents; 20import com.android.dx.io.Annotation; 21import com.android.dx.io.ClassDef; 22import com.android.dx.io.DexBuffer; 23import com.android.dx.io.EncodedValue; 24import com.android.dx.io.EncodedValueReader; 25import com.android.dx.io.FieldId; 26import com.android.dx.io.MethodId; 27import com.android.dx.io.ProtoId; 28import com.android.dx.util.ByteArrayAnnotatedOutput; 29import com.android.dx.util.ByteInput; 30import com.android.dx.util.ByteOutput; 31import com.android.dx.util.Leb128Utils; 32import com.android.dx.util.Unsigned; 33import java.util.HashMap; 34 35/** 36 * Maps the index offsets from one dex file to those in another. For example, if 37 * you have string #5 in the old dex file, its position in the new dex file is 38 * {@code strings[5]}. 39 */ 40public final class IndexMap { 41 private final DexBuffer target; 42 public final int[] stringIds; 43 public final short[] typeIds; 44 public final short[] protoIds; 45 public final short[] fieldIds; 46 public final short[] methodIds; 47 private final HashMap<Integer, Integer> typeListOffsets; 48 private final HashMap<Integer, Integer> annotationOffsets; 49 private final HashMap<Integer, Integer> annotationSetOffsets; 50 private final HashMap<Integer, Integer> annotationDirectoryOffsets; 51 private final HashMap<Integer, Integer> staticValuesOffsets; 52 53 public IndexMap(DexBuffer target, TableOfContents tableOfContents) { 54 this.target = target; 55 this.stringIds = new int[tableOfContents.stringIds.size]; 56 this.typeIds = new short[tableOfContents.typeIds.size]; 57 this.protoIds = new short[tableOfContents.protoIds.size]; 58 this.fieldIds = new short[tableOfContents.fieldIds.size]; 59 this.methodIds = new short[tableOfContents.methodIds.size]; 60 this.typeListOffsets = new HashMap<Integer, Integer>(); 61 this.annotationOffsets = new HashMap<Integer, Integer>(); 62 this.annotationSetOffsets = new HashMap<Integer, Integer>(); 63 this.annotationDirectoryOffsets = new HashMap<Integer, Integer>(); 64 this.staticValuesOffsets = new HashMap<Integer, Integer>(); 65 66 /* 67 * A type list, annotation set, annotation directory, or static value at 68 * offset 0 is always empty. Always map offset 0 to 0. 69 */ 70 this.typeListOffsets.put(0, 0); 71 this.annotationSetOffsets.put(0, 0); 72 this.annotationDirectoryOffsets.put(0, 0); 73 this.staticValuesOffsets.put(0, 0); 74 } 75 76 public void putTypeListOffset(int oldOffset, int newOffset) { 77 if (oldOffset <= 0 || newOffset <= 0) { 78 throw new IllegalArgumentException(); 79 } 80 typeListOffsets.put(oldOffset, newOffset); 81 } 82 83 public void putAnnotationOffset(int oldOffset, int newOffset) { 84 if (oldOffset <= 0 || newOffset <= 0) { 85 throw new IllegalArgumentException(); 86 } 87 annotationOffsets.put(oldOffset, newOffset); 88 } 89 90 public void putAnnotationSetOffset(int oldOffset, int newOffset) { 91 if (oldOffset <= 0 || newOffset <= 0) { 92 throw new IllegalArgumentException(); 93 } 94 annotationSetOffsets.put(oldOffset, newOffset); 95 } 96 97 public void putAnnotationDirectoryOffset(int oldOffset, int newOffset) { 98 if (oldOffset <= 0 || newOffset <= 0) { 99 throw new IllegalArgumentException(); 100 } 101 annotationDirectoryOffsets.put(oldOffset, newOffset); 102 } 103 104 public void putStaticValuesOffset(int oldOffset, int newOffset) { 105 if (oldOffset <= 0 || newOffset <= 0) { 106 throw new IllegalArgumentException(); 107 } 108 staticValuesOffsets.put(oldOffset, newOffset); 109 } 110 111 public int adjustString(int stringIndex) { 112 return stringIndex == ClassDef.NO_INDEX ? ClassDef.NO_INDEX : stringIds[stringIndex]; 113 } 114 115 public int adjustType(int typeIndex) { 116 return (typeIndex == ClassDef.NO_INDEX) ? ClassDef.NO_INDEX : (typeIds[typeIndex] & 0xffff); 117 } 118 119 public TypeList adjustTypeList(TypeList typeList) { 120 if (typeList == TypeList.EMPTY) { 121 return typeList; 122 } 123 short[] types = typeList.getTypes().clone(); 124 for (int i = 0; i < types.length; i++) { 125 types[i] = (short) adjustType(types[i]); 126 } 127 return new TypeList(target, types); 128 } 129 130 public int adjustProto(int protoIndex) { 131 return protoIds[protoIndex] & 0xffff; 132 } 133 134 public int adjustField(int fieldIndex) { 135 return fieldIds[fieldIndex] & 0xffff; 136 } 137 138 public int adjustMethod(int methodIndex) { 139 return methodIds[methodIndex] & 0xffff; 140 } 141 142 public int adjustTypeListOffset(int typeListOffset) { 143 return typeListOffsets.get(typeListOffset); 144 } 145 146 public int adjustAnnotation(int annotationOffset) { 147 return annotationOffsets.get(annotationOffset); 148 } 149 150 public int adjustAnnotationSet(int annotationSetOffset) { 151 return annotationSetOffsets.get(annotationSetOffset); 152 } 153 154 public int adjustAnnotationDirectory(int annotationDirectoryOffset) { 155 return annotationDirectoryOffsets.get(annotationDirectoryOffset); 156 } 157 158 public int adjustStaticValues(int staticValuesOffset) { 159 return staticValuesOffsets.get(staticValuesOffset); 160 } 161 162 public MethodId adjust(MethodId methodId) { 163 return new MethodId(target, 164 adjustType(methodId.getDeclaringClassIndex()), 165 adjustProto(methodId.getProtoIndex()), 166 adjustString(methodId.getNameIndex())); 167 } 168 169 public FieldId adjust(FieldId fieldId) { 170 return new FieldId(target, 171 adjustType(fieldId.getDeclaringClassIndex()), 172 adjustType(fieldId.getTypeIndex()), 173 adjustString(fieldId.getNameIndex())); 174 175 } 176 177 public ProtoId adjust(ProtoId protoId) { 178 return new ProtoId(target, 179 adjustString(protoId.getShortyIndex()), 180 adjustType(protoId.getReturnTypeIndex()), 181 adjustTypeListOffset(protoId.getParametersOffset())); 182 } 183 184 public ClassDef adjust(ClassDef classDef) { 185 return new ClassDef(target, classDef.getOffset(), adjustType(classDef.getTypeIndex()), 186 classDef.getAccessFlags(), adjustType(classDef.getSupertypeIndex()), 187 adjustTypeListOffset(classDef.getInterfacesOffset()), classDef.getSourceFileIndex(), 188 classDef.getAnnotationsOffset(), classDef.getClassDataOffset(), 189 classDef.getStaticValuesOffset()); 190 } 191 192 public SortableType adjust(SortableType sortableType) { 193 return new SortableType(sortableType.getBuffer(), adjust(sortableType.getClassDef())); 194 } 195 196 public EncodedValue adjustEncodedValue(EncodedValue encodedValue) { 197 ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput(32); 198 new EncodedValueTransformer(encodedValue, out).readValue(); 199 return new EncodedValue(out.toByteArray()); 200 } 201 202 public EncodedValue adjustEncodedArray(EncodedValue encodedArray) { 203 ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput(32); 204 new EncodedValueTransformer(encodedArray, out).readArray(); 205 return new EncodedValue(out.toByteArray()); 206 } 207 208 public Annotation adjust(Annotation annotation) { 209 int[] names = annotation.getNames().clone(); 210 EncodedValue[] values = annotation.getValues().clone(); 211 for (int i = 0; i < names.length; i++) { 212 names[i] = adjustString(names[i]); 213 values[i] = adjustEncodedValue(values[i]); 214 } 215 return new Annotation(target, annotation.getVisibility(), 216 adjustType(annotation.getTypeIndex()), names, values); 217 } 218 219 /** 220 * Adjust an encoded value or array. 221 */ 222 private final class EncodedValueTransformer extends EncodedValueReader { 223 private final ByteOutput out; 224 225 public EncodedValueTransformer(EncodedValue encodedValue, ByteOutput out) { 226 super(encodedValue); 227 this.out = out; 228 } 229 230 protected void visitArray(int size) { 231 Leb128Utils.writeUnsignedLeb128(out, size); 232 } 233 234 protected void visitAnnotation(int typeIndex, int size) { 235 Leb128Utils.writeUnsignedLeb128(out, adjustType(typeIndex)); 236 Leb128Utils.writeUnsignedLeb128(out, size); 237 } 238 239 protected void visitAnnotationName(int index) { 240 Leb128Utils.writeUnsignedLeb128(out, adjustString(index)); 241 } 242 243 protected void visitPrimitive(int argAndType, int type, int arg, int size) { 244 out.writeByte(argAndType); 245 copyBytes(in, out, size); 246 } 247 248 protected void visitString(int type, int index) { 249 writeTypeAndSizeAndIndex(type, adjustString(index)); 250 } 251 252 protected void visitType(int type, int index) { 253 writeTypeAndSizeAndIndex(type, adjustType(index)); 254 } 255 256 protected void visitField(int type, int index) { 257 writeTypeAndSizeAndIndex(type, adjustField(index)); 258 } 259 260 protected void visitMethod(int type, int index) { 261 writeTypeAndSizeAndIndex(type, adjustMethod(index)); 262 } 263 264 protected void visitArrayValue(int argAndType) { 265 out.writeByte(argAndType); 266 } 267 268 protected void visitAnnotationValue(int argAndType) { 269 out.writeByte(argAndType); 270 } 271 272 protected void visitEncodedBoolean(int argAndType) { 273 out.writeByte(argAndType); 274 } 275 276 protected void visitEncodedNull(int argAndType) { 277 out.writeByte(argAndType); 278 } 279 280 private void writeTypeAndSizeAndIndex(int type, int index) { 281 int byteCount; 282 if (Unsigned.compare(index, 0xff) <= 0) { 283 byteCount = 1; 284 } else if (Unsigned.compare(index, 0xffff) <= 0) { 285 byteCount = 2; 286 } else if (Unsigned.compare(index, 0xffffff) <= 0) { 287 byteCount = 3; 288 } else { 289 byteCount = 4; 290 } 291 int argAndType = ((byteCount - 1) << 5) | type; 292 out.writeByte(argAndType); 293 294 for (int i = 0; i < byteCount; i++) { 295 out.writeByte(index & 0xff); 296 index >>>= 8; 297 } 298 } 299 300 private void copyBytes(ByteInput in, ByteOutput out, int size) { 301 for (int i = 0; i < size; i++) { 302 out.writeByte(in.readByte()); 303 } 304 } 305 } 306} 307