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