summaryrefslogtreecommitdiff
path: root/dx/src/com/android/dx/command/dump/BaseDumper.java
diff options
context:
space:
mode:
Diffstat (limited to 'dx/src/com/android/dx/command/dump/BaseDumper.java')
-rw-r--r--dx/src/com/android/dx/command/dump/BaseDumper.java299
1 files changed, 299 insertions, 0 deletions
diff --git a/dx/src/com/android/dx/command/dump/BaseDumper.java b/dx/src/com/android/dx/command/dump/BaseDumper.java
new file mode 100644
index 0000000..ad6540b
--- /dev/null
+++ b/dx/src/com/android/dx/command/dump/BaseDumper.java
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.command.dump;
+
+import com.android.dx.cf.code.ConcreteMethod;
+import com.android.dx.cf.iface.Member;
+import com.android.dx.cf.iface.ParseObserver;
+import com.android.dx.rop.code.AccessFlags;
+import com.android.dx.util.ByteArray;
+import com.android.dx.util.Hex;
+import com.android.dx.util.IndentingWriter;
+import com.android.dx.util.TwoColumnOutput;
+
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.StringWriter;
+
+/**
+ * Base class for the various human-friendly dumpers.
+ */
+public abstract class BaseDumper
+ implements ParseObserver {
+ /** {@code non-null;} array of data being dumped */
+ private final byte[] bytes;
+
+ /** whether or not to include the raw bytes (in a column on the left) */
+ private final boolean rawBytes;
+
+ /** {@code non-null;} where to dump to */
+ private final PrintStream out;
+
+ /** width of the output in columns */
+ private final int width;
+
+ /**
+ * {@code non-null;} the file path for the class, excluding any base
+ * directory specification
+ */
+ private final String filePath;
+
+ /** whether to be strict about parsing */
+ private final boolean strictParse;
+
+ /** number of bytes per line in hex dumps */
+ private final int hexCols;
+
+ /** the current level of indentation */
+ private int indent;
+
+ /** {@code non-null;} the current column separator string */
+ private String separator;
+
+ /** the offset of the next byte to dump */
+ private int at;
+
+ /** commandline parsedArgs */
+ protected Args args;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param bytes {@code non-null;} bytes of the (alleged) class file
+ * on the left)
+ * @param out {@code non-null;} where to dump to
+ * @param filePath the file path for the class, excluding any base
+ * directory specification
+ */
+ public BaseDumper(byte[] bytes, PrintStream out,
+ String filePath, Args args) {
+ this.bytes = bytes;
+ this.rawBytes = args.rawBytes;
+ this.out = out;
+ this.width = (args.width <= 0) ? 79 : args.width;
+ this.filePath = filePath;
+ this.strictParse = args.strictParse;
+ this.indent = 0;
+ this.separator = rawBytes ? "|" : "";
+ this.at = 0;
+ this.args = args;
+
+ int hexCols = (((width - 5) / 15) + 1) & ~1;
+ if (hexCols < 6) {
+ hexCols = 6;
+ } else if (hexCols > 10) {
+ hexCols = 10;
+ }
+ this.hexCols = hexCols;
+ }
+
+ /**
+ * Computes the total width, in register-units, of the parameters for
+ * this method.
+ * @param meth method to process
+ * @return width in register-units
+ */
+ static int computeParamWidth(ConcreteMethod meth, boolean isStatic) {
+ return meth.getEffectiveDescriptor().getParameterTypes().
+ getWordCount();
+ }
+
+ /** {@inheritDoc} */
+ public void changeIndent(int indentDelta) {
+ indent += indentDelta;
+
+ separator = rawBytes ? "|" : "";
+ for (int i = 0; i < indent; i++) {
+ separator += " ";
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void parsed(ByteArray bytes, int offset, int len, String human) {
+ offset = bytes.underlyingOffset(offset, getBytes());
+
+ boolean rawBytes = getRawBytes();
+
+ if (offset < at) {
+ println("<dump skipped backwards to " + Hex.u4(offset) + ">");
+ at = offset;
+ } else if (offset > at) {
+ String hex = rawBytes ? hexDump(at, offset - at) : "";
+ print(twoColumns(hex, "<skipped to " + Hex.u4(offset) + ">"));
+ at = offset;
+ }
+
+ String hex = rawBytes ? hexDump(offset, len) : "";
+ print(twoColumns(hex, human));
+ at += len;
+ }
+
+ /** {@inheritDoc} */
+ public void startParsingMember(ByteArray bytes, int offset, String name,
+ String descriptor) {
+ // This space intentionally left blank.
+ }
+
+ /** {@inheritDoc} */
+ public void endParsingMember(ByteArray bytes, int offset, String name,
+ String descriptor, Member member) {
+ // This space intentionally left blank.
+ }
+
+ /**
+ * Gets the current dump cursor (that is, the offset of the expected
+ * next byte to dump).
+ *
+ * @return {@code >= 0;} the dump cursor
+ */
+ protected final int getAt() {
+ return at;
+ }
+
+ /**
+ * Sets the dump cursor to the indicated offset in the given array.
+ *
+ * @param arr {@code non-null;} array in question
+ * @param offset {@code >= 0;} offset into the array
+ */
+ protected final void setAt(ByteArray arr, int offset) {
+ at = arr.underlyingOffset(offset, bytes);
+ }
+
+ /**
+ * Gets the array of {@code byte}s to process.
+ *
+ * @return {@code non-null;} the bytes
+ */
+ protected final byte[] getBytes() {
+ return bytes;
+ }
+
+ /**
+ * Gets the filesystem/jar path of the file being dumped.
+ *
+ * @return {@code non-null;} the path
+ */
+ protected final String getFilePath() {
+ return filePath;
+ }
+
+ /**
+ * Gets whether to be strict about parsing.
+ *
+ * @return whether to be strict about parsing
+ */
+ protected final boolean getStrictParse() {
+ return strictParse;
+ }
+
+ /**
+ * Prints the given string to this instance's output stream.
+ *
+ * @param s {@code null-ok;} string to print
+ */
+ protected final void print(String s) {
+ out.print(s);
+ }
+
+ /**
+ * Prints the given string to this instance's output stream, followed
+ * by a newline.
+ *
+ * @param s {@code null-ok;} string to print
+ */
+ protected final void println(String s) {
+ out.println(s);
+ }
+
+ /**
+ * Gets whether this dump is to include raw bytes.
+ *
+ * @return the raw bytes flag
+ */
+ protected final boolean getRawBytes() {
+ return rawBytes;
+ }
+
+ /**
+ * Gets the width of the first column of output. This is {@code 0}
+ * unless raw bytes are being included in the output.
+ *
+ * @return {@code >= 0;} the width of the first column
+ */
+ protected final int getWidth1() {
+ if (rawBytes) {
+ return 5 + (hexCols * 2) + (hexCols / 2);
+ }
+
+ return 0;
+ }
+
+ /**
+ * Gets the width of the second column of output.
+ *
+ * @return {@code >= 0;} the width of the second column
+ */
+ protected final int getWidth2() {
+ int w1 = rawBytes ? (getWidth1() + 1) : 0;
+ return width - w1 - (indent * 2);
+ }
+
+ /**
+ * Constructs a hex data dump of the given portion of {@link #bytes}.
+ *
+ * @param offset offset to start dumping at
+ * @param len length to dump
+ * @return {@code non-null;} the dump
+ */
+ protected final String hexDump(int offset, int len) {
+ return Hex.dump(bytes, offset, len, offset, hexCols, 4);
+ }
+
+ /**
+ * Combines a pair of strings as two columns, or if this is one-column
+ * output, format the otherwise-second column.
+ *
+ * @param s1 {@code non-null;} the first column's string
+ * @param s2 {@code non-null;} the second column's string
+ * @return {@code non-null;} the combined output
+ */
+ protected final String twoColumns(String s1, String s2) {
+ int w1 = getWidth1();
+ int w2 = getWidth2();
+
+ try {
+ if (w1 == 0) {
+ int len2 = s2.length();
+ StringWriter sw = new StringWriter(len2 * 2);
+ IndentingWriter iw = new IndentingWriter(sw, w2, separator);
+
+ iw.write(s2);
+ if ((len2 == 0) || (s2.charAt(len2 - 1) != '\n')) {
+ iw.write('\n');
+ }
+ iw.flush();
+
+ return sw.toString();
+ } else {
+ return TwoColumnOutput.toString(s1, w1, separator, s2, w2);
+ }
+ } catch (IOException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+}