• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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