Home | History | Annotate | Download | only in dump
      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