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(), adjust(sortableType.getClassDef()));
224     }
225 
adjustEncodedValue(EncodedValue encodedValue)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 
adjustEncodedArray(EncodedValue encodedArray)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 
adjust(Annotation annotation)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 
EncodedValueTransformer(ByteOutput out)253         public EncodedValueTransformer(ByteOutput out) {
254             this.out = out;
255         }
256 
transform(EncodedValueReader reader)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 
transformAnnotation(EncodedValueReader reader)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 
transformArray(EncodedValueReader reader)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 
writeTypeAndArg(int type, int arg)343         private void writeTypeAndArg(int type, int arg) {
344             out.writeByte((arg << 5) | type);
345         }
346     }
347 }
348