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 this.reader.setMethodAndProtoVisitor(new MethodAndProtoVisitor()); 41 this.reader.setCallSiteVisitor(new CallSiteVisitor()); 42 } 43 transform(IndexMap indexMap, short[] encodedInstructions)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 visit(DecodedInstruction[] all, DecodedInstruction one)67 public void visit(DecodedInstruction[] all, DecodedInstruction one) { 68 mappedInstructions[mappedAt++] = one; 69 } 70 } 71 72 private class StringVisitor implements CodeReader.Visitor { 73 @Override visit(DecodedInstruction[] all, DecodedInstruction one)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 visit(DecodedInstruction[] all, DecodedInstruction one)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 visit(DecodedInstruction[] all, DecodedInstruction one)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 visit(DecodedInstruction[] all, DecodedInstruction one)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 visit(DecodedInstruction[] all, DecodedInstruction one)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 visit(DecodedInstruction[] all, DecodedInstruction one)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 jumboCheck(boolean isJumbo, int newIndex)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