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