summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorAndy McFadden <>2009-03-26 16:02:55 -0700
committerThe Android Open Source Project <initial-contribution@android.com>2009-03-26 16:02:55 -0700
commit12d6d4c0ea192b6a924df0df1e3b14ce1ed5793b (patch)
tree207f93a0e689132f1faab540004274e62b1d4906 /tools
parent642d9646f0399648e377ed8e1b36eba9b12f84b2 (diff)
downloadandroid_dalvik-12d6d4c0ea192b6a924df0df1e3b14ce1ed5793b.tar.gz
android_dalvik-12d6d4c0ea192b6a924df0df1e3b14ce1ed5793b.tar.bz2
android_dalvik-12d6d4c0ea192b6a924df0df1e3b14ce1ed5793b.zip
Automated import from //branches/master/...@143003,143003
Diffstat (limited to 'tools')
-rw-r--r--tools/dexdeps/Android.mk44
-rw-r--r--tools/dexdeps/etc/dexdeps69
-rw-r--r--tools/dexdeps/etc/manifest.txt1
-rw-r--r--tools/dexdeps/src/Android.mk32
-rw-r--r--tools/dexdeps/src/com/android/dexdeps/DexData.java656
-rw-r--r--tools/dexdeps/src/com/android/dexdeps/DexDataException.java24
-rw-r--r--tools/dexdeps/src/com/android/dexdeps/FieldRef.java43
-rw-r--r--tools/dexdeps/src/com/android/dexdeps/Main.java200
-rw-r--r--tools/dexdeps/src/com/android/dexdeps/MethodRef.java69
-rw-r--r--tools/dexdeps/src/com/android/dexdeps/Output.java61
-rw-r--r--tools/dexdeps/src/com/android/dexdeps/UsageException.java24
11 files changed, 1223 insertions, 0 deletions
diff --git a/tools/dexdeps/Android.mk b/tools/dexdeps/Android.mk
new file mode 100644
index 000000000..9c2cec700
--- /dev/null
+++ b/tools/dexdeps/Android.mk
@@ -0,0 +1,44 @@
+# Copyright (C) 2009 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.
+
+LOCAL_PATH := $(call my-dir)
+
+# We use copy-file-to-new-target so that the installed
+# script files' timestamps are at least as new as the
+# .jar files they wrap.
+
+# the dexdeps script
+# ============================================================
+include $(CLEAR_VARS)
+LOCAL_IS_HOST_MODULE := true
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_MODULE := dexdeps
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(HOST_OUT_JAVA_LIBRARIES)/dexdeps$(COMMON_JAVA_PACKAGE_SUFFIX)
+$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/etc/dexdeps | $(ACP)
+ @echo "Copy: $(PRIVATE_MODULE) ($@)"
+ $(copy-file-to-new-target)
+ $(hide) chmod 755 $@
+
+INTERNAL_DALVIK_MODULES += $(LOCAL_INSTALLED_MODULE)
+
+# the other stuff
+# ============================================================
+subdirs := $(addprefix $(LOCAL_PATH)/,$(addsuffix /Android.mk, \
+ src \
+ ))
+
+include $(subdirs)
diff --git a/tools/dexdeps/etc/dexdeps b/tools/dexdeps/etc/dexdeps
new file mode 100644
index 000000000..dc628bd14
--- /dev/null
+++ b/tools/dexdeps/etc/dexdeps
@@ -0,0 +1,69 @@
+#!/bin/bash
+#
+# Copyright (C) 2009 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.
+
+# Set up prog to be the path of this script, including following symlinks,
+# and set up progdir to be the fully-qualified pathname of its directory.
+prog="$0"
+while [ -h "${prog}" ]; do
+ newProg=`/bin/ls -ld "${prog}"`
+ newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
+ if expr "x${newProg}" : 'x/' >/dev/null; then
+ prog="${newProg}"
+ else
+ progdir=`dirname "${prog}"`
+ prog="${progdir}/${newProg}"
+ fi
+done
+oldwd=`pwd`
+progdir=`dirname "${prog}"`
+cd "${progdir}"
+progdir=`pwd`
+prog="${progdir}"/`basename "${prog}"`
+cd "${oldwd}"
+
+jarfile=dexdeps.jar
+libdir="$progdir"
+if [ ! -r "$libdir/$jarfile" ]
+then
+ libdir=`dirname "$progdir"`/tools/lib
+fi
+if [ ! -r "$libdir/$jarfile" ]
+then
+ libdir=`dirname "$progdir"`/framework
+fi
+if [ ! -r "$libdir/$jarfile" ]
+then
+ echo `basename "$prog"`": can't find $jarfile"
+ exit 1
+fi
+
+javaOpts=""
+
+# Alternatively, this will extract any parameter "-Jxxx" from the command line
+# and pass them to Java (instead of to dexdeps).
+while expr "x$1" : 'x-J' >/dev/null; do
+ opt=`expr "$1" : '-J\(.*\)'`
+ javaOpts="${javaOpts} -${opt}"
+ shift
+done
+
+if [ "$OSTYPE" = "cygwin" ] ; then
+ jarpath=`cygpath -w "$libdir/$jarfile"`
+else
+ jarpath="$libdir/$jarfile"
+fi
+
+exec java $javaOpts -jar "$jarpath" "$@"
diff --git a/tools/dexdeps/etc/manifest.txt b/tools/dexdeps/etc/manifest.txt
new file mode 100644
index 000000000..760674402
--- /dev/null
+++ b/tools/dexdeps/etc/manifest.txt
@@ -0,0 +1 @@
+Main-Class: com.android.dexdeps.Main
diff --git a/tools/dexdeps/src/Android.mk b/tools/dexdeps/src/Android.mk
new file mode 100644
index 000000000..756a0b370
--- /dev/null
+++ b/tools/dexdeps/src/Android.mk
@@ -0,0 +1,32 @@
+# Copyright (C) 2009 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.
+
+LOCAL_PATH := $(call my-dir)
+
+
+# dexdeps java library
+# ============================================================
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_JAR_MANIFEST := ../etc/manifest.txt
+
+LOCAL_MODULE:= dexdeps
+
+include $(BUILD_HOST_JAVA_LIBRARY)
+
+INTERNAL_DALVIK_MODULES += $(LOCAL_INSTALLED_MODULE)
+
+include $(BUILD_DROIDDOC)
+
diff --git a/tools/dexdeps/src/com/android/dexdeps/DexData.java b/tools/dexdeps/src/com/android/dexdeps/DexData.java
new file mode 100644
index 000000000..0bfd20253
--- /dev/null
+++ b/tools/dexdeps/src/com/android/dexdeps/DexData.java
@@ -0,0 +1,656 @@
+/*
+ * Copyright (C) 2009 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.dexdeps;
+
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.util.Arrays;
+
+/**
+ * Data extracted from a DEX file.
+ */
+public class DexData {
+ private RandomAccessFile mDexFile;
+ private HeaderItem mHeaderItem;
+ private String[] mStrings; // strings from string_data_*
+ private TypeIdItem[] mTypeIds;
+ private ProtoIdItem[] mProtoIds;
+ private FieldIdItem[] mFieldIds;
+ private MethodIdItem[] mMethodIds;
+ private ClassDefItem[] mClassDefs;
+
+ private byte tmpBuf[] = new byte[4];
+ private boolean isBigEndian = false;
+
+ /**
+ * Constructs a new DexData for this file.
+ */
+ public DexData(RandomAccessFile raf) {
+ mDexFile = raf;
+ }
+
+ /**
+ * Loads the contents of the DEX file into our data structures.
+ *
+ * @throws IOException if we encounter a problem while reading
+ * @throws DexDataException if the DEX contents look bad
+ */
+ public void load() throws IOException {
+ parseHeaderItem();
+
+ loadStrings();
+ loadTypeIds();
+ loadProtoIds();
+ loadFieldIds();
+ loadMethodIds();
+ loadClassDefs();
+
+ markInternalClasses();
+ }
+
+
+ /**
+ * Parses the interesting bits out of the header.
+ */
+ void parseHeaderItem() throws IOException {
+ mHeaderItem = new HeaderItem();
+
+ seek(0);
+
+ byte[] magic = new byte[8];
+ readBytes(magic);
+ if (!Arrays.equals(magic, HeaderItem.DEX_FILE_MAGIC)) {
+ System.err.println("Magic number is wrong -- are you sure " +
+ "this is a DEX file?");
+ throw new DexDataException();
+ }
+
+ /*
+ * Read the endian tag, so we properly swap things as we read
+ * them from here on.
+ */
+ seek(8+4+20+4+4);
+ mHeaderItem.endianTag = readInt();
+ if (mHeaderItem.endianTag == HeaderItem.ENDIAN_CONSTANT) {
+ /* do nothing */
+ } else if (mHeaderItem.endianTag == HeaderItem.REVERSE_ENDIAN_CONSTANT){
+ /* file is big-endian (!), reverse future reads */
+ isBigEndian = true;
+ } else {
+ System.err.println("Endian constant has unexpected value " +
+ Integer.toHexString(mHeaderItem.endianTag));
+ throw new DexDataException();
+ }
+
+ seek(8+4+20); // magic, checksum, signature
+ mHeaderItem.fileSize = readInt();
+ mHeaderItem.headerSize = readInt();
+ /*mHeaderItem.endianTag =*/ readInt();
+ /*mHeaderItem.linkSize =*/ readInt();
+ /*mHeaderItem.linkOff =*/ readInt();
+ /*mHeaderItem.mapOff =*/ readInt();
+ mHeaderItem.stringIdsSize = readInt();
+ mHeaderItem.stringIdsOff = readInt();
+ mHeaderItem.typeIdsSize = readInt();
+ mHeaderItem.typeIdsOff = readInt();
+ mHeaderItem.protoIdsSize = readInt();
+ mHeaderItem.protoIdsOff = readInt();
+ mHeaderItem.fieldIdsSize = readInt();
+ mHeaderItem.fieldIdsOff = readInt();
+ mHeaderItem.methodIdsSize = readInt();
+ mHeaderItem.methodIdsOff = readInt();
+ mHeaderItem.classDefsSize = readInt();
+ mHeaderItem.classDefsOff = readInt();
+ /*mHeaderItem.dataSize =*/ readInt();
+ /*mHeaderItem.dataOff =*/ readInt();
+ }
+
+ /**
+ * Loads the string table out of the DEX.
+ *
+ * First we read all of the string_id_items, then we read all of the
+ * string_data_item. Doing it this way should allow us to avoid
+ * seeking around in the file.
+ */
+ void loadStrings() throws IOException {
+ int count = mHeaderItem.stringIdsSize;
+ int stringOffsets[] = new int[count];
+
+ //System.out.println("reading " + count + " strings");
+
+ seek(mHeaderItem.stringIdsOff);
+ for (int i = 0; i < count; i++) {
+ stringOffsets[i] = readInt();
+ }
+
+ mStrings = new String[count];
+
+ seek(stringOffsets[0]);
+ for (int i = 0; i < count; i++) {
+ seek(stringOffsets[i]); // should be a no-op
+ mStrings[i] = readString();
+ //System.out.println("STR: " + i + ": " + mStrings[i]);
+ }
+ }
+
+ /**
+ * Loads the type ID list.
+ */
+ void loadTypeIds() throws IOException {
+ int count = mHeaderItem.typeIdsSize;
+ mTypeIds = new TypeIdItem[count];
+
+ //System.out.println("reading " + count + " typeIds");
+ seek(mHeaderItem.typeIdsOff);
+ for (int i = 0; i < count; i++) {
+ mTypeIds[i] = new TypeIdItem();
+ mTypeIds[i].descriptorIdx = readInt();
+
+ //System.out.println(i + ": " + mTypeIds[i].descriptorIdx +
+ // " " + mStrings[mTypeIds[i].descriptorIdx]);
+ }
+ }
+
+ /**
+ * Loads the proto ID list.
+ */
+ void loadProtoIds() throws IOException {
+ int count = mHeaderItem.protoIdsSize;
+ mProtoIds = new ProtoIdItem[count];
+
+ //System.out.println("reading " + count + " protoIds");
+ seek(mHeaderItem.protoIdsOff);
+
+ /*
+ * Read the proto ID items.
+ */
+ for (int i = 0; i < count; i++) {
+ mProtoIds[i] = new ProtoIdItem();
+ mProtoIds[i].shortyIdx = readInt();
+ mProtoIds[i].returnTypeIdx = readInt();
+ mProtoIds[i].parametersOff = readInt();
+
+ //System.out.println(i + ": " + mProtoIds[i].shortyIdx +
+ // " " + mStrings[mProtoIds[i].shortyIdx]);
+ }
+
+ /*
+ * Go back through and read the type lists.
+ */
+ for (int i = 0; i < count; i++) {
+ ProtoIdItem protoId = mProtoIds[i];
+
+ int offset = protoId.parametersOff;
+
+ if (offset == 0) {
+ protoId.types = new int[0];
+ continue;
+ } else {
+ seek(offset);
+ int size = readInt(); // #of entries in list
+ protoId.types = new int[size];
+
+ for (int j = 0; j < size; j++) {
+ protoId.types[j] = readShort() & 0xffff;
+ }
+ }
+ }
+ }
+
+ /**
+ * Loads the field ID list.
+ */
+ void loadFieldIds() throws IOException {
+ int count = mHeaderItem.fieldIdsSize;
+ mFieldIds = new FieldIdItem[count];
+
+ //System.out.println("reading " + count + " fieldIds");
+ seek(mHeaderItem.fieldIdsOff);
+ for (int i = 0; i < count; i++) {
+ mFieldIds[i] = new FieldIdItem();
+ mFieldIds[i].classIdx = readShort() & 0xffff;
+ mFieldIds[i].typeIdx = readShort() & 0xffff;
+ mFieldIds[i].nameIdx = readInt();
+
+ //System.out.println(i + ": " + mFieldIds[i].nameIdx +
+ // " " + mStrings[mFieldIds[i].nameIdx]);
+ }
+ }
+
+ /**
+ * Loads the method ID list.
+ */
+ void loadMethodIds() throws IOException {
+ int count = mHeaderItem.methodIdsSize;
+ mMethodIds = new MethodIdItem[count];
+
+ //System.out.println("reading " + count + " methodIds");
+ seek(mHeaderItem.methodIdsOff);
+ for (int i = 0; i < count; i++) {
+ mMethodIds[i] = new MethodIdItem();
+ mMethodIds[i].classIdx = readShort() & 0xffff;
+ mMethodIds[i].protoIdx = readShort() & 0xffff;
+ mMethodIds[i].nameIdx = readInt();
+
+ //System.out.println(i + ": " + mMethodIds[i].nameIdx +
+ // " " + mStrings[mMethodIds[i].nameIdx]);
+ }
+ }
+
+ /**
+ * Loads the class defs list.
+ */
+ void loadClassDefs() throws IOException {
+ int count = mHeaderItem.classDefsSize;
+ mClassDefs = new ClassDefItem[count];
+
+ //System.out.println("reading " + count + " classDefs");
+ seek(mHeaderItem.classDefsOff);
+ for (int i = 0; i < count; i++) {
+ mClassDefs[i] = new ClassDefItem();
+ mClassDefs[i].classIdx = readInt();
+
+ /* access_flags = */ readInt();
+ /* superclass_idx = */ readInt();
+ /* interfaces_off = */ readInt();
+ /* source_file_idx = */ readInt();
+ /* annotations_off = */ readInt();
+ /* class_data_off = */ readInt();
+ /* static_values_off = */ readInt();
+
+ //System.out.println(i + ": " + mClassDefs[i].classIdx + " " +
+ // mStrings[mTypeIds[mClassDefs[i].classIdx].descriptorIdx]);
+ }
+ }
+
+ /**
+ * Sets the "internal" flag on type IDs which are defined in the
+ * DEX file or within the VM (e.g. primitive classes and arrays).
+ */
+ void markInternalClasses() {
+ for (int i = mClassDefs.length -1; i >= 0; i--) {
+ mTypeIds[mClassDefs[i].classIdx].internal = true;
+ }
+
+ for (int i = 0; i < mTypeIds.length; i++) {
+ String className = mStrings[mTypeIds[i].descriptorIdx];
+
+ if (className.length() == 1) {
+ // primitive class
+ mTypeIds[i].internal = true;
+ } else if (className.charAt(0) == '[') {
+ mTypeIds[i].internal = true;
+ }
+
+ //System.out.println(i + " " +
+ // (mTypeIds[i].internal ? "INTERNAL" : "external") + " - " +
+ // mStrings[mTypeIds[i].descriptorIdx]);
+ }
+ }
+
+
+ /*
+ * =======================================================================
+ * Queries
+ * =======================================================================
+ */
+
+ /**
+ * Converts a single-character primitive type into its human-readable
+ * equivalent.
+ */
+ private String primitiveTypeLabel(char typeChar) {
+ /* primitive type; substitute human-readable name in */
+ switch (typeChar) {
+ case 'B': return "byte";
+ case 'C': return "char";
+ case 'D': return "double";
+ case 'F': return "float";
+ case 'I': return "int";
+ case 'J': return "long";
+ case 'S': return "short";
+ case 'V': return "void";
+ case 'Z': return "boolean";
+ default:
+ /* huh? */
+ System.err.println("Unexpected class char " + typeChar);
+ assert false;
+ return "UNKNOWN";
+ }
+ }
+
+ /**
+ * Converts a descriptor to dotted form. For example,
+ * "Ljava/lang/String;" becomes "java.lang.String", and "[I" becomes
+ * "int[].
+ */
+ private String descriptorToDot(String descr) {
+ int targetLen = descr.length();
+ int offset = 0;
+ int arrayDepth = 0;
+
+ /* strip leading [s; will be added to end */
+ while (targetLen > 1 && descr.charAt(offset) == '[') {
+ offset++;
+ targetLen--;
+ }
+ arrayDepth = offset;
+
+ if (targetLen == 1) {
+ descr = primitiveTypeLabel(descr.charAt(offset));
+ offset = 0;
+ targetLen = descr.length();
+ } else {
+ /* account for leading 'L' and trailing ';' */
+ if (targetLen >= 2 && descr.charAt(offset) == 'L' &&
+ descr.charAt(offset+targetLen-1) == ';')
+ {
+ targetLen -= 2; /* two fewer chars to copy */
+ offset++; /* skip the 'L' */
+ }
+ }
+
+ char[] buf = new char[targetLen + arrayDepth * 2];
+
+ /* copy class name over */
+ int i;
+ for (i = 0; i < targetLen; i++) {
+ char ch = descr.charAt(offset + i);
+ buf[i] = (ch == '/') ? '.' : ch;
+ }
+
+ /* add the appopriate number of brackets for arrays */
+ while (arrayDepth-- > 0) {
+ buf[i++] = '[';
+ buf[i++] = ']';
+ }
+ assert i == buf.length;
+
+ return new String(buf);
+ }
+
+ /**
+ * Returns the dot-form class name, given an index into the type_ids
+ * table.
+ */
+ private String classNameFromTypeIndex(int idx) {
+ String descriptor = mStrings[mTypeIds[idx].descriptorIdx];
+ return descriptorToDot(descriptor);
+ }
+
+ /**
+ * Returns the method prototype descriptor, given an index into the
+ * proto_ids table.
+ */
+ private String protoStringFromProtoIndex(int idx) {
+ StringBuilder builder = new StringBuilder();
+ ProtoIdItem protoId = mProtoIds[idx];
+
+ builder.append("(");
+ for (int i = 0; i < protoId.types.length; i++) {
+ String elem = mStrings[mTypeIds[protoId.types[i]].descriptorIdx];
+ builder.append(elem);
+ }
+
+ builder.append(")");
+ String ret = mStrings[mTypeIds[protoId.returnTypeIdx].descriptorIdx];
+ builder.append(ret);
+
+ return builder.toString();
+ }
+
+ /**
+ * Returns an array with all of the field references that don't
+ * correspond to classes in the DEX file.
+ */
+ public FieldRef[] getExternalFieldReferences() {
+ // get a count
+ int count = 0;
+ for (int i = 0; i < mFieldIds.length; i++) {
+ if (!mTypeIds[mFieldIds[i].classIdx].internal)
+ count++;
+ }
+
+ //System.out.println("count is " + count + " of " + mFieldIds.length);
+
+ FieldRef[] fieldRefs = new FieldRef[count];
+ count = 0;
+ for (int i = 0; i < mFieldIds.length; i++) {
+ if (!mTypeIds[mFieldIds[i].classIdx].internal) {
+ FieldIdItem fieldId = mFieldIds[i];
+ fieldRefs[count++] =
+ new FieldRef(classNameFromTypeIndex(fieldId.classIdx),
+ classNameFromTypeIndex(fieldId.typeIdx),
+ mStrings[fieldId.nameIdx]);
+ }
+ }
+
+ assert count == fieldRefs.length;
+
+ return fieldRefs;
+ }
+
+ /**
+ * Returns an array with all of the method references that don't
+ * correspond to classes in the DEX file.
+ */
+ public MethodRef[] getExternalMethodReferences() {
+ // get a count
+ int count = 0;
+ for (int i = 0; i < mMethodIds.length; i++) {
+ if (!mTypeIds[mMethodIds[i].classIdx].internal)
+ count++;
+ }
+
+ //System.out.println("count is " + count + " of " + mMethodIds.length);
+
+ MethodRef[] methodRefs = new MethodRef[count];
+ count = 0;
+ for (int i = 0; i < mMethodIds.length; i++) {
+ if (!mTypeIds[mMethodIds[i].classIdx].internal) {
+ MethodIdItem methodId = mMethodIds[i];
+ methodRefs[count++] =
+ new MethodRef(classNameFromTypeIndex(methodId.classIdx),
+ protoStringFromProtoIndex(methodId.protoIdx),
+ mStrings[methodId.nameIdx]);
+ }
+ }
+
+ assert count == methodRefs.length;
+
+ return methodRefs;
+ }
+
+ /*
+ * =======================================================================
+ * Basic I/O functions
+ * =======================================================================
+ */
+
+ /**
+ * Seeks the DEX file to the specified absolute position.
+ */
+ void seek(int position) throws IOException {
+ mDexFile.seek(position);
+ }
+
+ /**
+ * Fills the buffer by reading bytes from the DEX file.
+ */
+ void readBytes(byte[] buffer) throws IOException {
+ mDexFile.readFully(buffer);
+ }
+
+ /**
+ * Reads a single signed byte value.
+ */
+ byte readByte() throws IOException {
+ mDexFile.readFully(tmpBuf, 0, 1);
+ return tmpBuf[0];
+ }
+
+ /**
+ * Reads a signed 16-bit integer, byte-swapping if necessary.
+ */
+ short readShort() throws IOException {
+ mDexFile.readFully(tmpBuf, 0, 2);
+ if (isBigEndian) {
+ return (short) ((tmpBuf[1] & 0xff) | ((tmpBuf[0] & 0xff) << 8));
+ } else {
+ return (short) ((tmpBuf[0] & 0xff) | ((tmpBuf[1] & 0xff) << 8));
+ }
+ }
+
+ /**
+ * Reads a signed 32-bit integer, byte-swapping if necessary.
+ */
+ int readInt() throws IOException {
+ mDexFile.readFully(tmpBuf, 0, 4);
+
+ if (isBigEndian) {
+ return (tmpBuf[3] & 0xff) | ((tmpBuf[2] & 0xff) << 8) |
+ ((tmpBuf[1] & 0xff) << 16) | ((tmpBuf[0] & 0xff) << 24);
+ } else {
+ return (tmpBuf[0] & 0xff) | ((tmpBuf[1] & 0xff) << 8) |
+ ((tmpBuf[2] & 0xff) << 16) | ((tmpBuf[3] & 0xff) << 24);
+ }
+ }
+
+ /**
+ * Reads a variable-length unsigned LEB128 value. Does not attempt to
+ * verify that the value is valid.
+ *
+ * @throws EOFException if we run off the end of the file
+ */
+ int readUnsignedLeb128() throws IOException {
+ int result = 0;
+ byte val;
+
+ do {
+ val = readByte();
+ result = (result << 7) | (val & 0x7f);
+ } while (val < 0);
+
+ return result;
+ }
+
+ /**
+ * Reads a UTF-8 string.
+ *
+ * We don't know how long the UTF-8 string is, so we have to read one
+ * byte at a time. We could make an educated guess based on the
+ * utf16_size and seek back if we get it wrong, but seeking backward
+ * may cause the underlying implementation to reload I/O buffers.
+ */
+ String readString() throws IOException {
+ int utf16len = readUnsignedLeb128();
+ byte inBuf[] = new byte[utf16len * 3]; // worst case
+ int idx;
+
+ for (idx = 0; idx < inBuf.length; idx++) {
+ byte val = readByte();
+ if (val == 0)
+ break;
+ inBuf[idx] = val;
+ }
+
+ return new String(inBuf, 0, idx, "UTF-8");
+ }
+
+
+ /*
+ * =======================================================================
+ * Internal "structure" declarations
+ * =======================================================================
+ */
+
+ /**
+ * Holds the contents of a header_item.
+ */
+ static class HeaderItem {
+ public int fileSize;
+ public int headerSize;
+ public int endianTag;
+ public int stringIdsSize, stringIdsOff;
+ public int typeIdsSize, typeIdsOff;
+ public int protoIdsSize, protoIdsOff;
+ public int fieldIdsSize, fieldIdsOff;
+ public int methodIdsSize, methodIdsOff;
+ public int classDefsSize, classDefsOff;
+
+ /* expected magic values */
+ public static final byte[] DEX_FILE_MAGIC = {
+ 0x64, 0x65, 0x78, 0x0a, 0x30, 0x33, 0x35, 0x00 };
+ public static final int ENDIAN_CONSTANT = 0x12345678;
+ public static final int REVERSE_ENDIAN_CONSTANT = 0x78563412;
+ }
+
+ /**
+ * Holds the contents of a type_id_item.
+ *
+ * This is chiefly a list of indices into the string table. We need
+ * some additional bits of data, such as whether or not the type ID
+ * represents a class defined in this DEX, so we use an object for
+ * each instead of a simple integer. (Could use a parallel array, but
+ * since this is a desktop app it's not essential.)
+ */
+ static class TypeIdItem {
+ public int descriptorIdx; // index into string_ids
+
+ public boolean internal; // defined within this DEX file?
+ }
+
+ /**
+ * Holds the contents of a proto_id_item.
+ */
+ static class ProtoIdItem {
+ public int shortyIdx; // index into string_ids
+ public int returnTypeIdx; // index into type_ids
+ public int parametersOff; // file offset to a type_list
+
+ public int types[]; // contents of type list
+ }
+
+ /**
+ * Holds the contents of a field_id_item.
+ */
+ static class FieldIdItem {
+ public int classIdx; // index into type_ids (defining class)
+ public int typeIdx; // index into type_ids (field type)
+ public int nameIdx; // index into string_ids
+ }
+
+ /**
+ * Holds the contents of a method_id_item.
+ */
+ static class MethodIdItem {
+ public int classIdx; // index into type_ids
+ public int protoIdx; // index into proto_ids
+ public int nameIdx; // index into string_ids
+ }
+
+ /**
+ * Holds the contents of a class_def_item.
+ *
+ * We don't really need a class for this, but there's some stuff in
+ * the class_def_item that we might want later.
+ */
+ static class ClassDefItem {
+ public int classIdx; // index into type_ids
+ }
+}
+
diff --git a/tools/dexdeps/src/com/android/dexdeps/DexDataException.java b/tools/dexdeps/src/com/android/dexdeps/DexDataException.java
new file mode 100644
index 000000000..e51853f53
--- /dev/null
+++ b/tools/dexdeps/src/com/android/dexdeps/DexDataException.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2009 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.dexdeps;
+
+/**
+ * Bad data found inside a DEX file.
+ */
+public class DexDataException extends RuntimeException {
+}
+
diff --git a/tools/dexdeps/src/com/android/dexdeps/FieldRef.java b/tools/dexdeps/src/com/android/dexdeps/FieldRef.java
new file mode 100644
index 000000000..96e0ef01f
--- /dev/null
+++ b/tools/dexdeps/src/com/android/dexdeps/FieldRef.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2009 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.dexdeps;
+
+public class FieldRef {
+ private String mDeclClass, mFieldType, mFieldName;
+
+ /**
+ * Initializes a new field reference.
+ */
+ public FieldRef(String declClass, String fieldType, String fieldName) {
+ mDeclClass = declClass;
+ mFieldType = fieldType;
+ mFieldName = fieldName;
+ }
+
+ public String getDeclClassName() {
+ return mDeclClass;
+ }
+
+ public String getTypeName() {
+ return mFieldType;
+ }
+
+ public String getName() {
+ return mFieldName;
+ }
+}
+
diff --git a/tools/dexdeps/src/com/android/dexdeps/Main.java b/tools/dexdeps/src/com/android/dexdeps/Main.java
new file mode 100644
index 000000000..43698458f
--- /dev/null
+++ b/tools/dexdeps/src/com/android/dexdeps/Main.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2009 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.dexdeps;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.RandomAccessFile;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipException;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipInputStream;
+
+public class Main {
+ private static final String CLASSES_DEX = "classes.dex";
+
+ private String mInputFileName;
+ private String mOutputFormat = "brief";
+
+ /**
+ * Entry point.
+ */
+ public static void main(String[] args) {
+ Main main = new Main();
+ main.run(args);
+ }
+
+ /**
+ * Start things up.
+ */
+ void run(String[] args) {
+ try {
+ parseArgs(args);
+ RandomAccessFile raf = openInputFile();
+ DexData dexData = new DexData(raf);
+ dexData.load();
+
+ Output.generate(dexData, mOutputFormat);
+ } catch (UsageException ue) {
+ usage();
+ System.exit(2);
+ } catch (IOException ioe) {
+ /* a message was already reported, just bail quietly */
+ System.exit(1);
+ } catch (DexDataException dde) {
+ /* a message was already reported, just bail quietly */
+ System.exit(1);
+ }
+ }
+
+ /**
+ * Opens the input file, which could be a .dex or a .jar/.apk with a
+ * classes.dex inside. If the latter, we extract the contents to a
+ * temporary file.
+ */
+ RandomAccessFile openInputFile() throws IOException {
+ RandomAccessFile raf;
+
+ raf = openInputFileAsZip();
+ if (raf == null) {
+ File inputFile = new File(mInputFileName);
+ raf = new RandomAccessFile(inputFile, "r");
+ }
+
+ return raf;
+ }
+
+ /**
+ * Tries to open the input file as a Zip archive (jar/apk) with a
+ * "classes.dex" inside.
+ *
+ * @return a RandomAccessFile for classes.dex, or null if the input file
+ * is not a zip archive
+ * @throws IOException if the file isn't found, or it's a zip and
+ * classes.dex isn't found inside
+ */
+ RandomAccessFile openInputFileAsZip() throws IOException {
+ ZipFile zipFile;
+
+ /*
+ * Try it as a zip file.
+ */
+ try {
+ zipFile = new ZipFile(mInputFileName);
+ } catch (FileNotFoundException fnfe) {
+ /* not found, no point in retrying as non-zip */
+ System.err.println("Unable to open '" + mInputFileName + "': " +
+ fnfe.getMessage());
+ throw fnfe;
+ } catch (ZipException ze) {
+ /* not a zip */
+ return null;
+ }
+
+ /*
+ * We know it's a zip; see if there's anything useful inside. A
+ * failure here results in some type of IOException (of which
+ * ZipException is a subclass).
+ */
+ ZipEntry entry = zipFile.getEntry(CLASSES_DEX);
+ if (entry == null) {
+ System.err.println("Unable to find '" + CLASSES_DEX +
+ "' in '" + mInputFileName + "'");
+ zipFile.close();
+ throw new ZipException();
+ }
+
+ InputStream zis = zipFile.getInputStream(entry);
+
+ /*
+ * Create a temp file to hold the DEX data, open it, and delete it
+ * to ensure it doesn't hang around if we fail.
+ */
+ File tempFile = File.createTempFile("dexdeps", ".dex");
+ System.out.println("+++ using temp " + tempFile);
+ RandomAccessFile raf = new RandomAccessFile(tempFile, "rw");
+ tempFile.delete();
+
+ /*
+ * Copy all data from input stream to output file.
+ */
+ byte copyBuf[] = new byte[32768];
+ int actual;
+
+ while (true) {
+ actual = zis.read(copyBuf);
+ if (actual == -1)
+ break;
+
+ raf.write(copyBuf, 0, actual);
+ }
+
+ zis.close();
+ raf.seek(0);
+
+ return raf;
+ }
+
+
+ /**
+ * Parses command-line arguments.
+ *
+ * @throws UsageException if arguments are missing or poorly formed
+ */
+ void parseArgs(String[] args) {
+ int idx;
+
+ for (idx = 0; idx < args.length; idx++) {
+ String arg = args[idx];
+
+ if (arg.equals("--") || !arg.startsWith("--")) {
+ break;
+ } else if (arg.startsWith("--format=")) {
+ mOutputFormat = arg.substring(arg.indexOf('=') + 1);
+ if (!mOutputFormat.equals("brief") &&
+ !mOutputFormat.equals("xml"))
+ {
+ System.err.println("Unknown format '" + mOutputFormat +"'");
+ throw new UsageException();
+ }
+ System.out.println("Using format " + mOutputFormat);
+ } else {
+ System.err.println("Unknown option '" + arg + "'");
+ throw new UsageException();
+ }
+ }
+
+ // expecting one argument left
+ if (idx != args.length - 1) {
+ throw new UsageException();
+ }
+
+ mInputFileName = args[idx];
+ }
+
+ /**
+ * Prints command-line usage info.
+ */
+ void usage() {
+ System.err.println("\nUsage: dexdeps [options] <file.{dex,apk,jar}>");
+ System.err.println("Options:");
+ System.err.println(" --format={brief,xml}");
+ }
+}
+
diff --git a/tools/dexdeps/src/com/android/dexdeps/MethodRef.java b/tools/dexdeps/src/com/android/dexdeps/MethodRef.java
new file mode 100644
index 000000000..1ca68528d
--- /dev/null
+++ b/tools/dexdeps/src/com/android/dexdeps/MethodRef.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2009 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.dexdeps;
+
+public class MethodRef {
+ private String mDeclClass, mDescriptor, mMethodName;
+
+ /**
+ * Initializes a new field reference.
+ */
+ public MethodRef(String declClass, String descriptor, String methodName) {
+ mDeclClass = declClass;
+ mDescriptor = descriptor;
+ mMethodName = methodName;
+ }
+
+ /**
+ * Gets the name of the method's declaring class.
+ */
+ public String getDeclClassName() {
+ return mDeclClass;
+ }
+
+ /**
+ * Gets the method's descriptor.
+ */
+ public String getDescriptor() {
+ return mDescriptor;
+ }
+
+ /**
+ * Gets the method's name.
+ */
+ public String getName() {
+ return mMethodName;
+ }
+
+ /**
+ * Gets the method arguments as an array of type strings.
+ */
+ public String[] getArguments() {
+ // TODO
+ return null;
+ }
+
+ /**
+ * Gets the method's return type.
+ */
+ public String getReturnType() {
+ // TODO
+ return null;
+ }
+}
+
+
diff --git a/tools/dexdeps/src/com/android/dexdeps/Output.java b/tools/dexdeps/src/com/android/dexdeps/Output.java
new file mode 100644
index 000000000..0c350b68a
--- /dev/null
+++ b/tools/dexdeps/src/com/android/dexdeps/Output.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2009 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.dexdeps;
+
+/**
+ * Generate fancy output.
+ */
+public class Output {
+ public static void generate(DexData dexData, String format) {
+ FieldRef[] externFieldRefs;
+ MethodRef[] externMethodRefs;
+
+ externFieldRefs = dexData.getExternalFieldReferences();
+ externMethodRefs = dexData.getExternalMethodReferences();
+
+ if (format.equals("brief")) {
+ printFieldRefs(externFieldRefs);
+ printMethodRefs(externMethodRefs);
+ } else if (format.equals("xml")) {
+ // ...
+ } else {
+ /* should've been trapped in arg handler */
+ throw new RuntimeException("unknown output format");
+ }
+ }
+
+ static void printFieldRefs(FieldRef[] fields) {
+ System.out.println("Fields:");
+ for (int i = 0; i < fields.length; i++) {
+ FieldRef ref = fields[i];
+
+ System.out.println(ref.getDeclClassName() + "." +
+ ref.getName() + " : " + ref.getTypeName());
+ }
+ }
+
+ static void printMethodRefs(MethodRef[] methods) {
+ System.out.println("Methods:");
+ for (int i = 0; i < methods.length; i++) {
+ MethodRef ref = methods[i];
+
+ System.out.println(ref.getDeclClassName() + "." +
+ ref.getName() + " : " + ref.getDescriptor());
+ }
+ }
+}
+
diff --git a/tools/dexdeps/src/com/android/dexdeps/UsageException.java b/tools/dexdeps/src/com/android/dexdeps/UsageException.java
new file mode 100644
index 000000000..f9f971b78
--- /dev/null
+++ b/tools/dexdeps/src/com/android/dexdeps/UsageException.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2009 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.dexdeps;
+
+/**
+ * Tells the main entry point to show the usage information and bail.
+ */
+public class UsageException extends RuntimeException {
+}
+