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