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 */ 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 public void visitInvalid(int opcode, int offset, int length) { 66 observer.parsed(bytes, offset, length, header(offset)); 67 } 68 69 /** {@inheritDoc} */ 70 public void visitNoArgs(int opcode, int offset, int length, Type type) { 71 observer.parsed(bytes, offset, length, header(offset)); 72 } 73 74 /** {@inheritDoc} */ 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} */ 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} */ 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} */ 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} */ 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} */ 187 public void setPreviousOffset(int offset) { 188 // Do nothing 189 } 190 191 /** {@inheritDoc} */ 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 */ 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 */ 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 */ 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 */ 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 */ 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