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.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 
     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