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 InstructionTransformer()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 } 41 transform(IndexMap indexMap, short[] encodedInstructions)42 public short[] transform(IndexMap indexMap, short[] encodedInstructions) throws DexException { 43 DecodedInstruction[] decodedInstructions = 44 DecodedInstruction.decodeAll(encodedInstructions); 45 int size = decodedInstructions.length; 46 47 this.indexMap = indexMap; 48 mappedInstructions = new DecodedInstruction[size]; 49 mappedAt = 0; 50 reader.visitAll(decodedInstructions); 51 52 ShortArrayCodeOutput out = new ShortArrayCodeOutput(size); 53 for (DecodedInstruction instruction : mappedInstructions) { 54 if (instruction != null) { 55 instruction.encode(out); 56 } 57 } 58 59 this.indexMap = null; 60 return out.getArray(); 61 } 62 63 private class GenericVisitor implements CodeReader.Visitor { visit(DecodedInstruction[] all, DecodedInstruction one)64 public void visit(DecodedInstruction[] all, DecodedInstruction one) { 65 mappedInstructions[mappedAt++] = one; 66 } 67 } 68 69 private class StringVisitor implements CodeReader.Visitor { visit(DecodedInstruction[] all, DecodedInstruction one)70 public void visit(DecodedInstruction[] all, DecodedInstruction one) { 71 int stringId = one.getIndex(); 72 int mappedId = indexMap.adjustString(stringId); 73 boolean isJumbo = (one.getOpcode() == Opcodes.CONST_STRING_JUMBO); 74 jumboCheck(isJumbo, mappedId); 75 mappedInstructions[mappedAt++] = one.withIndex(mappedId); 76 } 77 } 78 79 private class FieldVisitor implements CodeReader.Visitor { visit(DecodedInstruction[] all, DecodedInstruction one)80 public void visit(DecodedInstruction[] all, DecodedInstruction one) { 81 int fieldId = one.getIndex(); 82 int mappedId = indexMap.adjustField(fieldId); 83 boolean isJumbo = (one.getOpcode() == Opcodes.CONST_STRING_JUMBO); 84 jumboCheck(isJumbo, mappedId); 85 mappedInstructions[mappedAt++] = one.withIndex(mappedId); 86 } 87 } 88 89 private class TypeVisitor implements CodeReader.Visitor { visit(DecodedInstruction[] all, DecodedInstruction one)90 public void visit(DecodedInstruction[] all, DecodedInstruction one) { 91 int typeId = one.getIndex(); 92 int mappedId = indexMap.adjustType(typeId); 93 boolean isJumbo = (one.getOpcode() == Opcodes.CONST_STRING_JUMBO); 94 jumboCheck(isJumbo, mappedId); 95 mappedInstructions[mappedAt++] = one.withIndex(mappedId); 96 } 97 } 98 99 private class MethodVisitor implements CodeReader.Visitor { visit(DecodedInstruction[] all, DecodedInstruction one)100 public void visit(DecodedInstruction[] all, DecodedInstruction one) { 101 int methodId = one.getIndex(); 102 int mappedId = indexMap.adjustMethod(methodId); 103 boolean isJumbo = (one.getOpcode() == Opcodes.CONST_STRING_JUMBO); 104 jumboCheck(isJumbo, mappedId); 105 mappedInstructions[mappedAt++] = one.withIndex(mappedId); 106 } 107 } 108 jumboCheck(boolean isJumbo, int newIndex)109 private static void jumboCheck(boolean isJumbo, int newIndex) { 110 if (!isJumbo && (newIndex > 0xffff)) { 111 throw new DexIndexOverflowException("Cannot merge new index " + newIndex + 112 " into a non-jumbo instruction!"); 113 } 114 } 115 } 116