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