BaseDumper.java revision 99409883d9c4c0ffb49b070ce307bb33a9dfe9f1
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
17package com.android.dx.command.dump;
18
19import com.android.dx.cf.code.ConcreteMethod;
20import com.android.dx.cf.iface.Member;
21import com.android.dx.cf.iface.ParseObserver;
22import com.android.dx.rop.code.AccessFlags;
23import com.android.dx.util.ByteArray;
24import com.android.dx.util.Hex;
25import com.android.dx.util.IndentingWriter;
26import com.android.dx.util.TwoColumnOutput;
27
28import java.io.IOException;
29import java.io.PrintStream;
30import java.io.StringWriter;
31
32/**
33 * Base class for the various human-friendly dumpers.
34 */
35public 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     */
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     */
110    static int computeParamWidth(ConcreteMethod meth, boolean isStatic) {
111        return meth.getEffectiveDescriptor().getParameterTypes().
112            getWordCount();
113    }
114
115    /** {@inheritDoc} */
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} */
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} */
146    public void startParsingMember(ByteArray bytes, int offset, String name,
147                                   String descriptor) {
148        // This space intentionally left blank.
149    }
150
151    /** {@inheritDoc} */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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