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