Home | History | Annotate | Download | only in merge
      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 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    145     public int adjustString(int stringIndex) {
    146         return stringIndex == ClassDef.NO_INDEX ? ClassDef.NO_INDEX : stringIds[stringIndex];
    147     }
    148 
    149     public int adjustType(int typeIndex) {
    150         return (typeIndex == ClassDef.NO_INDEX) ? ClassDef.NO_INDEX : (typeIds[typeIndex] & 0xffff);
    151     }
    152 
    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 
    164     public int adjustProto(int protoIndex) {
    165         return protoIds[protoIndex] & 0xffff;
    166     }
    167 
    168     public int adjustField(int fieldIndex) {
    169         return fieldIds[fieldIndex] & 0xffff;
    170     }
    171 
    172     public int adjustMethod(int methodIndex) {
    173         return methodIds[methodIndex] & 0xffff;
    174     }
    175 
    176     public int adjustTypeListOffset(int typeListOffset) {
    177         return typeListOffsets.get(typeListOffset);
    178     }
    179 
    180     public int adjustAnnotation(int annotationOffset) {
    181         return annotationOffsets.get(annotationOffset);
    182     }
    183 
    184     public int adjustAnnotationSet(int annotationSetOffset) {
    185         return annotationSetOffsets.get(annotationSetOffset);
    186     }
    187 
    188     public int adjustAnnotationSetRefList(int annotationSetRefListOffset) {
    189         return annotationSetRefListOffsets.get(annotationSetRefListOffset);
    190     }
    191 
    192     public int adjustAnnotationDirectory(int annotationDirectoryOffset) {
    193         return annotationDirectoryOffsets.get(annotationDirectoryOffset);
    194     }
    195 
    196     public int adjustEncodedArray(int encodedArrayAttribute) {
    197         return encodedArrayValueOffset.get(encodedArrayAttribute);
    198     }
    199 
    200     public int adjustCallSite(int callSiteIndex) {
    201         return callSiteIds[callSiteIndex];
    202     }
    203 
    204     public int adjustMethodHandle(int methodHandleIndex) {
    205         return methodHandleIds.get(methodHandleIndex);
    206     }
    207 
    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 
    215     public CallSiteId adjust(CallSiteId callSiteId) {
    216         return new CallSiteId(target, adjustEncodedArray(callSiteId.getCallSiteOffset()));
    217     }
    218 
    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 
    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 
    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 
    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 
    253     public SortableType adjust(SortableType sortableType) {
    254         return new SortableType(sortableType.getDex(),
    255                 sortableType.getIndexMap(), adjust(sortableType.getClassDef()));
    256     }
    257 
    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 
    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 
    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 
    285         public EncodedValueTransformer(ByteOutput out) {
    286             this.out = out;
    287         }
    288 
    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 
    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 
    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 
    385         private void writeTypeAndArg(int type, int arg) {
    386             out.writeByte((arg << 5) | type);
    387         }
    388     }
    389 }
    390