• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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