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 17package com.android.dx.merge; 18 19import com.android.dex.DexException; 20import com.android.dex.DexIndexOverflowException; 21import com.android.dx.io.CodeReader; 22import com.android.dx.io.Opcodes; 23import com.android.dx.io.instructions.DecodedInstruction; 24import com.android.dx.io.instructions.ShortArrayCodeOutput; 25 26final 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 public void visit(DecodedInstruction[] all, DecodedInstruction one) { 67 mappedInstructions[mappedAt++] = one; 68 } 69 } 70 71 private class StringVisitor implements CodeReader.Visitor { 72 public void visit(DecodedInstruction[] all, DecodedInstruction one) { 73 int stringId = one.getIndex(); 74 int mappedId = indexMap.adjustString(stringId); 75 boolean isJumbo = (one.getOpcode() == Opcodes.CONST_STRING_JUMBO); 76 jumboCheck(isJumbo, mappedId); 77 mappedInstructions[mappedAt++] = one.withIndex(mappedId); 78 } 79 } 80 81 private class FieldVisitor implements CodeReader.Visitor { 82 public void visit(DecodedInstruction[] all, DecodedInstruction one) { 83 int fieldId = one.getIndex(); 84 int mappedId = indexMap.adjustField(fieldId); 85 boolean isJumbo = (one.getOpcode() == Opcodes.CONST_STRING_JUMBO); 86 jumboCheck(isJumbo, mappedId); 87 mappedInstructions[mappedAt++] = one.withIndex(mappedId); 88 } 89 } 90 91 private class TypeVisitor implements CodeReader.Visitor { 92 public void visit(DecodedInstruction[] all, DecodedInstruction one) { 93 int typeId = one.getIndex(); 94 int mappedId = indexMap.adjustType(typeId); 95 boolean isJumbo = (one.getOpcode() == Opcodes.CONST_STRING_JUMBO); 96 jumboCheck(isJumbo, mappedId); 97 mappedInstructions[mappedAt++] = one.withIndex(mappedId); 98 } 99 } 100 101 private class MethodVisitor implements CodeReader.Visitor { 102 public void visit(DecodedInstruction[] all, DecodedInstruction one) { 103 int methodId = one.getIndex(); 104 int mappedId = indexMap.adjustMethod(methodId); 105 boolean isJumbo = (one.getOpcode() == Opcodes.CONST_STRING_JUMBO); 106 jumboCheck(isJumbo, mappedId); 107 mappedInstructions[mappedAt++] = one.withIndex(mappedId); 108 } 109 } 110 111 private class MethodAndProtoVisitor implements CodeReader.Visitor { 112 @Override 113 public void visit(DecodedInstruction[] all, DecodedInstruction one) { 114 int methodId = one.getIndex(); 115 int protoId = one.getProtoIndex(); 116 mappedInstructions[mappedAt++] = 117 one.withProtoIndex(indexMap.adjustMethod(methodId), indexMap.adjustProto(protoId)); 118 } 119 } 120 121 private class CallSiteVisitor implements CodeReader.Visitor { 122 @Override 123 public void visit(DecodedInstruction[] all, DecodedInstruction one) { 124 int callSiteId = one.getIndex(); 125 int mappedCallSiteId = indexMap.adjustCallSite(callSiteId); 126 mappedInstructions[mappedAt++] = one.withIndex(mappedCallSiteId); 127 } 128 } 129 130 private static void jumboCheck(boolean isJumbo, int newIndex) { 131 if (!isJumbo && (newIndex > 0xffff)) { 132 throw new DexIndexOverflowException("Cannot merge new index " + newIndex + 133 " into a non-jumbo instruction!"); 134 } 135 } 136} 137