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