CodeObserver.java revision 99409883d9c4c0ffb49b070ce307bb33a9dfe9f1
1/*
2 * Copyright (C) 2007 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.cf.direct;
18
19import com.android.dx.cf.code.ByteOps;
20import com.android.dx.cf.code.BytecodeArray;
21import com.android.dx.cf.code.SwitchList;
22import com.android.dx.cf.iface.ParseObserver;
23import com.android.dx.rop.cst.Constant;
24import com.android.dx.rop.cst.CstDouble;
25import com.android.dx.rop.cst.CstFloat;
26import com.android.dx.rop.cst.CstInteger;
27import com.android.dx.rop.cst.CstKnownNull;
28import com.android.dx.rop.cst.CstLong;
29import com.android.dx.rop.cst.CstType;
30import com.android.dx.rop.type.Type;
31import com.android.dx.util.ByteArray;
32import com.android.dx.util.Hex;
33import com.android.dx.util.IntList;
34
35import java.util.List;
36import java.util.ArrayList;
37
38/**
39 * Bytecode visitor to use when "observing" bytecode getting parsed.
40 */
41public class CodeObserver implements BytecodeArray.Visitor {
42    /** {@code non-null;} actual array of bytecode */
43    private final ByteArray bytes;
44
45    /** {@code non-null;} observer to inform of parsing */
46    private final ParseObserver observer;
47
48    /**
49     * Constructs an instance.
50     *
51     * @param bytes {@code non-null;} actual array of bytecode
52     * @param observer {@code non-null;} observer to inform of parsing
53     */
54    public CodeObserver(ByteArray bytes, ParseObserver observer) {
55        if (bytes == null) {
56            throw new NullPointerException("bytes == null");
57        }
58
59        if (observer == null) {
60            throw new NullPointerException("observer == null");
61        }
62
63        this.bytes = bytes;
64        this.observer = observer;
65    }
66
67    /** {@inheritDoc} */
68    public void visitInvalid(int opcode, int offset, int length) {
69        observer.parsed(bytes, offset, length, header(offset));
70    }
71
72    /** {@inheritDoc} */
73    public void visitNoArgs(int opcode, int offset, int length, Type type) {
74        observer.parsed(bytes, offset, length, header(offset));
75    }
76
77    /** {@inheritDoc} */
78    public void visitLocal(int opcode, int offset, int length,
79            int idx, Type type, int value) {
80        String idxStr = (length <= 3) ? Hex.u1(idx) : Hex.u2(idx);
81        boolean argComment = (length == 1);
82        String valueStr = "";
83
84        if (opcode == ByteOps.IINC) {
85            valueStr = ", #" +
86                ((length <= 3) ? Hex.s1(value) : Hex.s2(value));
87        }
88
89        String catStr = "";
90        if (type.isCategory2()) {
91            catStr = (argComment ? "," : " //") + " category-2";
92        }
93
94        observer.parsed(bytes, offset, length,
95                        header(offset) + (argComment ? " // " : " ") +
96                        idxStr + valueStr + catStr);
97    }
98
99    /** {@inheritDoc} */
100    public void visitConstant(int opcode, int offset, int length,
101            Constant cst, int value) {
102        if (cst instanceof CstKnownNull) {
103            // This is aconst_null.
104            visitNoArgs(opcode, offset, length, null);
105            return;
106        }
107
108        if (cst instanceof CstInteger) {
109            visitLiteralInt(opcode, offset, length, value);
110            return;
111        }
112
113        if (cst instanceof CstLong) {
114            visitLiteralLong(opcode, offset, length,
115                             ((CstLong) cst).getValue());
116            return;
117        }
118
119        if (cst instanceof CstFloat) {
120            visitLiteralFloat(opcode, offset, length,
121                              ((CstFloat) cst).getIntBits());
122            return;
123        }
124
125        if (cst instanceof CstDouble) {
126            visitLiteralDouble(opcode, offset, length,
127                             ((CstDouble) cst).getLongBits());
128            return;
129        }
130
131        String valueStr = "";
132        if (value != 0) {
133            valueStr = ", ";
134            if (opcode == ByteOps.MULTIANEWARRAY) {
135                valueStr += Hex.u1(value);
136            } else {
137                valueStr += Hex.u2(value);
138            }
139        }
140
141        observer.parsed(bytes, offset, length,
142                        header(offset) + " " + cst + valueStr);
143    }
144
145    /** {@inheritDoc} */
146    public void visitBranch(int opcode, int offset, int length,
147                            int target) {
148        String targetStr = (length <= 3) ? Hex.u2(target) : Hex.u4(target);
149        observer.parsed(bytes, offset, length,
150                        header(offset) + " " + targetStr);
151    }
152
153    /** {@inheritDoc} */
154    public void visitSwitch(int opcode, int offset, int length,
155            SwitchList cases, int padding) {
156        int sz = cases.size();
157        StringBuffer sb = new StringBuffer(sz * 20 + 100);
158
159        sb.append(header(offset));
160        if (padding != 0) {
161            sb.append(" // padding: " + Hex.u4(padding));
162        }
163        sb.append('\n');
164
165        for (int i = 0; i < sz; i++) {
166            sb.append("  ");
167            sb.append(Hex.s4(cases.getValue(i)));
168            sb.append(": ");
169            sb.append(Hex.u2(cases.getTarget(i)));
170            sb.append('\n');
171        }
172
173        sb.append("  default: ");
174        sb.append(Hex.u2(cases.getDefaultTarget()));
175
176        observer.parsed(bytes, offset, length, sb.toString());
177    }
178
179    /** {@inheritDoc} */
180    public void visitNewarray(int offset, int length, CstType cst,
181            ArrayList<Constant> intVals) {
182        String commentOrSpace = (length == 1) ? " // " : " ";
183        String typeName = cst.getClassType().getComponentType().toHuman();
184
185        observer.parsed(bytes, offset, length,
186                        header(offset) + commentOrSpace + typeName);
187    }
188
189    /** {@inheritDoc} */
190    public void setPreviousOffset(int offset) {
191        // Do nothing
192    }
193
194    /** {@inheritDoc} */
195    public int getPreviousOffset() {
196        return -1;
197    }
198
199    /**
200     * Helper to produce the first bit of output for each instruction.
201     *
202     * @param offset the offset to the start of the instruction
203     */
204    private String header(int offset) {
205        /*
206         * Note: This uses the original bytecode, not the
207         * possibly-transformed one.
208         */
209        int opcode = bytes.getUnsignedByte(offset);
210        String name = ByteOps.opName(opcode);
211
212        if (opcode == ByteOps.WIDE) {
213            opcode = bytes.getUnsignedByte(offset + 1);
214            name += " " + ByteOps.opName(opcode);
215        }
216
217        return Hex.u2(offset) + ": " + name;
218    }
219
220    /**
221     * Helper for {@link #visitConstant} where the constant is an
222     * {@code int}.
223     *
224     * @param opcode the opcode
225     * @param offset offset to the instruction
226     * @param length instruction length
227     * @param value constant value
228     */
229    private void visitLiteralInt(int opcode, int offset, int length,
230            int value) {
231        String commentOrSpace = (length == 1) ? " // " : " ";
232        String valueStr;
233
234        opcode = bytes.getUnsignedByte(offset); // Compare with orig op below.
235        if ((length == 1) || (opcode == ByteOps.BIPUSH)) {
236            valueStr = "#" + Hex.s1(value);
237        } else if (opcode == ByteOps.SIPUSH) {
238            valueStr = "#" + Hex.s2(value);
239        } else {
240            valueStr = "#" + Hex.s4(value);
241        }
242
243        observer.parsed(bytes, offset, length,
244                        header(offset) + commentOrSpace + valueStr);
245    }
246
247    /**
248     * Helper for {@link #visitConstant} where the constant is a
249     * {@code long}.
250     *
251     * @param opcode the opcode
252     * @param offset offset to the instruction
253     * @param length instruction length
254     * @param value constant value
255     */
256    private void visitLiteralLong(int opcode, int offset, int length,
257            long value) {
258        String commentOrLit = (length == 1) ? " // " : " #";
259        String valueStr;
260
261        if (length == 1) {
262            valueStr = Hex.s1((int) value);
263        } else {
264            valueStr = Hex.s8(value);
265        }
266
267        observer.parsed(bytes, offset, length,
268                        header(offset) + commentOrLit + valueStr);
269    }
270
271    /**
272     * Helper for {@link #visitConstant} where the constant is a
273     * {@code float}.
274     *
275     * @param opcode the opcode
276     * @param offset offset to the instruction
277     * @param length instruction length
278     * @param bits constant value, as float-bits
279     */
280    private void visitLiteralFloat(int opcode, int offset, int length,
281            int bits) {
282        String optArg = (length != 1) ? " #" + Hex.u4(bits) : "";
283
284        observer.parsed(bytes, offset, length,
285                        header(offset) + optArg + " // " +
286                        Float.intBitsToFloat(bits));
287    }
288
289    /**
290     * Helper for {@link #visitConstant} where the constant is a
291     * {@code double}.
292     *
293     * @param opcode the opcode
294     * @param offset offset to the instruction
295     * @param length instruction length
296     * @param bits constant value, as double-bits
297     */
298    private void visitLiteralDouble(int opcode, int offset, int length,
299            long bits) {
300        String optArg = (length != 1) ? " #" + Hex.u8(bits) : "";
301
302        observer.parsed(bytes, offset, length,
303                        header(offset) + optArg + " // " +
304                        Double.longBitsToDouble(bits));
305    }
306}
307