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.command.dump; 18 19 import com.android.dx.cf.code.ConcreteMethod; 20 import com.android.dx.cf.iface.Member; 21 import com.android.dx.cf.iface.ParseObserver; 22 import com.android.dx.dex.DexOptions; 23 import com.android.dx.util.ByteArray; 24 import com.android.dx.util.Hex; 25 import com.android.dx.util.IndentingWriter; 26 import com.android.dx.util.TwoColumnOutput; 27 import java.io.IOException; 28 import java.io.PrintStream; 29 import java.io.StringWriter; 30 31 /** 32 * Base class for the various human-friendly dumpers. 33 */ 34 public abstract class BaseDumper 35 implements ParseObserver { 36 /** {@code non-null;} array of data being dumped */ 37 private final byte[] bytes; 38 39 /** whether or not to include the raw bytes (in a column on the left) */ 40 private final boolean rawBytes; 41 42 /** {@code non-null;} where to dump to */ 43 private final PrintStream out; 44 45 /** width of the output in columns */ 46 private final int width; 47 48 /** 49 * {@code non-null;} the file path for the class, excluding any base 50 * directory specification 51 */ 52 private final String filePath; 53 54 /** whether to be strict about parsing */ 55 private final boolean strictParse; 56 57 /** number of bytes per line in hex dumps */ 58 private final int hexCols; 59 60 /** the current level of indentation */ 61 private int indent; 62 63 /** {@code non-null;} the current column separator string */ 64 private String separator; 65 66 /** the offset of the next byte to dump */ 67 private int at; 68 69 /** commandline parsedArgs */ 70 protected Args args; 71 72 /** {@code non-null;} options for dex output, always set to the defaults for now */ 73 protected final DexOptions dexOptions; 74 75 /** 76 * Constructs an instance. 77 * 78 * @param bytes {@code non-null;} bytes of the (alleged) class file 79 * on the left) 80 * @param out {@code non-null;} where to dump to 81 * @param filePath the file path for the class, excluding any base 82 * directory specification 83 */ 84 public BaseDumper(byte[] bytes, PrintStream out, 85 String filePath, Args args) { 86 this.bytes = bytes; 87 this.rawBytes = args.rawBytes; 88 this.out = out; 89 this.width = (args.width <= 0) ? 79 : args.width; 90 this.filePath = filePath; 91 this.strictParse = args.strictParse; 92 this.indent = 0; 93 this.separator = rawBytes ? "|" : ""; 94 this.at = 0; 95 this.args = args; 96 97 this.dexOptions = new DexOptions(); 98 99 int hexCols = (((width - 5) / 15) + 1) & ~1; 100 if (hexCols < 6) { 101 hexCols = 6; 102 } else if (hexCols > 10) { 103 hexCols = 10; 104 } 105 this.hexCols = hexCols; 106 } 107 108 /** 109 * Computes the total width, in register-units, of the parameters for 110 * this method. 111 * @param meth method to process 112 * @return width in register-units 113 */ 114 static int computeParamWidth(ConcreteMethod meth, boolean isStatic) { 115 return meth.getEffectiveDescriptor().getParameterTypes(). 116 getWordCount(); 117 } 118 119 /** {@inheritDoc} */ 120 public void changeIndent(int indentDelta) { 121 indent += indentDelta; 122 123 separator = rawBytes ? "|" : ""; 124 for (int i = 0; i < indent; i++) { 125 separator += " "; 126 } 127 } 128 129 /** {@inheritDoc} */ 130 public void parsed(ByteArray bytes, int offset, int len, String human) { 131 offset = bytes.underlyingOffset(offset, getBytes()); 132 133 boolean rawBytes = getRawBytes(); 134 135 if (offset < at) { 136 println("<dump skipped backwards to " + Hex.u4(offset) + ">"); 137 at = offset; 138 } else if (offset > at) { 139 String hex = rawBytes ? hexDump(at, offset - at) : ""; 140 print(twoColumns(hex, "<skipped to " + Hex.u4(offset) + ">")); 141 at = offset; 142 } 143 144 String hex = rawBytes ? hexDump(offset, len) : ""; 145 print(twoColumns(hex, human)); 146 at += len; 147 } 148 149 /** {@inheritDoc} */ 150 public void startParsingMember(ByteArray bytes, int offset, String name, 151 String descriptor) { 152 // This space intentionally left blank. 153 } 154 155 /** {@inheritDoc} */ 156 public void endParsingMember(ByteArray bytes, int offset, String name, 157 String descriptor, Member member) { 158 // This space intentionally left blank. 159 } 160 161 /** 162 * Gets the current dump cursor (that is, the offset of the expected 163 * next byte to dump). 164 * 165 * @return {@code >= 0;} the dump cursor 166 */ 167 protected final int getAt() { 168 return at; 169 } 170 171 /** 172 * Sets the dump cursor to the indicated offset in the given array. 173 * 174 * @param arr {@code non-null;} array in question 175 * @param offset {@code >= 0;} offset into the array 176 */ 177 protected final void setAt(ByteArray arr, int offset) { 178 at = arr.underlyingOffset(offset, bytes); 179 } 180 181 /** 182 * Gets the array of {@code byte}s to process. 183 * 184 * @return {@code non-null;} the bytes 185 */ 186 protected final byte[] getBytes() { 187 return bytes; 188 } 189 190 /** 191 * Gets the filesystem/jar path of the file being dumped. 192 * 193 * @return {@code non-null;} the path 194 */ 195 protected final String getFilePath() { 196 return filePath; 197 } 198 199 /** 200 * Gets whether to be strict about parsing. 201 * 202 * @return whether to be strict about parsing 203 */ 204 protected final boolean getStrictParse() { 205 return strictParse; 206 } 207 208 /** 209 * Prints the given string to this instance's output stream. 210 * 211 * @param s {@code null-ok;} string to print 212 */ 213 protected final void print(String s) { 214 out.print(s); 215 } 216 217 /** 218 * Prints the given string to this instance's output stream, followed 219 * by a newline. 220 * 221 * @param s {@code null-ok;} string to print 222 */ 223 protected final void println(String s) { 224 out.println(s); 225 } 226 227 /** 228 * Gets whether this dump is to include raw bytes. 229 * 230 * @return the raw bytes flag 231 */ 232 protected final boolean getRawBytes() { 233 return rawBytes; 234 } 235 236 /** 237 * Gets the width of the first column of output. This is {@code 0} 238 * unless raw bytes are being included in the output. 239 * 240 * @return {@code >= 0;} the width of the first column 241 */ 242 protected final int getWidth1() { 243 if (rawBytes) { 244 return 5 + (hexCols * 2) + (hexCols / 2); 245 } 246 247 return 0; 248 } 249 250 /** 251 * Gets the width of the second column of output. 252 * 253 * @return {@code >= 0;} the width of the second column 254 */ 255 protected final int getWidth2() { 256 int w1 = rawBytes ? (getWidth1() + 1) : 0; 257 return width - w1 - (indent * 2); 258 } 259 260 /** 261 * Constructs a hex data dump of the given portion of {@link #bytes}. 262 * 263 * @param offset offset to start dumping at 264 * @param len length to dump 265 * @return {@code non-null;} the dump 266 */ 267 protected final String hexDump(int offset, int len) { 268 return Hex.dump(bytes, offset, len, offset, hexCols, 4); 269 } 270 271 /** 272 * Combines a pair of strings as two columns, or if this is one-column 273 * output, format the otherwise-second column. 274 * 275 * @param s1 {@code non-null;} the first column's string 276 * @param s2 {@code non-null;} the second column's string 277 * @return {@code non-null;} the combined output 278 */ 279 protected final String twoColumns(String s1, String s2) { 280 int w1 = getWidth1(); 281 int w2 = getWidth2(); 282 283 try { 284 if (w1 == 0) { 285 int len2 = s2.length(); 286 StringWriter sw = new StringWriter(len2 * 2); 287 IndentingWriter iw = new IndentingWriter(sw, w2, separator); 288 289 iw.write(s2); 290 if ((len2 == 0) || (s2.charAt(len2 - 1) != '\n')) { 291 iw.write('\n'); 292 } 293 iw.flush(); 294 295 return sw.toString(); 296 } else { 297 return TwoColumnOutput.toString(s1, w1, separator, s2, w2); 298 } 299 } catch (IOException ex) { 300 throw new RuntimeException(ex); 301 } 302 } 303 } 304