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