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.DexException;
     20 import com.android.dex.DexIndexOverflowException;
     21 import com.android.dx.io.CodeReader;
     22 import com.android.dx.io.Opcodes;
     23 import com.android.dx.io.instructions.DecodedInstruction;
     24 import com.android.dx.io.instructions.ShortArrayCodeOutput;
     25 
     26 final class InstructionTransformer {
     27     private final CodeReader reader;
     28 
     29     private DecodedInstruction[] mappedInstructions;
     30     private int mappedAt;
     31     private IndexMap indexMap;
     32 
     33     public InstructionTransformer() {
     34         this.reader = new CodeReader();
     35         this.reader.setAllVisitors(new GenericVisitor());
     36         this.reader.setStringVisitor(new StringVisitor());
     37         this.reader.setTypeVisitor(new TypeVisitor());
     38         this.reader.setFieldVisitor(new FieldVisitor());
     39         this.reader.setMethodVisitor(new MethodVisitor());
     40         this.reader.setMethodAndProtoVisitor(new MethodAndProtoVisitor());
     41         this.reader.setCallSiteVisitor(new CallSiteVisitor());
     42     }
     43 
     44     public short[] transform(IndexMap indexMap, short[] encodedInstructions) throws DexException {
     45         DecodedInstruction[] decodedInstructions =
     46             DecodedInstruction.decodeAll(encodedInstructions);
     47         int size = decodedInstructions.length;
     48 
     49         this.indexMap = indexMap;
     50         mappedInstructions = new DecodedInstruction[size];
     51         mappedAt = 0;
     52         reader.visitAll(decodedInstructions);
     53 
     54         ShortArrayCodeOutput out = new ShortArrayCodeOutput(size);
     55         for (DecodedInstruction instruction : mappedInstructions) {
     56             if (instruction != null) {
     57                 instruction.encode(out);
     58             }
     59         }
     60 
     61         this.indexMap = null;
     62         return out.getArray();
     63     }
     64 
     65     private class GenericVisitor implements CodeReader.Visitor {
     66         @Override
     67         public void visit(DecodedInstruction[] all, DecodedInstruction one) {
     68             mappedInstructions[mappedAt++] = one;
     69         }
     70     }
     71 
     72     private class StringVisitor implements CodeReader.Visitor {
     73         @Override
     74         public void visit(DecodedInstruction[] all, DecodedInstruction one) {
     75             int stringId = one.getIndex();
     76             int mappedId = indexMap.adjustString(stringId);
     77             boolean isJumbo = (one.getOpcode() == Opcodes.CONST_STRING_JUMBO);
     78             jumboCheck(isJumbo, mappedId);
     79             mappedInstructions[mappedAt++] = one.withIndex(mappedId);
     80         }
     81     }
     82 
     83     private class FieldVisitor implements CodeReader.Visitor {
     84         @Override
     85         public void visit(DecodedInstruction[] all, DecodedInstruction one) {
     86             int fieldId = one.getIndex();
     87             int mappedId = indexMap.adjustField(fieldId);
     88             boolean isJumbo = (one.getOpcode() == Opcodes.CONST_STRING_JUMBO);
     89             jumboCheck(isJumbo, mappedId);
     90             mappedInstructions[mappedAt++] = one.withIndex(mappedId);
     91         }
     92     }
     93 
     94     private class TypeVisitor implements CodeReader.Visitor {
     95         @Override
     96         public void visit(DecodedInstruction[] all, DecodedInstruction one) {
     97             int typeId = one.getIndex();
     98             int mappedId = indexMap.adjustType(typeId);
     99             boolean isJumbo = (one.getOpcode() == Opcodes.CONST_STRING_JUMBO);
    100             jumboCheck(isJumbo, mappedId);
    101             mappedInstructions[mappedAt++] = one.withIndex(mappedId);
    102         }
    103     }
    104 
    105     private class MethodVisitor implements CodeReader.Visitor {
    106         @Override
    107         public void visit(DecodedInstruction[] all, DecodedInstruction one) {
    108             int methodId = one.getIndex();
    109             int mappedId = indexMap.adjustMethod(methodId);
    110             boolean isJumbo = (one.getOpcode() == Opcodes.CONST_STRING_JUMBO);
    111             jumboCheck(isJumbo, mappedId);
    112             mappedInstructions[mappedAt++] = one.withIndex(mappedId);
    113         }
    114     }
    115 
    116     private class MethodAndProtoVisitor implements CodeReader.Visitor {
    117         @Override
    118         public void visit(DecodedInstruction[] all, DecodedInstruction one) {
    119             int methodId = one.getIndex();
    120             int protoId = one.getProtoIndex();
    121             mappedInstructions[mappedAt++] =
    122                 one.withProtoIndex(indexMap.adjustMethod(methodId), indexMap.adjustProto(protoId));
    123         }
    124     }
    125 
    126     private class CallSiteVisitor implements CodeReader.Visitor {
    127         @Override
    128         public void visit(DecodedInstruction[] all, DecodedInstruction one) {
    129             int callSiteId = one.getIndex();
    130             int mappedCallSiteId = indexMap.adjustCallSite(callSiteId);
    131             mappedInstructions[mappedAt++] = one.withIndex(mappedCallSiteId);
    132         }
    133     }
    134 
    135     private static void jumboCheck(boolean isJumbo, int newIndex) {
    136         if (!isJumbo && (newIndex > 0xffff)) {
    137             throw new DexIndexOverflowException("Cannot merge new index " + newIndex +
    138                                    " into a non-jumbo instruction!");
    139         }
    140     }
    141 }
    142