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