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.dx.dex.TableOfContents;
     20 import com.android.dx.io.Annotation;
     21 import com.android.dx.io.ClassDef;
     22 import com.android.dx.io.DexBuffer;
     23 import com.android.dx.io.EncodedValue;
     24 import com.android.dx.io.EncodedValueReader;
     25 import com.android.dx.io.FieldId;
     26 import com.android.dx.io.MethodId;
     27 import com.android.dx.io.ProtoId;
     28 import com.android.dx.util.ByteArrayAnnotatedOutput;
     29 import com.android.dx.util.ByteInput;
     30 import com.android.dx.util.ByteOutput;
     31 import com.android.dx.util.Leb128Utils;
     32 import com.android.dx.util.Unsigned;
     33 import 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  */
     40 public 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