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