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