From 83b80f81d311b233188c281059aad4a9f5e8b4e6 Mon Sep 17 00:00:00 2001
From: "JesusFreke@JesusFreke.com"
These are the main use cases that drove the design of this library Annotate an existing dex file - In this case, the intent is to document the structure of
+ * an existing dex file. We want to be able to read in the dex file, and then write out a dex file
+ * that is exactly the same (while adding annotation information to an AnnotatedOutput object) Canonicalize an existing dex file - In this case, the intent is to rewrite an existing dex file
+ * so that it is in a canonical form. There is a certain amount of leeway in how various types of
+ * tems in a dex file are ordered or represented. It is sometimes useful to be able to easily
+ * compare a disassebled and reassembled dex file with the original dex file. If both dex-files are
+ * written canonically, they "should" match exactly, barring any explicit changes to the reassembled
+ * file. Currently, there are a couple of pieces of information that probably won't match exactly
+ * AnnotationDirectoryItem
+ * @param dexFile The DexFile
that this item belongs to
+ */
+ protected AnnotationDirectoryItem(DexFile dexFile) {
+ super(dexFile);
+ }
+
+ /**
+ * Creates a new AnnotationDirectoryItem
with the given values
+ * @param dexFile The DexFile
that this item belongs to
+ * @param classAnnotations The annotations associated with the overall class
+ * @param fieldAnnotationFields An array of FieldIdItem
objects that the annotations in
+ * fieldAnnotations
are associated with
+ * @param fieldAnnotations An array of AnnotationSetItem
objects that contain the annotations for the
+ * fields in fieldAnnotationFields
+ * @param methodAnnotationMethods An array of MethodIdItem
objects that the annotations in
+ * methodAnnotations
are associated with
+ * @param methodAnnotations An array of AnnotationSetItem
objects that contain the annotations for the
+ * methods in methodAnnotationMethods
+ * @param parameterAnnotationMethods An array of MethodIdItem
objects that the annotations in
+ * parameterAnnotations
are associated with
+ * @param parameterAnnotations An array of AnnotationSetRefList
objects that contain the parameter
+ * annotations for the methods in parameterAnnotationMethods
+ */
+ private AnnotationDirectoryItem(DexFile dexFile, AnnotationSetItem classAnnotations,
+ FieldIdItem[] fieldAnnotationFields, AnnotationSetItem[] fieldAnnotations,
+ MethodIdItem[] methodAnnotationMethods, AnnotationSetItem[] methodAnnotations,
+ MethodIdItem[] parameterAnnotationMethods,
+ AnnotationSetRefList[] parameterAnnotations) {
+ super(dexFile);
+ this.classAnnotations = classAnnotations;
+ this.fieldAnnotationFields = fieldAnnotationFields;
+ this.fieldAnnotations = fieldAnnotations;
+ this.methodAnnotationMethods = methodAnnotationMethods;
+ this.methodAnnotations = methodAnnotations;
+ this.parameterAnnotationMethods = parameterAnnotationMethods;
+ this.parameterAnnotations = parameterAnnotations;
+ }
+
+ /**
+ * Returns an AnnotationDirectoryItem
for the given values, and that has been interned into the given
+ * DexFile
+ * @param dexFile The DexFile
that this item belongs to
+ * @param classAnnotations The annotations associated with the class
+ * @param fieldAnnotationFields An array of FieldIdItem
objects that the annotations in
+ * fieldAnnotations
are associated with
+ * @param fieldAnnotations An array of AnnotationSetItem
objects that contain the annotations for the
+ * fields in fieldAnnotationFields
+ * @param methodAnnotationMethods An array of MethodIdItem
objects that the annotations in
+ * methodAnnotations
are associated with
+ * @param methodAnnotations An array of AnnotationSetItem
objects that contain the annotations for the
+ * methods in methodAnnotationMethods
+ * @param parameterAnnotationMethods An array of MethodIdItem
objects that the annotations in
+ * parameterAnnotations
are associated with
+ * @param parameterAnnotations An array of AnnotationSetRefList
objects that contain the parameter
+ * annotations for the methods in parameterAnnotationMethods
+ * @return an AnnotationItem
for the given values, and that has been interned into the given
+ * DexFile
+ */
+ public static AnnotationDirectoryItem getInternedAnnotationDirectoryItem(DexFile dexFile,
+ AnnotationSetItem classAnnotations,
+ FieldIdItem[] fieldAnnotationFields, AnnotationSetItem[] fieldAnnotations,
+ MethodIdItem[] methodAnnotationMethods, AnnotationSetItem[] methodAnnotations,
+ MethodIdItem[] parameterAnnotationMethods,
+ AnnotationSetRefList[] parameterAnnotations) {
+ AnnotationDirectoryItem annotationDirectoryItem = new AnnotationDirectoryItem(dexFile, classAnnotations,
+ fieldAnnotationFields, fieldAnnotations, methodAnnotationMethods, methodAnnotations,
+ parameterAnnotationMethods, parameterAnnotations);
+ return dexFile.AnnotationDirectoriesSection.intern(annotationDirectoryItem);
+ }
+
+ /** {@inheritDoc} */
+ protected void readItem(Input in, ReadContext readContext) {
+ readContext.getOffsettedItemByOffset(ItemType.TYPE_ANNOTATION_SET_ITEM, in.readInt());
+ fieldAnnotationFields = new FieldIdItem[in.readInt()];
+ fieldAnnotations = new AnnotationSetItem[fieldAnnotationFields.length];
+
+ methodAnnotationMethods = new MethodIdItem[in.readInt()];
+ methodAnnotations = new AnnotationSetItem[methodAnnotationMethods.length];
+
+ parameterAnnotationMethods = new MethodIdItem[in.readInt()];
+ parameterAnnotations = new AnnotationSetRefList[parameterAnnotationMethods.length];
+
+ for (int i=0; iClassDefItem
that this AnnotationDirectoryItem
is associated with.
+ * This is only applicable if this AnnotationDirectoryItem contains only class annotations, and no field, method
+ * or parameter annotations.
+ * @param classDefItem the ClassDefItem
that this AnnotationDirectoryItem
is associated
+ * with
+ */
+ protected void setParent(ClassDefItem classDefItem) {
+ this.parent = classDefItem;
+ }
+
+ @Override
+ public int hashCode() {
+ //an instance is only internable if it has only class annotations, but
+ //no other type of annotation
+ if (!isInternable()) {
+ return super.hashCode();
+ }
+ return classAnnotations.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this==o) {
+ return true;
+ }
+ if (o==null || !this.getClass().equals(o.getClass())) {
+ return false;
+ }
+
+ AnnotationDirectoryItem other = (AnnotationDirectoryItem)o;
+ return (this.compareTo(other) == 0);
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/AnnotationItem.java b/dexlib/src/main/java/org/jf/dexlib/AnnotationItem.java
new file mode 100644
index 00000000..ab523f22
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/AnnotationItem.java
@@ -0,0 +1,163 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2009 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib;
+
+import org.jf.dexlib.EncodedValue.AnnotationEncodedSubValue;
+import org.jf.dexlib.Util.Input;
+import org.jf.dexlib.Util.AnnotatedOutput;
+
+public class AnnotationItem extends ItemAnnotationItem
+ * @param dexFile The DexFile
that this item belongs to
+ */
+ protected AnnotationItem(DexFile dexFile) {
+ super(dexFile);
+ }
+
+ /**
+ * Creates a new AnnotationItem
with the given values
+ * @param dexFile The DexFile
that this item belongs to
+ * @param visibility The visibility of this annotation
+ * @param annotationValue The value of this annotation
+ */
+ private AnnotationItem(DexFile dexFile, AnnotationVisibility visibility,
+ AnnotationEncodedSubValue annotationValue) {
+ super(dexFile);
+ this.visibility = visibility;
+ this.annotationValue = annotationValue;
+ }
+
+ /**
+ * Returns an AnnotationItem
for the given values, and that has been interned into the given
+ * DexFile
+ * @param dexFile The DexFile
that this item belongs to
+ * @param visibility The visibility of this annotation
+ * @param annotationValue The value of this annotation
+ * @return an AnnotationItem
for the given values, and that has been interned into the given
+ * DexFile
+ */
+ public static AnnotationItem getInternedAnnotationItem(DexFile dexFile, AnnotationVisibility visibility,
+ AnnotationEncodedSubValue annotationValue) {
+ AnnotationItem annotationItem = new AnnotationItem(dexFile, visibility, annotationValue);
+ return dexFile.AnnotationsSection.intern(annotationItem);
+ }
+
+ /** {@inheritDoc} */
+ protected void readItem(Input in, ReadContext readContext) {
+ visibility = AnnotationVisibility.fromByte(in.readByte());
+ annotationValue = new AnnotationEncodedSubValue(dexFile, in);
+ }
+
+ /** {@inheritDoc} */
+ protected int placeItem(int offset) {
+ return annotationValue.placeValue(offset + 1);
+ }
+
+ /** {@inheritDoc} */
+ protected void writeItem(AnnotatedOutput out) {
+ if (out.annotates()) {
+ out.annotate("visibility");
+ out.writeByte(visibility.value);
+ out.annotate("annotation");
+ annotationValue.writeValue(out);
+ }else {
+ out.writeByte(visibility.value);
+ annotationValue.writeValue(out);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public ItemType getItemType() {
+ return ItemType.TYPE_ANNOTATION_ITEM;
+ }
+
+ /** {@inheritDoc} */
+ public String getConciseIdentity() {
+ return "annotation_item @0x" + Integer.toHexString(getOffset());
+ }
+
+ /** {@inheritDoc} */
+ public int compareTo(AnnotationItem o) {
+ int comp = visibility.value - o.visibility.value;
+ if (comp == 0) {
+ comp = annotationValue.compareTo(o.annotationValue);
+ }
+ return comp;
+ }
+
+ /**
+ * @return The visibility of this annotation
+ */
+ public AnnotationVisibility getVisibility() {
+ return visibility;
+ }
+
+ /**
+ * @return The encoded annotation value of this annotation
+ */
+ public AnnotationEncodedSubValue getEncodedAnnotation() {
+ return annotationValue;
+ }
+
+ /**
+ * calculate and cache the hashcode
+ */
+ private void calcHashCode() {
+ hashCode = visibility.value;
+ hashCode = hashCode * 31 + annotationValue.hashCode();
+ }
+
+ @Override
+ public int hashCode() {
+ //there's a small possibility that the actual hash code will be 0. If so, we'll
+ //just end up recalculating it each time
+ if (hashCode == 0)
+ calcHashCode();
+ return hashCode;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this==o) {
+ return true;
+ }
+ if (o==null || !this.getClass().equals(o.getClass())) {
+ return false;
+ }
+
+ AnnotationItem other = (AnnotationItem)o;
+ return visibility == other.visibility && annotationValue.equals(other.annotationValue);
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/AnnotationSetItem.java b/dexlib/src/main/java/org/jf/dexlib/AnnotationSetItem.java
new file mode 100644
index 00000000..33a17a99
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/AnnotationSetItem.java
@@ -0,0 +1,164 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2009 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib;
+
+import org.jf.dexlib.Util.Input;
+import org.jf.dexlib.Util.AnnotatedOutput;
+
+public class AnnotationSetItem extends ItemAnnotationSetItem
+ * @param dexFile The DexFile
that this item belongs to
+ */
+ protected AnnotationSetItem(DexFile dexFile) {
+ super(dexFile);
+ }
+
+ /**
+ * Creates a new AnnotationSetItem
for the given annotations
+ * @param dexFile The DexFile
that this item belongs to
+ * @param annotations The annotations for this AnnotationSetItem
+ */
+ private AnnotationSetItem(DexFile dexFile, AnnotationItem[] annotations) {
+ super(dexFile);
+ this.annotations = annotations;
+ }
+
+ /**
+ * Returns an AnnotationSetItem
for the given annotations, and that has been interned into the given
+ * DexFile
+ * @param dexFile The DexFile
that this item belongs to
+ * @param annotations The annotations for this AnnotationSetItem
+ * @return an AnnotationSetItem
for the given annotations
+ */
+ public static AnnotationSetItem getInternedAnnotationSetItem(DexFile dexFile, AnnotationItem[] annotations) {
+ AnnotationSetItem annotationSetItem = new AnnotationSetItem(dexFile, annotations);
+ return dexFile.AnnotationSetsSection.intern(annotationSetItem);
+ }
+
+ /** {@inheritDoc} */
+ protected void readItem(Input in, ReadContext readContext) {
+ annotations = new AnnotationItem[in.readInt()];
+
+ for (int i=0; iAnnotationSetItem
+ */
+ public AnnotationItem[] getAnnotations() {
+ return annotations;
+ }
+
+ /**
+ * calculate and cache the hashcode
+ */
+ private void calcHashCode() {
+ hashCode = 0;
+ for (AnnotationItem annotationItem: annotations) {
+ hashCode = hashCode * 31 + annotationItem.hashCode();
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ //there's a small possibility that the actual hash code will be 0. If so, we'll
+ //just end up recalculating it each time
+ if (hashCode == 0)
+ calcHashCode();
+ return hashCode;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this==o) {
+ return true;
+ }
+ if (o==null || !this.getClass().equals(o.getClass())) {
+ return false;
+ }
+
+ AnnotationSetItem other = (AnnotationSetItem)o;
+ return (this.compareTo(other) == 0);
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/AnnotationSetRefList.java b/dexlib/src/main/java/org/jf/dexlib/AnnotationSetRefList.java
new file mode 100644
index 00000000..d4509d4b
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/AnnotationSetRefList.java
@@ -0,0 +1,165 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2009 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib;
+
+import org.jf.dexlib.Util.Input;
+import org.jf.dexlib.Util.AnnotatedOutput;
+
+public class AnnotationSetRefList extends ItemAnnotationSetRefList
+ * @param dexFile The DexFile
that this item belongs to
+ */
+ protected AnnotationSetRefList(DexFile dexFile) {
+ super(dexFile);
+ }
+
+ /**
+ * Creates a new AnnotationSetRefList
for the given annotation sets
+ * @param dexFile The DexFile
that this item belongs to
+ * @param annotationSets The annotationSets for this AnnotationSetRefList
+ */
+ private AnnotationSetRefList(DexFile dexFile, AnnotationSetItem[] annotationSets) {
+ super(dexFile);
+ this.annotationSets = annotationSets;
+ }
+
+ /**
+ * Returns an AnnotationSetRefList
for the given annotation sets, and that has been interned into the
+ * given DexFile
+ * @param dexFile The DexFile
that this item belongs to
+ * @param annotationSets The annotation sets for this AnnotationSetRefList
+ * @return an AnnotationSetItem
for the given annotations
+ */
+ public static AnnotationSetRefList getInternedAnnotationSetRefList(DexFile dexFile,
+ AnnotationSetItem[] annotationSets) {
+ AnnotationSetRefList annotationSetRefList = new AnnotationSetRefList(dexFile, annotationSets);
+ return dexFile.AnnotationSetRefListsSection.intern(annotationSetRefList);
+ }
+
+ /** {@inheritDoc} */
+ protected void readItem(Input in, ReadContext readContext) {
+ annotationSets = new AnnotationSetItem[in.readInt()];
+
+ for (int i=0; iAnnotationSetRefList
+ */
+ public AnnotationSetItem[] getAnnotationSets() {
+ return annotationSets;
+ }
+
+ /**
+ * calculate and cache the hashcode
+ */
+ private void calcHashCode() {
+ hashCode = 0;
+ for (AnnotationSetItem annotationSetItem: annotationSets) {
+ hashCode = hashCode * 31 + annotationSetItem.hashCode();
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ //there's a small possibility that the actual hash code will be 0. If so, we'll
+ //just end up recalculating it each time
+ if (hashCode == 0)
+ calcHashCode();
+ return hashCode;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this==o) {
+ return true;
+ }
+ if (o==null || !this.getClass().equals(o.getClass())) {
+ return false;
+ }
+
+ AnnotationSetRefList other = (AnnotationSetRefList)o;
+ return (this.compareTo(other) == 0);
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/AnnotationVisibility.java b/dexlib/src/main/java/org/jf/dexlib/AnnotationVisibility.java
new file mode 100644
index 00000000..e82661fa
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/AnnotationVisibility.java
@@ -0,0 +1,53 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2009 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib;
+
+public enum AnnotationVisibility {
+ BUILD((byte)0),
+ RUNTIME((byte)1),
+ SYSTEM((byte)2);
+
+ public final byte value;
+ private AnnotationVisibility(byte value) {
+ this.value = value;
+ }
+
+ public static AnnotationVisibility fromByte(byte value) {
+ switch (value) {
+ case (byte)0:
+ return BUILD;
+ case (byte)1:
+ return RUNTIME;
+ case (byte)2:
+ return SYSTEM;
+ default:
+ throw new RuntimeException("Invalid annotation visibility value " + value);
+ }
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/ClassDataItem.java b/dexlib/src/main/java/org/jf/dexlib/ClassDataItem.java
new file mode 100644
index 00000000..45d35bae
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/ClassDataItem.java
@@ -0,0 +1,473 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2009 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib;
+
+import org.jf.dexlib.Util.*;
+
+public class ClassDataItem extends ItemClassDataItem
+ * @param dexFile The DexFile
that this item belongs to
+ */
+ public ClassDataItem(final DexFile dexFile) {
+ super(dexFile);
+ }
+
+ /**
+ * Creates a new ClassDataItem
with the given values
+ * @param dexFile The DexFile
that this item belongs to
+ * @param staticFields The static fields for this class
+ * @param instanceFields The instance fields for this class
+ * @param directMethods The direct methods for this class
+ * @param virtualMethods The virtual methods for this class
+ */
+ private ClassDataItem(DexFile dexFile, EncodedField[] staticFields, EncodedField[] instanceFields,
+ EncodedMethod[] directMethods, EncodedMethod[] virtualMethods) {
+ super(dexFile);
+ this.staticFields = staticFields==null?new EncodedField[0]:staticFields;
+ this.instanceFields = instanceFields==null?new EncodedField[0]:instanceFields;
+ this.directMethods = directMethods==null?new EncodedMethod[0]:directMethods;
+ this.virtualMethods = virtualMethods==null?new EncodedMethod[0]:virtualMethods;
+ }
+
+ /**
+ * Creates a new ClassDataItem
with the given values
+ * @param dexFile The DexFile
that this item belongs to
+ * @param staticFields The static fields for this class
+ * @param instanceFields The instance fields for this class
+ * @param directMethods The direct methods for this class
+ * @param virtualMethods The virtual methods for this class
+ * @return a new ClassDataItem
with the given values
+ */
+ public static ClassDataItem getInternedClassDataItem(DexFile dexFile, EncodedField[] staticFields,
+ EncodedField[] instanceFields, EncodedMethod[] directMethods,
+ EncodedMethod[] virtualMethods) {
+ ClassDataItem classDataItem = new ClassDataItem(dexFile, staticFields, instanceFields, directMethods,
+ virtualMethods);
+ return dexFile.ClassDataSection.intern(classDataItem);
+ }
+
+ /** {@inheritDoc} */
+ protected void readItem(Input in, ReadContext readContext) {
+ staticFields = new EncodedField[in.readUnsignedLeb128()];
+ instanceFields = new EncodedField[in.readUnsignedLeb128()];
+ directMethods = new EncodedMethod[in.readUnsignedLeb128()];
+ virtualMethods = new EncodedMethod[in.readUnsignedLeb128()];
+
+ EncodedField previousEncodedField = null;
+ for (int i=0; iClassDataItem
is associated with
+ * @param classDefItem the ClassDefItem
that this ClassDataItem
is associated with
+ */
+ protected void setParent(ClassDefItem classDefItem) {
+ this.parent = classDefItem;
+ }
+
+ /**
+ * @return the static fields for this class
+ */
+ public EncodedField[] getStaticFields() {
+ return staticFields;
+ }
+
+ /**
+ * @return the instance fields for this class
+ */
+ public EncodedField[] getInstanceFields() {
+ return instanceFields;
+ }
+
+ /**
+ * @return the direct methods for this class
+ */
+ public EncodedMethod[] getDirectMethods() {
+ return directMethods;
+ }
+
+ /**
+ * @return the virtual methods for this class
+ */
+ public EncodedMethod[] getVirtualMethods() {
+ return virtualMethods;
+ }
+
+ public static class EncodedField {
+ /**
+ * The FieldIdItem
that this EncodedField
is associated with
+ */
+ public final FieldIdItem field;
+
+ /**
+ * The access flags for this field
+ */
+ public final int accessFlags;
+
+ /**
+ * Constructs a new EncodedField
with the given values
+ * @param field The FieldIdItem
that this EncodedField
is associated with
+ * @param accessFlags The access flags for this field
+ */
+ public EncodedField(FieldIdItem field, int accessFlags) {
+ this.field = field;
+ this.accessFlags = accessFlags;
+ }
+
+ /**
+ * This is used internally to construct a new EncodedField
while reading in a DexFile
+ * @param dexFile The DexFile
that is being read in
+ * @param in the Input object to read the EncodedField
from
+ * @param previousEncodedField The previous EncodedField
in the list containing this
+ * EncodedField
.
+ */
+ private EncodedField(DexFile dexFile, Input in, EncodedField previousEncodedField) {
+ int previousIndex = previousEncodedField==null?0:previousEncodedField.field.getIndex();
+ field = dexFile.FieldIdsSection.getItemByIndex(in.readUnsignedLeb128() + previousIndex);
+ accessFlags = in.readUnsignedLeb128();
+ }
+
+ /**
+ * Writes the EncodedField
to the given AnnotatedOutput
object
+ * @param out the AnnotatedOutput
object to write to
+ * @param previousEncodedField The previous EncodedField
in the list containing this
+ * EncodedField
.
+ */
+ private void writeTo(AnnotatedOutput out, EncodedField previousEncodedField) {
+ int previousIndex = previousEncodedField==null?0:previousEncodedField.field.getIndex();
+
+ if (out.annotates()) {
+ out.annotate("field_idx_diff");
+ out.writeUnsignedLeb128(field.getIndex() - previousIndex);
+ out.annotate("access_flags");
+ out.writeUnsignedLeb128(accessFlags);
+ }else {
+ out.writeUnsignedLeb128(field.getIndex() - previousIndex);
+ out.writeUnsignedLeb128(accessFlags);
+ }
+ }
+
+ /**
+ * Calculates the size of this EncodedField
and returns the offset
+ * immediately following it
+ * @param offset the offset of this EncodedField
in the DexFile
+ * @param previousEncodedField The previous EncodedField
in the list containing this
+ * EncodedField
.
+ * @return the offset immediately following this EncodedField
+ */
+ private int place(int offset, EncodedField previousEncodedField) {
+ int previousIndex = previousEncodedField==null?0:previousEncodedField.field.getIndex();
+
+ offset += Leb128Utils.unsignedLeb128Size(field.getIndex() - previousIndex);
+ offset += Leb128Utils.unsignedLeb128Size(accessFlags);
+ return offset;
+ }
+
+ /**
+ * Compares this EncodedField
to another, based on the comparison of the associated
+ * FieldIdItem
+ * @param other The EncodedField
to compare against
+ * @return a standard integer comparison value indicating the relationship
+ */
+ public int compareTo(EncodedField other)
+ {
+ return field.compareTo(other.field);
+ }
+
+ /**
+ * @return true if this is a static field
+ */
+ public boolean isStatic() {
+ return (accessFlags & AccessFlags.STATIC.getValue()) != 0;
+ }
+ }
+
+ public static class EncodedMethod {
+ /**
+ * The MethodIdItem
that this EncodedMethod
is associated with
+ */
+ public final MethodIdItem method;
+
+ /**
+ * The access flags for this method
+ */
+ public final int accessFlags;
+
+ /**
+ * The CodeItem
containing the code for this method, or null if there is no code for this method
+ * (i.e. an abstract method)
+ */
+ public final CodeItem codeItem;
+
+ /**
+ * Constructs a new EncodedMethod
with the given values
+ * @param method The MethodIdItem
that this EncodedMethod
is associated with
+ * @param accessFlags The access flags for this method
+ * @param codeItem The CodeItem
containing the code for this method, or null if there is no code
+ * for this method (i.e. an abstract method)
+ */
+ public EncodedMethod(MethodIdItem method, int accessFlags, CodeItem codeItem) {
+ this.method = method;
+ this.accessFlags = accessFlags;
+ this.codeItem = codeItem;
+ if (codeItem != null) {
+ codeItem.setParent(method);
+ }
+ }
+
+ /**
+ * This is used internally to construct a new EncodedMethod
while reading in a DexFile
+ * @param dexFile The DexFile
that is being read in
+ * @param readContext a ReadContext
object to hold information that is only needed while reading
+ * in a file
+ * @param in the Input object to read the EncodedMethod
from
+ * @param previousEncodedMethod The previous EncodedMethod
in the list containing this
+ * EncodedMethod
.
+ */
+ public EncodedMethod(DexFile dexFile, ReadContext readContext, Input in, EncodedMethod previousEncodedMethod) {
+ int previousIndex = previousEncodedMethod==null?0:previousEncodedMethod.method.getIndex();
+ method = dexFile.MethodIdsSection.getItemByIndex(in.readUnsignedLeb128() + previousIndex);
+ accessFlags = in.readUnsignedLeb128();
+ codeItem = (CodeItem)readContext.getOffsettedItemByOffset(ItemType.TYPE_CODE_ITEM, in.readUnsignedLeb128());
+ if (codeItem != null) {
+ codeItem.setParent(method);
+ }
+ }
+
+ /**
+ * Writes the EncodedMethod
to the given AnnotatedOutput
object
+ * @param out the AnnotatedOutput
object to write to
+ * @param previousEncodedMethod The previous EncodedMethod
in the list containing this
+ * EncodedMethod
.
+ */
+ private void writeTo(AnnotatedOutput out, EncodedMethod previousEncodedMethod) {
+ int previousIndex = previousEncodedMethod==null?0:previousEncodedMethod.method.getIndex();
+
+ if (out.annotates()) {
+ out.annotate("method_idx_diff");
+ out.writeUnsignedLeb128(method.getIndex() - previousIndex);
+ out.annotate("access_flags");
+ out.writeUnsignedLeb128(accessFlags);
+ out.annotate("code_off");
+ out.writeUnsignedLeb128(codeItem==null?0:codeItem.getIndex());
+ }else {
+ out.writeUnsignedLeb128(method.getIndex() - previousIndex);
+ out.writeUnsignedLeb128(accessFlags);
+ out.writeUnsignedLeb128(codeItem==null?0:codeItem.getIndex());
+ }
+ }
+
+ /**
+ * Calculates the size of this EncodedMethod
and returns the offset
+ * immediately following it
+ * @param offset the offset of this EncodedMethod
in the DexFile
+ * @param previousEncodedMethod The previous EncodedMethod
in the list containing this
+ * EncodedMethod
.
+ * @return the offset immediately following this EncodedField
+ */
+ private int place(int offset, EncodedMethod previousEncodedMethod) {
+ int previousIndex = previousEncodedMethod==null?0:previousEncodedMethod.method.getIndex();
+
+ offset += Leb128Utils.unsignedLeb128Size(method.getIndex() - previousIndex);
+ offset += Leb128Utils.unsignedLeb128Size(accessFlags);
+ offset += codeItem==null?1:Leb128Utils.unsignedLeb128Size(codeItem.getIndex());
+ return offset;
+ }
+
+ /**
+ * Compares this EncodedMethod
to another, based on the comparison of the associated
+ * MethodIdItem
+ * @param other The EncodedMethod
to compare against
+ * @return a standard integer comparison value indicating the relationship
+ */
+ public int compareTo(EncodedMethod other) {
+ return method.compareTo(other.method);
+ }
+
+ /**
+ * @return true if this is a direct method
+ */
+ public boolean isDirect() {
+ return ((accessFlags & (AccessFlags.STATIC.getValue() | AccessFlags.PRIVATE.getValue() |
+ AccessFlags.CONSTRUCTOR.getValue())) != 0);
+ }
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/ClassDefItem.java b/dexlib/src/main/java/org/jf/dexlib/ClassDefItem.java
new file mode 100644
index 00000000..77930bb4
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/ClassDefItem.java
@@ -0,0 +1,286 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2009 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib;
+
+import org.jf.dexlib.Util.Input;
+import org.jf.dexlib.Util.AnnotatedOutput;
+
+import java.util.*;
+
+public class ClassDefItem extends ItemClassDefItem
+ * @param dexFile The DexFile
that this item belongs to
+ */
+ protected ClassDefItem(DexFile dexFile) {
+ super(dexFile);
+ }
+
+ /**
+ * Creates a new ClassDefItem
with the given values
+ * @param dexFile The DexFile
that this item belongs to
+ * @param classType The type of this class
+ * @param accessFlags The access flags of this class
+ * @param superType The superclass of this class, or null if none (only valid for java.lang.Object)
+ * @param implementedInterfaces A list of the interfaces that this class implements, or null if none
+ * @param sourceFile The main source file that this class is defined in, or null if not available
+ * @param annotations The annotations for this class and its fields, methods and method parameters, or null if none
+ * @param classData The ClassDataItem
containing the method and field definitions for this class
+ * @param staticFieldInitializers The initial values for this class's static fields, or null if none
+ */
+ private ClassDefItem(DexFile dexFile, TypeIdItem classType, int accessFlags, TypeIdItem superType,
+ TypeListItem implementedInterfaces, StringIdItem sourceFile,
+ AnnotationDirectoryItem annotations, ClassDataItem classData,
+ EncodedArrayItem staticFieldInitializers) {
+ super(dexFile);
+ this.classType = classType;
+ this.accessFlags = accessFlags;
+ this.superType = superType;
+ this.implementedInterfaces = implementedInterfaces;
+ this.sourceFile = sourceFile;
+ this.annotations = annotations;
+ this.classData = classData;
+ this.staticFieldInitializers = staticFieldInitializers;
+
+ if (classData != null) {
+ classData.setParent(this);
+ }
+ if (annotations != null) {
+ annotations.setParent(this);
+ }
+ }
+
+ /**
+ * Returns a ClassDefItem
for the given values, and that has been interned into the given
+ * DexFile
+ * @param dexFile The DexFile
that this item belongs to
+ * @param classType The type of this class
+ * @param accessFlags The access flags of this class
+ * @param superType The superclass of this class, or null if none (only valid for java.lang.Object)
+ * @param implementedInterfaces A list of the interfaces that this class implements, or null if none
+ * @param sourceFile The main source file that this class is defined in, or null if not available
+ * @param annotations The annotations for this class and its fields, methods and method parameters, or null if none
+ * @param classData The ClassDataItem
containing the method and field definitions for this class
+ * @param staticFieldInitializers The initial values for this class's static fields, or null if none
+ * @return a ClassDefItem
for the given values, and that has been interned into the given
+ * DexFile
+ */
+ public static ClassDefItem getInternedClassDefItem(DexFile dexFile, TypeIdItem classType, int accessFlags,
+ TypeIdItem superType, TypeListItem implementedInterfaces, StringIdItem sourceFile,
+ AnnotationDirectoryItem annotations, ClassDataItem classData,
+ EncodedArrayItem staticFieldInitializers) {
+ ClassDefItem classDefItem = new ClassDefItem(dexFile, classType, accessFlags, superType, implementedInterfaces,
+ sourceFile, annotations, classData, staticFieldInitializers);
+ return dexFile.ClassDefsSection.intern(classDefItem);
+ }
+
+ /** {@inheritDoc} */
+ protected void readItem(Input in, ReadContext readContext) {
+ classType = dexFile.TypeIdsSection.getItemByIndex(in.readInt());
+ accessFlags = in.readInt();
+ superType = dexFile.TypeIdsSection.getItemByIndex(in.readInt());
+ implementedInterfaces = (TypeListItem)readContext.getOffsettedItemByOffset(ItemType.TYPE_TYPE_LIST,
+ in.readInt());
+ sourceFile = dexFile.StringIdsSection.getItemByIndex(in.readInt());
+ annotations = (AnnotationDirectoryItem)readContext.getOffsettedItemByOffset(
+ ItemType.TYPE_ANNOTATIONS_DIRECTORY_ITEM, in.readInt());
+ classData = (ClassDataItem)readContext.getOffsettedItemByOffset(ItemType.TYPE_CLASS_DATA_ITEM, in.readInt());
+ staticFieldInitializers = (EncodedArrayItem)readContext.getOffsettedItemByOffset(
+ ItemType.TYPE_ENCODED_ARRAY_ITEM, in.readInt());
+
+ if (classData != null) {
+ classData.setParent(this);
+ }
+ if (annotations != null) {
+ annotations.setParent(this);
+ }
+ }
+
+ /** {@inheritDoc} */
+ protected int placeItem(int offset) {
+ return offset + 32;
+ }
+
+ /** {@inheritDoc} */
+ protected void writeItem(AnnotatedOutput out) {
+ if (out.annotates()) {
+ out.annotate(4, "class_idx");
+ out.annotate(4, "access_flags");
+ out.annotate(4, "superclass_idx");
+ out.annotate(4, "interfaces_off");
+ out.annotate(4, "source_file_idx");
+ out.annotate(4, "annotations_off");
+ out.annotate(4, "class_data_off");
+ out.annotate(4, "static_values_off");
+ }
+ out.writeInt(classType.getIndex());
+ out.writeInt(accessFlags);
+ out.writeInt(superType==null?-1:superType.getIndex());
+ out.writeInt(implementedInterfaces==null?0:implementedInterfaces.getOffset());
+ out.writeInt(sourceFile==null?-1:sourceFile.getIndex());
+ out.writeInt(annotations==null?0:annotations.getOffset());
+ out.writeInt(classData==null?0:classData.getOffset());
+ out.writeInt(staticFieldInitializers==null?0:staticFieldInitializers.getOffset());
+ }
+
+ /** {@inheritDoc} */
+ public ItemType getItemType() {
+ return ItemType.TYPE_CLASS_DEF_ITEM;
+ }
+
+ /** {@inheritDoc} */
+ public String getConciseIdentity() {
+ return "class_def_item: " + classType.getTypeDescriptor();
+ }
+
+ /** {@inheritDoc} */
+ public int compareTo(ClassDefItem o) {
+ //The actual sorting for this class is implemented in SortClassDefItemSection.
+ //This method is just used for sorting the associated ClassDataItem items, so
+ //we can just do the comparison based on the offsets of the items
+ return this.getOffset() - o.getOffset();
+ }
+
+ public TypeIdItem getClassType() {
+ return classType;
+ }
+
+ public int getAccessFlags() {
+ return accessFlags;
+ }
+
+ public TypeIdItem getSuperclass() {
+ return superType;
+ }
+
+ public TypeListItem getInterfaces() {
+ return implementedInterfaces;
+ }
+
+ public StringIdItem getSourceFile() {
+ return sourceFile;
+ }
+
+ public AnnotationDirectoryItem getAnnotations() {
+ return annotations;
+ }
+
+ public ClassDataItem getClassData() {
+ return classData;
+ }
+
+ public EncodedArrayItem getStaticFieldInitializers() {
+ return staticFieldInitializers;
+ }
+
+ public static int placeClassDefItems(IndexedSectionInstructionIterator
calls this method when a "normal" instruction is encountered. I.e.
+ * not a special or reference instruction
+ * @param opcode the opcode of the instruction that was encountered
+ * @param index the start index of the instruction in the byte array that the
+ * InstructionIterator
is iterating
+ */
+ public void ProcessNormalInstruction(Opcode opcode, int index);
+
+ /**
+ * The InstructionIterator
calls this method when a "reference" instruction is encountered.
+ * I.e. an instruction that contains an index that is a reference to a string, method, type or field.
+ * @param opcode the opcode of the instruction that was encountered
+ * @param index the start index of the instruction in the byte array that the
+ * InstructionIterator
is iterating
+ */
+ public void ProcessReferenceInstruction(Opcode opcode, int index);
+
+ /**
+ * The InstructionIterator
calls this method when a packed switch instruction is encountered.
+ * @param index the start index of the instruction in the byte array that the
+ * InstructionIterator
is iterating
+ * @param targetCount the number of targets that this packed switch structure contains
+ * @param instructionLength the length of this instruction in bytes
+ */
+ public void ProcessPackedSwitchInstruction(int index, int targetCount, int instructionLength);
+
+ /**
+ * The InstructionIterator
calls this method when a sparse switch instruction is encountered.
+ * @param index the start index of the instruction in the byte array that the
+ * InstructionIterator
is iterating
+ * @param targetCount the number of targets that this sparse switch structure contains
+ * @param instructionLength the length of this instruction in bytes
+ */
+ public void ProcessSparseSwitchInstruction(int index, int targetCount, int instructionLength);
+
+ /**
+ * The InstructionIterator
calls this method when a fill-array-data instruction is encountered.
+ * @param index the start index of the instruction in the byte array that the
+ * InstructionIterator
is iterating
+ * @param elementWidth the width of the elements contained in this fill-array-data structure
+ * @param elementCount the number of elements contained in this fill-array-data structure
+ * @param instructionLength the length of this instruction in bytes
+ */
+ public void ProcessFillArrayDataInstruction(int index, int elementWidth, int elementCount,
+ int instructionLength);
+ }
+
+ public static interface ProcessInstructionDelegate {
+ public void ProcessInstruction(int index, Instruction instruction);
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/InstructionReader.java b/dexlib/src/main/java/org/jf/dexlib/Code/InstructionReader.java
new file mode 100644
index 00000000..1072825c
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/InstructionReader.java
@@ -0,0 +1,96 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2009 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code;
+
+import org.jf.dexlib.Item;
+import org.jf.dexlib.DexFile;
+import org.jf.dexlib.Util.NumberUtils;
+
+import java.util.LinkedList;
+
+public class InstructionReader {
+ /**
+ * Decodes the instructions in the given byte array, and builds a list of the items referenced by instructions,
+ * using the given DexFile
to resolve the item references
+ * @param insns a byte array containing encoded instructions that have just been read in
+ * @param dexFile the DexFile
used to resolve item references
+ * @return an array of the referenced Item
objects, in the same order as their occurance in the
+ * byte array
+ */
+ public static Item[] getReferencedItems(final byte[] insns, final DexFile dexFile) {
+ final LinkedListCodeItem
+ * @param dexFile The DexFile
that this item belongs to
+ */
+ public CodeItem(DexFile dexFile) {
+ super(dexFile);
+ }
+
+ /**
+ * Creates a new CodeItem
with the given values.
+ * @param dexFile The DexFile
that this item belongs to
+ * @param registerCount the number of registers that the method containing this code uses
+ * @param inWords the number of 2-byte words that the parameters to the method containing this code take
+ * @param outWords the maximum number of 2-byte words for the arguments of any method call in this code
+ * @param debugInfo the debug information for this code/method
+ * @param encodedInstructions the instructions, encoded as a byte array
+ * @param referencedItems an array of the items referenced by instructions, in order of occurance in the code
+ * @param tries an array of the tries defined for this code/method
+ * @param encodedCatchHandlers an array of the exception handlers defined for this code/method
+ */
+ private CodeItem(DexFile dexFile,
+ int registerCount,
+ int inWords,
+ int outWords,
+ DebugInfoItem debugInfo,
+ byte[] encodedInstructions,
+ Item[] referencedItems,
+ TryItem[] tries,
+ EncodedCatchHandler[] encodedCatchHandlers) {
+ super(dexFile);
+
+ this.registerCount = registerCount;
+ this.inWords = inWords;
+ this.outWords = outWords;
+ this.debugInfo = debugInfo;
+ if (debugInfo != null) {
+ debugInfo.setParent(this);
+ }
+ this.encodedInstructions = encodedInstructions;
+ this.referencedItems = referencedItems;
+ this.tries = tries;
+ this.encodedCatchHandlers = encodedCatchHandlers;
+ }
+
+ /**
+ * Returns a new CodeItem
with the given values.
+ * @param dexFile The DexFile
that this item belongs to
+ * @param registerCount the number of registers that the method containing this code uses
+ * @param inWords the number of 2-byte words that the parameters to the method containing this code take
+ * @param outWords the maximum number of 2-byte words for the arguments of any method call in this code
+ * @param debugInfo the debug information for this code/method
+ * @param encodedInstructions the instructions, encoded as a byte array
+ * @param referencedItems an array of the items referenced by instructions, in order of occurance in the code
+ * @param tries an array of the tries defined for this code/method
+ * @param encodedCatchHandlers an array of the exception handlers defined for this code/method
+ * @return a new CodeItem
with the given values.
+ */
+ public static CodeItem getInternedCodeItem(DexFile dexFile,
+ int registerCount,
+ int inWords,
+ int outWords,
+ DebugInfoItem debugInfo,
+ byte[] encodedInstructions,
+ Item[] referencedItems,
+ TryItem[] tries,
+ EncodedCatchHandler[] encodedCatchHandlers) {
+ CodeItem codeItem = new CodeItem(dexFile, registerCount, inWords, outWords, debugInfo, encodedInstructions,
+ referencedItems, tries, encodedCatchHandlers);
+ return dexFile.CodeItemsSection.intern(codeItem);
+ }
+
+ /** {@inheritDoc} */
+ protected void readItem(Input in, ReadContext readContext) {
+ this.registerCount = in.readShort();
+ this.inWords = in.readShort();
+ this.outWords = in.readShort();
+ int triesCount = in.readShort();
+ this.debugInfo = (DebugInfoItem)readContext.getOffsettedItemByOffset(ItemType.TYPE_DEBUG_INFO_ITEM,
+ in.readInt());
+ if (this.debugInfo != null) {
+ this.debugInfo.setParent(this);
+ }
+ int instructionCount = in.readInt();
+ this.encodedInstructions = in.readBytes(instructionCount * 2);
+ this.referencedItems = InstructionReader.getReferencedItems(encodedInstructions, dexFile);
+ if (triesCount > 0) {
+ in.alignTo(4);
+
+ //we need to read in the catch handlers first, so save the offset to the try items for future reference
+ int triesOffset = in.getCursor();
+ in.setCursor(triesOffset + 8 * triesCount);
+
+ //read in the encoded catch handlers
+ int encodedHandlerStart = in.getCursor();
+ int handlerCount = in.readUnsignedLeb128();
+ SparseArrayTryItem
objects in this CodeItem
+ */
+ public TryItem[] getTries() {
+ return tries;
+ }
+
+ /**
+ * @return the DebugInfoItem
associated with this CodeItem
+ */
+ public DebugInfoItem getDebugInfo() {
+ return debugInfo;
+ }
+
+ /**
+ * Sets the MethodIdItem
of the method that this CodeItem
is associated with
+ * @param methodIdItem the MethodIdItem
of the method that this CodeItem
is associated
+ * with
+ */
+ protected void setParent(MethodIdItem methodIdItem) {
+ this.parent = methodIdItem;
+ }
+
+ public static class TryItem {
+ /**
+ * The address (in 2-byte words) within the code where the try block starts
+ */
+ public final int startAddress;
+
+ /**
+ * The number of 2-byte words that the try block covers
+ */
+ public final int instructionCount;
+
+ /**
+ * The associated exception handler
+ */
+ public final EncodedCatchHandler encodedCatchHandler;
+
+ /**
+ * Construct a new TryItem
with the given values
+ * @param startAddress the address (in 2-byte words) within the code where the try block starts
+ * @param instructionCount the number of 2-byte words that the try block covers
+ * @param encodedCatchHandler the associated exception handler
+ */
+ public TryItem(int startAddress, int instructionCount, EncodedCatchHandler encodedCatchHandler) {
+ this.startAddress = startAddress;
+ this.instructionCount = instructionCount;
+ this.encodedCatchHandler = encodedCatchHandler;
+ }
+
+ /**
+ * This is used internally to construct a new TryItem
while reading in a DexFile
+ * @param in the Input object to read the TryItem
from
+ * @param encodedCatchHandlers a SparseArray of the EncodedCatchHandlers for this CodeItem
. The
+ * key should be the offset of the EncodedCatchHandler from the beginning of the encoded_catch_handler_list
+ * structure.
+ */
+ private TryItem(Input in, SparseArrayTryItem
to the given AnnotatedOutput
object
+ * @param out the AnnotatedOutput
object to write to
+ */
+ private void writeTo(AnnotatedOutput out) {
+ if (out.annotates()) {
+ out.annotate(4, "start_addr");
+ out.annotate(2, "insn_count");
+ out.annotate(2, "handler_off");
+ }
+
+ out.writeInt(startAddress);
+ out.writeShort(instructionCount);
+ out.writeShort(encodedCatchHandler.getOffsetInList());
+ }
+ }
+
+ public static class EncodedCatchHandler {
+ /**
+ * An array of the individual exception handlers
+ */
+ public final EncodedTypeAddrPair[] handlers;
+
+ /**
+ * The address within the code (in 2-byte words) for the catch all handler, or -1 if there is no catch all
+ * handler
+ */
+ public final int catchAllHandlerAddress;
+
+ //TODO: would it be possible to get away without having these? and generate/create these values while writing?
+ private int baseOffset;
+ private int offset;
+
+ /**
+ * Constructs a new EncodedCatchHandler
with the given values
+ * @param handlers an array of the individual exception handlers
+ * @param catchAllHandlerAddress The address within the code (in 2-byte words) for the catch all handler, or -1
+ * if there is no catch all handler
+ */
+ public EncodedCatchHandler(EncodedTypeAddrPair[] handlers, int catchAllHandlerAddress) {
+ this.handlers = handlers;
+ this.catchAllHandlerAddress = catchAllHandlerAddress;
+ }
+
+ /**
+ * This is used internally to construct a new EncodedCatchHandler
while reading in a
+ * DexFile
+ * @param dexFile the DexFile
that is being read in
+ * @param in the Input object to read the EncodedCatchHandler
from
+ */
+ private EncodedCatchHandler(DexFile dexFile, Input in) {
+ int handlerCount = in.readSignedLeb128();
+
+ if (handlerCount < 0) {
+ handlers = new EncodedTypeAddrPair[-1 * handlerCount];
+ } else {
+ handlers = new EncodedTypeAddrPair[handlerCount];
+ }
+
+ for (int i=0; iEncodedCatchHandler
, storing the offset and baseOffset, and returning the offset
+ * immediately following this EncodedCatchHandler
+ * @param offset the offset of this EncodedCatchHandler
in the DexFile
+ * @param baseOffset the offset of the beginning of the encoded_catch_handler_list structure in the
+ * DexFile
+ * @return the offset immediately following this EncodedCatchHandler
+ */
+ private int place(int offset, int baseOffset) {
+ this.offset = offset;
+ this.baseOffset = baseOffset;
+
+ int size = handlers.length;
+ if (catchAllHandlerAddress > -1) {
+ size *= -1;
+ offset += Leb128Utils.unsignedLeb128Size(catchAllHandlerAddress);
+ }
+ offset += Leb128Utils.signedLeb128Size(size);
+
+ for (EncodedTypeAddrPair handler: handlers) {
+ offset += handler.getSize();
+ }
+ return offset;
+ }
+
+ /**
+ * Writes the EncodedCatchHandler
to the given AnnotatedOutput
object
+ * @param out the AnnotatedOutput
object to write to
+ */
+ private void writeTo(AnnotatedOutput out) {
+ if (out.annotates()) {
+ out.annotate("size");
+
+ int size = handlers.length;
+ if (catchAllHandlerAddress < 0) {
+ size = size * -1;
+ }
+ out.writeSignedLeb128(size);
+
+ for (EncodedTypeAddrPair handler: handlers) {
+ handler.writeTo(out);
+ }
+
+ if (catchAllHandlerAddress > -1) {
+ out.annotate("catch_all_addr");
+ out.writeUnsignedLeb128(catchAllHandlerAddress);
+ }
+ } else {
+ int size = handlers.length;
+ if (catchAllHandlerAddress < 0) {
+ size = size * -1;
+ }
+ out.writeSignedLeb128(size);
+
+ for (EncodedTypeAddrPair handler: handlers) {
+ handler.writeTo(out);
+ }
+
+ if (catchAllHandlerAddress > -1) {
+ out.writeUnsignedLeb128(catchAllHandlerAddress);
+ }
+ }
+ }
+ }
+
+ public static class EncodedTypeAddrPair {
+ /**
+ * The type of the Exception
that this handler handles
+ */
+ public final TypeIdItem exceptionType;
+
+ /**
+ * The address (in 2-byte words) in the code of the handler
+ */
+ public final int handlerAddress;
+
+ /**
+ * Constructs a new EncodedTypeAddrPair
with the given values
+ * @param exceptionType the type of the Exception
that this handler handles
+ * @param handlerAddress the address (in 2-byte words) in the code of the handler
+ */
+ public EncodedTypeAddrPair(TypeIdItem exceptionType, int handlerAddress) {
+ this.exceptionType = exceptionType;
+ this.handlerAddress = handlerAddress;
+ }
+
+ /**
+ * This is used internally to construct a new EncodedTypeAddrPair
while reading in a
+ * DexFile
+ * @param dexFile the DexFile
that is being read in
+ * @param in the Input object to read the EncodedCatchHandler
from
+ */
+ private EncodedTypeAddrPair(DexFile dexFile, Input in) {
+ exceptionType = dexFile.TypeIdsSection.getItemByIndex(in.readUnsignedLeb128());
+ handlerAddress = in.readUnsignedLeb128();
+ }
+
+ /**
+ * @return the size of this EncodedTypeAddrPair
+ */
+ private int getSize() {
+ return Leb128Utils.unsignedLeb128Size(exceptionType.getIndex()) +
+ Leb128Utils.unsignedLeb128Size(handlerAddress);
+ }
+
+ /**
+ * Writes the EncodedTypeAddrPair
to the given AnnotatedOutput
object
+ * @param out the AnnotatedOutput
object to write to
+ */
+ private void writeTo(AnnotatedOutput out) {
+ if (out.annotates()) {
+ out.annotate("type_idx");
+ out.writeUnsignedLeb128(exceptionType.getIndex());
+
+ out.annotate("addr");
+ out.writeUnsignedLeb128(handlerAddress);
+ } else {
+ out.writeUnsignedLeb128(exceptionType.getIndex());
+ out.writeUnsignedLeb128(handlerAddress);
+ }
+ }
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Debug/DebugInstructionIterator.java b/dexlib/src/main/java/org/jf/dexlib/Debug/DebugInstructionIterator.java
new file mode 100644
index 00000000..fe75bddb
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Debug/DebugInstructionIterator.java
@@ -0,0 +1,336 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2009 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Debug;
+
+import org.jf.dexlib.Util.Input;
+import org.jf.dexlib.Util.ByteArrayInput;
+import org.jf.dexlib.TypeIdItem;
+import org.jf.dexlib.StringIdItem;
+import org.jf.dexlib.DexFile;
+import org.jf.dexlib.DebugInfoItem;
+
+public class DebugInstructionIterator {
+ /**
+ * This method decodes the debug instructions in the given byte array and iterates over them, calling
+ * the ProcessDebugInstructionDelegate.ProcessDebugInstruction method for each instruction
+ * @param in an Input object that the debug instructions can be read from
+ * @param processDebugInstruction a ProcessDebugInstructionDelegate
object that gets called
+ * for each instruction that is encountered
+ */
+ public static void IterateInstructions(Input in, ProcessRawDebugInstructionDelegate processDebugInstruction) {
+ int startOffset;
+
+ while(true)
+ {
+ startOffset = in.getCursor();
+ byte debugOpcode = in.readByte();
+
+ switch (debugOpcode) {
+ case 0x00:
+ {
+ processDebugInstruction.ProcessEndSequence(startOffset);
+ return;
+ }
+ case 0x01:
+ {
+ int addressDiff = in.readUnsignedLeb128();
+ processDebugInstruction.ProcessAdvancePC(startOffset, in.getCursor() - startOffset, addressDiff);
+ break;
+ }
+ case 0x02:
+ {
+ int lineDiff = in.readSignedLeb128();
+ processDebugInstruction.ProcessAdvanceLine(startOffset, in.getCursor() - startOffset, lineDiff);
+ break;
+ }
+ case 0x03:
+ {
+ int registerNum = in.readUnsignedLeb128();
+ int nameIndex = in.readUnsignedLeb128() - 1;
+ int typeIndex = in.readUnsignedLeb128() - 1;
+ processDebugInstruction.ProcessStartLocal(startOffset, in.getCursor() - startOffset, registerNum,
+ nameIndex, typeIndex);
+ break;
+ }
+ case 0x04:
+ {
+ int registerNum = in.readUnsignedLeb128();
+ int nameIndex = in.readUnsignedLeb128() - 1;
+ int typeIndex = in.readUnsignedLeb128() - 1;
+ int signatureIndex = in.readUnsignedLeb128() - 1;
+ processDebugInstruction.ProcessStartLocalExtended(startOffset, in.getCursor() - startOffset,
+ registerNum, nameIndex, typeIndex, signatureIndex);
+ break;
+ }
+ case 0x05:
+ {
+ int registerNum = in.readUnsignedLeb128();
+ processDebugInstruction.ProcessEndLocal(startOffset, in.getCursor() - startOffset, registerNum);
+ break;
+ }
+ case 0x06:
+ {
+ int registerNum = in.readUnsignedLeb128();
+ processDebugInstruction.ProcessRestartLocal(startOffset, in.getCursor() - startOffset, registerNum);
+ break;
+ }
+ case 0x07:
+ {
+ processDebugInstruction.ProcessSetPrologueEnd(startOffset);
+ break;
+ }
+ case 0x08:
+ {
+ processDebugInstruction.ProcessSetEpilogueBegin(startOffset);
+ break;
+ }
+ case 0x09:
+ {
+ int nameIndex = in.readUnsignedLeb128();
+ processDebugInstruction.ProcessSetFile(startOffset, in.getCursor() - startOffset, nameIndex);
+ break;
+ }
+ default:
+ {
+ byte base = (byte)((debugOpcode & 0xFF) - 0x0A);
+ processDebugInstruction.ProcessSpecialOpcode(startOffset, debugOpcode, (base % 15) - 4, base / 15);
+ }
+ }
+ }
+ }
+
+ /**
+ * This method decodes the debug instructions in the given byte array and iterates over them, calling
+ * the ProcessDebugInstructionDelegate.ProcessDebugInstruction method for each instruction
+ * @param debugInfoItem the DebugInfoItem
to iterate over
+ * @param registerCount the number of registers in the method that the given debug info is for
+ * @param processDecodedDebugInstruction a ProcessDebugInstructionDelegate
object that gets called
+ * for each instruction that is encountered
+ */
+ public static void DecodeInstructions(DebugInfoItem debugInfoItem, int registerCount,
+ ProcessDecodedDebugInstructionDelegate processDecodedDebugInstruction) {
+ int startOffset;
+ int address = 0;
+ int line = debugInfoItem.getLineStart();
+ Input in = new ByteArrayInput(debugInfoItem.getEncodedDebugInfo());
+ DexFile dexFile = debugInfoItem.getDexFile();
+
+ Local[] locals = new Local[registerCount];
+
+ while(true)
+ {
+ startOffset = in.getCursor();
+ byte debugOpcode = in.readByte();
+
+ switch (debugOpcode) {
+ case 0x00:
+ {
+ return;
+ }
+ case 0x01:
+ {
+ int addressDiff = in.readUnsignedLeb128();
+ address += addressDiff;
+ break;
+ }
+ case 0x02:
+ {
+ int lineDiff = in.readSignedLeb128();
+ line += lineDiff;
+ break;
+ }
+ case 0x03:
+ {
+ int registerNum = in.readUnsignedLeb128();
+ StringIdItem name = dexFile.StringIdsSection.getItemByIndex(in.readUnsignedLeb128() - 1);
+ TypeIdItem type = dexFile.TypeIdsSection.getItemByIndex(in.readUnsignedLeb128() - 1);
+ locals[registerNum] = new Local(registerNum, name, type, null);
+ processDecodedDebugInstruction.ProcessStartLocal(startOffset, in.getCursor() - startOffset, registerNum,
+ name, type);
+ break;
+ }
+ case 0x04:
+ {
+ int registerNum = in.readUnsignedLeb128();
+ StringIdItem name = dexFile.StringIdsSection.getItemByIndex(in.readUnsignedLeb128() - 1);
+ TypeIdItem type = dexFile.TypeIdsSection.getItemByIndex(in.readUnsignedLeb128() - 1);
+ StringIdItem signature = dexFile.StringIdsSection.getItemByIndex(in.readUnsignedLeb128() - 1);
+ locals[registerNum] = new Local(registerNum, name, type, signature);
+ processDecodedDebugInstruction.ProcessStartLocalExtended(startOffset, in.getCursor() - startOffset,
+ registerNum, name, type, signature);
+ break;
+ }
+ case 0x05:
+ {
+ int registerNum = in.readUnsignedLeb128();
+ Local local = locals[registerNum];
+ if (local == null) {
+ processDecodedDebugInstruction.ProcessEndLocal(startOffset, in.getCursor() - startOffset, registerNum,
+ null, null, null);
+ } else {
+ processDecodedDebugInstruction.ProcessEndLocal(startOffset, in.getCursor() - startOffset, registerNum,
+ local.name, local.type, local.signature);
+ }
+ break;
+ }
+ case 0x06:
+ {
+ int registerNum = in.readUnsignedLeb128();
+ Local local = locals[registerNum];
+ if (local == null) {
+ processDecodedDebugInstruction.ProcessRestartLocal(startOffset, in.getCursor() - startOffset,
+ registerNum, null, null, null);
+ } else {
+ processDecodedDebugInstruction.ProcessRestartLocal(startOffset, in.getCursor() - startOffset,
+ registerNum, local.name, local.type, local.signature);
+ }
+
+ break;
+ }
+ case 0x07:
+ {
+ processDecodedDebugInstruction.ProcessSetPrologueEnd(startOffset);
+ break;
+ }
+ case 0x08:
+ {
+ processDecodedDebugInstruction.ProcessSetEpilogueBegin(startOffset);
+ break;
+ }
+ case 0x09:
+ {
+ StringIdItem name = dexFile.StringIdsSection.getItemByIndex(in.readUnsignedLeb128() - 1);
+ processDecodedDebugInstruction.ProcessSetFile(startOffset, in.getCursor() - startOffset, name);
+ break;
+ }
+ default:
+ {
+ byte base = (byte)((debugOpcode & 0xFF) - 0x0A);
+ address += base / 15;
+ line += (base % 15) - 4;
+ processDecodedDebugInstruction.ProcessLineEmit(address, line);
+ }
+ }
+ }
+ }
+
+ public static class ProcessRawDebugInstructionDelegate
+ {
+ //TODO: add javadocs
+ public void ProcessEndSequence(int startOffset) {
+ ProcessStaticOpcode(startOffset, 1);
+ }
+
+ public void ProcessAdvancePC(int startOffset, int length, int addressDiff) {
+ ProcessStaticOpcode(startOffset, length);
+ }
+
+ public void ProcessAdvanceLine(int startOffset, int length, int lineDiff) {
+ ProcessStaticOpcode(startOffset, length);
+ }
+
+ public void ProcessStartLocal(int startOffset, int length, int registerNum, int nameIndex, int typeIndex) {
+ }
+
+ public void ProcessStartLocalExtended(int startOffset, int length, int registerNum, int nameIndex,
+ int typeIndex,int signatureIndex) {
+ }
+
+ public void ProcessEndLocal(int startOffset, int length, int registerNum) {
+ ProcessStaticOpcode(startOffset, length);
+ }
+
+ public void ProcessRestartLocal(int startOffset, int length, int registerNum) {
+ ProcessStaticOpcode(startOffset, length);
+ }
+
+ public void ProcessSetPrologueEnd(int startOffset) {
+ ProcessStaticOpcode(startOffset, 1);
+ }
+
+ public void ProcessSetEpilogueBegin(int startOffset) {
+ ProcessStaticOpcode(startOffset, 1);
+ }
+
+ public void ProcessSetFile(int startOffset, int length, int nameIndex) {
+ }
+
+ public void ProcessSpecialOpcode(int startOffset, int debugOpcode, int lineDiff, int addressDiff) {
+ ProcessStaticOpcode(startOffset, 1);
+ }
+
+ public void ProcessStaticOpcode(int startOffset, int length) {
+ }
+ }
+
+ public static class ProcessDecodedDebugInstructionDelegate
+ {
+ public void ProcessStartLocal(int codeAddress, int length, int registerNum, StringIdItem name,
+ TypeIdItem type) {
+ }
+
+ public void ProcessStartLocalExtended(int codeAddress, int length, int registerNum, StringIdItem name,
+ TypeIdItem type, StringIdItem signature) {
+ }
+
+ public void ProcessEndLocal(int codeAddress, int length, int registerNum, StringIdItem name, TypeIdItem type,
+ StringIdItem signature) {
+ }
+
+ public void ProcessRestartLocal(int codeAddress, int length, int registerNum, StringIdItem name,
+ TypeIdItem type, StringIdItem signature) {
+ }
+
+ public void ProcessSetPrologueEnd(int codeAddress) {
+ }
+
+ public void ProcessSetEpilogueBegin(int codeAddress) {
+ }
+
+ public void ProcessSetFile(int codeAddress, int length, StringIdItem name) {
+ }
+
+ public void ProcessLineEmit(int codeAddress, int line) {
+ }
+ }
+
+ private static class Local {
+ public final int register;
+ public final StringIdItem name;
+ public final TypeIdItem type;
+ public final StringIdItem signature;
+ public Local(int register, StringIdItem name, TypeIdItem type, StringIdItem signature) {
+ this.register = register;
+ this.name = name;
+ this.type = type;
+ this.signature = signature;
+ }
+
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Debug/DebugOpcode.java b/dexlib/src/main/java/org/jf/dexlib/Debug/DebugOpcode.java
new file mode 100644
index 00000000..c11e7f9b
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Debug/DebugOpcode.java
@@ -0,0 +1,64 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2009 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Debug;
+
+public enum DebugOpcode {
+ DBG_END_SEQUENCE((byte)0x00),
+ DBG_ADVANCE_PC((byte)0x01),
+ DBG_ADVANCE_LINE((byte)0x02),
+ DBG_START_LOCAL((byte)0x03),
+ DBG_START_LOCAL_EXTENDED((byte)0x04),
+ DBG_END_LOCAL((byte)0x05),
+ DBG_RESTART_LOCAL((byte)0x06),
+ DBG_SET_PROLOGUE_END((byte)0x07),
+ DBG_SET_EPILOGUE_END((byte)0x08),
+ DBG_SET_FILE((byte)0x09),
+ DBG_SPECIAL_OPCODE((byte)0x0A);
+
+ private static DebugOpcode[] opcodesByValue;
+
+ static {
+ opcodesByValue = new DebugOpcode[11];
+
+ for (DebugOpcode debugOpcode: DebugOpcode.values()) {
+ opcodesByValue[debugOpcode.value & 0xFF] = debugOpcode;
+ }
+ }
+
+ public static DebugOpcode getDebugOpcodeByValue(byte debugOpcodeValue) {
+ debugOpcodeValue = (byte)Math.min(debugOpcodeValue, 0x0A);
+ return opcodesByValue[debugOpcodeValue];
+ }
+
+ public final byte value;
+
+ DebugOpcode(byte value) {
+ this.value = value;
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/DebugInfoItem.java b/dexlib/src/main/java/org/jf/dexlib/DebugInfoItem.java
new file mode 100644
index 00000000..a0e86886
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/DebugInfoItem.java
@@ -0,0 +1,379 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2009 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib;
+
+import org.jf.dexlib.Debug.DebugInstructionIterator;
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.Input;
+import org.jf.dexlib.Util.Leb128Utils;
+import org.jf.dexlib.Util.ByteArrayInput;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class DebugInfoItem extends ItemDebugInfoInfo
+ * @param dexFile The DexFile
that this item belongs to
+ */
+ public DebugInfoItem(DexFile dexFile) {
+ super(dexFile);
+ }
+
+ /**
+ * Creates a new DebugInfoItem
with the given values
+ * @param dexFile The DexFile
that this item belongs to
+ * @param lineStart the initial value for the line number register for the debug info machine
+ * @param parameterNames an array of the names of the associated method's parameters. The entire parameter
+ * can be null if no parameter info is available, or any element can be null to indicate no info for that parameter
+ * @param encodedDebugInfo the debug info, encoded as a byte array
+ * @param referencedItems an array of the items referenced by instructions, in order of occurance in the encoded
+ * debug info
+ */
+ private DebugInfoItem(DexFile dexFile,
+ int lineStart,
+ StringIdItem[] parameterNames,
+ byte[] encodedDebugInfo,
+ Item[] referencedItems) {
+ super(dexFile);
+ this.lineStart = lineStart;
+ this.parameterNames = parameterNames;
+ this.encodedDebugInfo = encodedDebugInfo;
+ this.referencedItems = referencedItems;
+ }
+
+ /**
+ * Returns a new DebugInfoItem
with the given values
+ * @param dexFile The DexFile
that this item belongs to
+ * @param lineStart the initial value for the line number register for the debug info machine
+ * @param parameterNames an array of the names of the associated method's parameters. The entire parameter
+ * can be null if no parameter info is available, or any element can be null to indicate no info for that parameter
+ * @param encodedDebugInfo the debug info, encoded as a byte array
+ * @param referencedItems an array of the items referenced by instructions, in order of occurance in the encoded
+ * debug info
+ * @return a new DebugInfoItem
with the given values
+ */
+ public static DebugInfoItem getInternedDebugInfoItem(DexFile dexFile,
+ int lineStart,
+ StringIdItem[] parameterNames,
+ byte[] encodedDebugInfo,
+ Item[] referencedItems) {
+ DebugInfoItem debugInfoItem = new DebugInfoItem(dexFile, lineStart, parameterNames, encodedDebugInfo,
+ referencedItems);
+ return dexFile.DebugInfoItemsSection.intern(debugInfoItem);
+ }
+
+ /** {@inheritDoc} */
+ protected void readItem(Input in, ReadContext readContext) {
+ lineStart = in.readUnsignedLeb128();
+ parameterNames = new StringIdItem[in.readUnsignedLeb128()];
+ IndexedSectionCodeItem
that this DebugInfoItem
is associated with
+ * @param codeItem the CodeItem
that this DebugInfoItem
is associated with
+ */
+ protected void setParent(CodeItem codeItem) {
+ this.parent = codeItem;
+ }
+
+ /**
+ * @return the initial value for the line number register for the debug info machine
+ */
+ public int getLineStart() {
+ return lineStart;
+ }
+
+ /**
+ * @return the debug info, encoded as a byte array
+ */
+ public byte[] getEncodedDebugInfo() {
+ return encodedDebugInfo;
+ }
+
+ /**
+ * @return an array of the items referenced by instructions, in order of occurance in the encoded debug info
+ */
+ public Item[] getReferencedItems() {
+ return referencedItems;
+ }
+
+ /**
+ * @return an array of the names of the associated method's parameters. The array can be null if no parameter info
+ * is available, or any element can be null to indicate no info for that parameter
+ */
+ public StringIdItem[] getParameterNames() {
+ return parameterNames;
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/DexFile.java b/dexlib/src/main/java/org/jf/dexlib/DexFile.java
new file mode 100644
index 00000000..3757cffb
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/DexFile.java
@@ -0,0 +1,674 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2009 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib;
+
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.ByteArrayInput;
+import org.jf.dexlib.Util.FileUtils;
+import org.jf.dexlib.Util.Input;
+import org.jf.dexlib.*;
+import org.jf.dexlib.Item;
+import org.jf.dexlib.StringDataItem;
+
+import java.io.File;
+import java.security.DigestException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.HashMap;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.zip.Adler32;
+
+/**
+ * Main use cases
+ *
+ *
+ *
+ *
EncodedCatchHandlerList
for a method{@link org.jf.dexlib.DebugInfoItem}
for a method
Note that the above discrepancies should typically only be "intra-item" differences. They + * shouldn't change the size of the item, or affect how anything else is placed or laid out
+ * + *Creating a dex file from scratch - In this case, a blank dex file is created and then classes + * are added to it incrementally by calling the {@link org.jf.dexlib.Section#intern intern} method of + * {@link DexFile#ClassDefsSection}, which will add all the information necessary to represent the given + * class. For example, when assembling a dex file from a set of assembly text files.
+ * + *In this case, we can choose to write the dex file in a canonical form or not. It is somewhat + * slower to write it in a canonical format, due to the extra sorting and calculations that are + * required.
Reading in the dex file - In this case, the intent is to read in a dex file and expose all the + * data to the calling application. For example, when disassembling a dex file into a text based + * assembly format, or doing other misc processing of the dex file.
These are other use cases that are possible, but did not drive the design of the library. + * No effort was made to test these use cases or ensure that they work. Some of these could + * probably be better achieved with a disassemble - modify - reassemble type process, using + * smali/baksmali or another assembler/disassembler pair that are compatible with each other
+ * + *getPreserveSignedRegisters()
+ */
+ private DexFile(boolean preserveSignedRegisters) {
+ this.preserveSignedRegisters = preserveSignedRegisters;
+
+ sectionsByType = new Section[] {
+ StringIdsSection,
+ TypeIdsSection,
+ ProtoIdsSection,
+ FieldIdsSection,
+ MethodIdsSection,
+ ClassDefsSection,
+ TypeListsSection,
+ AnnotationSetRefListsSection,
+ AnnotationSetsSection,
+ ClassDataSection,
+ CodeItemsSection,
+ AnnotationDirectoriesSection,
+ StringDataSection,
+ DebugInfoItemsSection,
+ AnnotationsSection,
+ EncodedArraysSection,
+ null,
+ null
+ };
+
+ indexedSections = new IndexedSection[] {
+ StringIdsSection,
+ TypeIdsSection,
+ ProtoIdsSection,
+ FieldIdsSection,
+ MethodIdsSection,
+ ClassDefsSection
+ };
+
+ offsettedSections = new OffsettedSection[] {
+ AnnotationSetRefListsSection,
+ AnnotationSetsSection,
+ CodeItemsSection,
+ AnnotationDirectoriesSection,
+ TypeListsSection,
+ StringDataSection,
+ AnnotationsSection,
+ EncodedArraysSection,
+ ClassDataSection,
+ DebugInfoItemsSection
+ };
+ }
+
+
+ /**
+ * Construct a new DexFile instance by reading in the given dex file.
+ * @param file The dex file to read in
+ */
+ public DexFile(String file) {
+ this(new File(file), true);
+ }
+
+ /**
+ * Construct a new DexFile instance by reading in the given dex file,
+ * and optionally keep track of any registers in the debug information that are signed,
+ * so they will be written in the same format.
+ * @param file The dex file to read in
+ * @param preserveSignedRegisters If true, keep track of any registers in the debug information
+ * that are signed, so they will be written in the same format. See
+ * getPreserveSignedRegisters()
+ */
+ public DexFile(String file, boolean preserveSignedRegisters) {
+ this(new File(file), preserveSignedRegisters);
+ }
+
+ /**
+ * Construct a new DexFile instance by reading in the given dex file.
+ * @param file The dex file to read in
+ */
+ public DexFile(File file) {
+ this(file, true);
+ }
+
+ /**
+ * Construct a new DexFile instance by reading in the given dex file,
+ * and optionally keep track of any registers in the debug information that are signed,
+ * so they will be written in the same format.
+ * @param file The dex file to read in
+ * @param preserveSignedRegisters If true, keep track of any registers in the debug information
+ * that are signed, so they will be written in the same format.
+ * @see #getPreserveSignedRegisters
+ */
+ public DexFile(File file, boolean preserveSignedRegisters) {
+ this(preserveSignedRegisters);
+
+ Input in = new ByteArrayInput(FileUtils.readFile(file));
+
+ ReadContext readContext = new ReadContext(this);
+
+ HeaderItem.readFrom(in, 0, readContext);
+
+ //the map offset was set while reading in the header item
+ int mapOffset = readContext.getSectionOffset(ItemType.TYPE_MAP_LIST);
+
+ in.setCursor(mapOffset);
+ MapItem.readFrom(in, 0, readContext);
+
+ for (Section section: sectionsByType) {
+ if (section == null) {
+ continue;
+ }
+
+ int sectionOffset = readContext.getSectionOffset(section.ItemType);
+ if (sectionOffset > 0) {
+ int sectionSize = readContext.getSectionSize(section.ItemType);
+ in.setCursor(sectionOffset);
+ section.readFrom(sectionSize, in, readContext);
+ }
+ }
+ }
+
+ /**
+ * Constructs a new, blank dex file. Classes can be added to this dex file by calling
+ * the Section.intern()
method of ClassDefsSection
+ */
+ public DexFile() {
+ this(true);
+ }
+
+ /**
+ * Get the Section
containing items of the same type as the given item
+ * @param item Get the Section
that contains items of this type
+ * @param Section
containing items of the same type as the given item
+ */
+ public Section
containing items of the given type
+ * @param itemType the type of item
+ * @return the Section
containing items of the given type
+ */
+ public Section getSectionForType(ItemType itemType) {
+ return sectionsByType[itemType.SectionIndex];
+ }
+
+ /**
+ * Get a boolean value indicating whether this dex file preserved any signed
+ * registers in the debug info as it read the dex file in. By default, the dex file
+ * doesn't check whether the registers are encoded as unsigned or signed values.
+ *
+ * This does *not* affect the actual register value that is read in. The value is
+ * read correctly regardless
+ *
+ * This does affect whether any signed registers will retain the same encoding or be
+ * forced to the (correct) unsigned encoding when the dex file is written back out.
+ *
+ * See the discussion about signed register values in the documentation for
+ * DexFile
+ * @return a boolean indicating whether this dex file preserved any signed registers
+ * as it was read in
+ */
+ public boolean getPreserveSignedRegisters() {
+ return preserveSignedRegisters;
+ }
+
+ /**
+ * Get a boolean value indicating whether all items should be placed into a
+ * (possibly arbitrary) "canonical" ordering. If false, then only the items
+ * that must be ordered per the dex specification are sorted.
+ *
+ * When true, writing the dex file involves somewhat more overhead
+ *
+ * If both SortAllItems and Inplace are true, Inplace takes precedence
+ * @return a boolean value indicating whether all items should be sorted
+ */
+ public boolean getSortAllItems() {
+ return this.sortAllItems;
+ }
+
+ /**
+ * Set a boolean value indicating whether all items should be placed into a
+ * (possibly arbitrary) "canonical" ordering. If false, then only the items
+ * that must be ordered per the dex specification are sorted.
+ *
+ * When true, writing the dex file involves somewhat more overhead
+ *
+ * If both SortAllItems and Inplace are true, Inplace takes precedence
+ * @param value a boolean value indicating whether all items should be sorted
+ */
+ public void setSortAllItems(boolean value) {
+ this.sortAllItems = value;
+ }
+
+ /**
+ * Get a boolean value indicating whether items in this dex file should be
+ * written back out "in-place", or whether the normal layout logic should be
+ * applied.
+ *
+ * This should only be used for a dex file that has been read from an existing
+ * dex file, and no modifications have been made to the dex file. Otherwise,
+ * there is a good chance that the resulting dex file will be invalid due to
+ * items that aren't placed correctly
+ *
+ * If both SortAllItems and Inplace are true, Inplace takes precedence
+ * @return a boolean value indicating whether items in this dex file should be
+ * written back out in-place.
+ */
+ public boolean getInplace() {
+ return this.inplace;
+ }
+
+ public int getFileSize() {
+ //TODO: implement this
+ return 0;
+ }
+
+ public int getDataSize() {
+ //TODO: implement this
+ return 0;
+ }
+
+ public int getDataOffset() {
+ //TODO: implement this
+ return 0;
+ }
+
+ /**
+ * Set a boolean value indicating whether items in this dex file should be
+ * written back out "in-place", or whether the normal layout logic should be
+ * applied.
+ *
+ * This should only be used for a dex file that has been read from an existing
+ * dex file, and no modifications have been made to the dex file. Otherwise,
+ * there is a good chance that the resulting dex file will be invalid due to
+ * items that aren't placed correctly
+ *
+ * If both SortAllItems and Inplace are true, Inplace takes precedence
+ * @param value a boolean value indicating whether items in this dex file should be
+ * written back out in-place.
+ */
+ public void setInplace(boolean value) {
+ this.inplace = value;
+ }
+
+ /**
+ * Get an array of Section objects that are sorted by offset.
+ * @return an array of Section objects that are sorted by offset.
+ */
+ protected Section[] getOrderedSections() {
+ int sectionCount = 0;
+
+ for (Section section: sectionsByType) {
+ if (section.ItemType.isIndexedItem() || section.getItems().size() > 0) {
+ sectionCount++;
+ }
+ }
+
+ Section[] sections = new Section[sectionCount];
+ sectionCount = 0;
+ for (Section section: sectionsByType) {
+ if (section.ItemType.isIndexedItem() || section.getItems().size() > 0) {
+ sections[sectionCount++] = section;
+ }
+ }
+
+ Arrays.sort(sections, new ComparatorgetSortAllItems()
and getInplace()
,
+ * and then performs a pass through all of the items, finalizing the position (i.e.
+ * index and/or offset) of each item in the dex file.
+ *
+ * This step is needed primarily so that the indexes and offsets of all indexed and
+ * offsetted items are available when writing references to those items elsewhere.
+ */
+ public void place() {
+ int offset = HeaderItem.placeAt(0, 0);
+
+ for (IndexedSection indexedSection: indexedSections) {
+ if (!this.inplace) {
+ indexedSection.sortSection();
+ }
+ offset = indexedSection.placeAt(offset);
+ }
+
+ dataOffset = offset;
+
+ for (OffsettedSection offsettedSection: offsettedSections) {
+ if (this.sortAllItems && !this.inplace) {
+ offsettedSection.sortSection();
+ }
+ offset = offsettedSection.placeAt(offset);
+ }
+
+ offset = MapItem.placeAt(offset, 0);
+
+ fileSize = offset;
+ dataSize = offset - dataOffset;
+ }
+
+ /**
+ * Writes the dex file to the give AnnotatedOutput
object. If
+ * out.Annotates()
is true, then annotations that document the format
+ * of the dex file are written.
+ *
+ * You must call place()
on this dex file, before calling this method
+ * @param out the AnnotatedOutput object to write the dex file and annotations to
+ *
+ * After calling this method, you should call calcSignature()
and
+ * then calcChecksum()
on the resulting byte array, to calculate the
+ * signature and checksum in the header
+ */
+ public void writeTo(AnnotatedOutput out) {
+ HeaderItem.writeTo(out);
+
+ for (IndexedSection indexedSection: indexedSections) {
+ indexedSection.writeTo(out);
+ }
+
+ //TODO: if inplace is true, we need to use the order of the sections as they were in the original file
+ for (OffsettedSection offsettedSection: offsettedSections) {
+ offsettedSection.writeTo(out);
+ }
+
+ out.alignTo(MapItem.getItemType().ItemAlignment);
+ MapItem.writeTo(out);
+ }
+
+ public final HeaderItem HeaderItem = new HeaderItem(this);
+ public final MapItem MapItem = new MapItem(this);
+
+ /**
+ * The IndexedSection
containing StringIdItem
items
+ */
+ public final IndexedSectionIndexedSection
containing TypeIdItem
items
+ */
+ public final IndexedSectionIndexedSection
containing ProtoIdItem
items
+ */
+ public final IndexedSectionIndexedSection
containing FieldIdItem
items
+ */
+ public final IndexedSectionIndexedSection
containing MethodIdItem
items
+ */
+ public final IndexedSectionIndexedSection
containing ClassDefItem
items
+ */
+ public final IndexedSectionOffsettedSection
containing TypeListItem
items
+ */
+ public final OffsettedSectionOffsettedSection
containing AnnotationSetRefList
items
+ */
+ public final OffsettedSectionOffsettedSection
containing AnnotationSetItem
items
+ */
+ public final OffsettedSectionOffsettedSection
containing ClassDataItem
items
+ */
+ public final OffsettedSectionOffsettedSection
containing CodeItem
items
+ */
+ public final OffsettedSectionOffsettedSection
containing StringDataItem
items
+ */
+ public final OffsettedSectionOffsettedSection
containing DebugInfoItem
items
+ */
+ public final OffsettedSectionOffsettedSection
containing AnnotationItem
items
+ */
+ public final OffsettedSectionOffsettedSection
containing EncodedArrayItem
items
+ */
+ public final OffsettedSectionOffsettedSection
containing AnnotationDirectoryItem
items
+ */
+ public final OffsettedSection.dex
file in the
+ * given array, and modify the array to contain it.
+ *
+ * @param bytes non-null; the bytes of the file
+ */
+ public static void calcChecksum(byte[] bytes) {
+ Adler32 a32 = new Adler32();
+
+ a32.update(bytes, 12, bytes.length - 12);
+
+ int sum = (int) a32.getValue();
+
+ bytes[8] = (byte) sum;
+ bytes[9] = (byte) (sum >> 8);
+ bytes[10] = (byte) (sum >> 16);
+ bytes[11] = (byte) (sum >> 24);
+ }
+}
\ No newline at end of file
diff --git a/dexlib/src/main/java/org/jf/dexlib/EncodedArrayItem.java b/dexlib/src/main/java/org/jf/dexlib/EncodedArrayItem.java
new file mode 100644
index 00000000..e17c29b1
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/EncodedArrayItem.java
@@ -0,0 +1,138 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2009 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib;
+
+import org.jf.dexlib.EncodedValue.ArrayEncodedSubValue;
+import org.jf.dexlib.Util.Input;
+import org.jf.dexlib.Util.AnnotatedOutput;
+
+public class EncodedArrayItem extends ItemEncodedArrayItem
+ * @param dexFile The DexFile
that this item belongs to
+ */
+ protected EncodedArrayItem(DexFile dexFile) {
+ super(dexFile);
+ }
+
+ /**
+ * Creates a new EncodedArrayItem
with the given values
+ * @param dexFile The DexFile
that this item belongs to
+ * @param encodedArray The encoded array value
+ */
+ private EncodedArrayItem(DexFile dexFile, ArrayEncodedSubValue encodedArray) {
+ super(dexFile);
+ this.encodedArray = encodedArray;
+ }
+
+ /**
+ * Returns an EncodedArrayItem
for the given values, and that has been interned into the given
+ * DexFile
+ * @param dexFile The DexFile
that this item belongs to
+ * @param encodedArray The encoded array value
+ * @return an EncodedArrayItem
for the given values, and that has been interned into the given
+ */
+ public static EncodedArrayItem getInternedEncodedArrayItem(DexFile dexFile, ArrayEncodedSubValue encodedArray) {
+ EncodedArrayItem encodedArrayItem = new EncodedArrayItem(dexFile, encodedArray);
+ return dexFile.EncodedArraysSection.intern(encodedArrayItem);
+ }
+
+ /** {@inheritDoc} */
+ protected void readItem(Input in, ReadContext readContext) {
+ encodedArray = new ArrayEncodedSubValue(dexFile, in);
+ }
+
+ /** {@inheritDoc} */
+ protected int placeItem(int offset) {
+ return encodedArray.placeValue(offset);
+ }
+
+ /** {@inheritDoc} */
+ protected void writeItem(AnnotatedOutput out) {
+ if (out.annotates()) {
+ out.annotate("encoded_array");
+ }
+ encodedArray.writeValue(out);
+ }
+
+ /** {@inheritDoc} */
+ public ItemType getItemType() {
+ return ItemType.TYPE_ENCODED_ARRAY_ITEM;
+ }
+
+ /** {@inheritDoc} */
+ public String getConciseIdentity() {
+ return "encoded_array @0x" + Integer.toHexString(getOffset());
+ }
+
+ /** {@inheritDoc} */
+ public int compareTo(EncodedArrayItem encodedArrayItem) {
+ return encodedArray.compareTo(encodedArrayItem.encodedArray);
+ }
+
+ /**
+ * @return The encoded array value
+ */
+ public ArrayEncodedSubValue getEncodedArray() {
+ return encodedArray;
+ }
+
+ /**
+ * calculate and cache the hashcode
+ */
+ private void calcHashCode() {
+ hashCode = encodedArray.hashCode();
+ }
+
+ @Override
+ public int hashCode() {
+ //there's a small possibility that the actual hash code will be 0. If so, we'll
+ //just end up recalculating it each time
+ if (hashCode == 0)
+ calcHashCode();
+ return hashCode;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this==o) {
+ return true;
+ }
+ if (o==null || !this.getClass().equals(o.getClass())) {
+ return false;
+ }
+
+ EncodedArrayItem other = (EncodedArrayItem)o;
+ return (encodedArray.compareTo(other.encodedArray) == 0);
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/EncodedValue/AnnotationEncodedSubValue.java b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/AnnotationEncodedSubValue.java
new file mode 100644
index 00000000..2dd2133c
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/AnnotationEncodedSubValue.java
@@ -0,0 +1,160 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2009 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.EncodedValue;
+
+import org.jf.dexlib.TypeIdItem;
+import org.jf.dexlib.StringIdItem;
+import org.jf.dexlib.DexFile;
+import org.jf.dexlib.Util.Input;
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.Leb128Utils;
+
+/**
+ * An AnnotationEncodedSubValue
is identical to an AnnotationEncodedValue
, except that it
+ * doesn't have the initial valueType/valueArg byte. This is used in the AnnotationItem
object
+ */
+public class AnnotationEncodedSubValue extends EncodedValue {
+ private int hashCode = 0;
+
+ public final TypeIdItem annotationType;
+ public final StringIdItem[] names;
+ public final EncodedValue[] values;
+
+ /**
+ * Constructs a new AnnotationEncodedSubValue
by reading the value from the given Input
+ * object.
+ * @param dexFile The DexFile
that is being read in
+ * @param in The Input
object to read from
+ */
+ public AnnotationEncodedSubValue(DexFile dexFile, Input in) {
+ annotationType = dexFile.TypeIdsSection.getItemByIndex(in.readUnsignedLeb128());
+ names = new StringIdItem[in.readUnsignedLeb128()];
+ values = new EncodedValue[names.length];
+
+ for (int i=0; iInput
+ * object. The Input
's cursor should be set to the 2nd byte of the encoded value
+ * @param dexFile The DexFile
that is being read in
+ * @param in The Input
object to read from
+ */
+ protected AnnotationEncodedValue(DexFile dexFile, Input in) {
+ super(dexFile, in);
+ }
+
+ /**
+ * Constructs a new AnnotationEncodedValue
with the given values. names and values must be the same
+ * length, and must be sorted according to the name
+ * @param annotationType The type of the annotation
+ * @param names An array of the names of the elements of the annotation
+ * @param values An array of the values of the elements on the annotation
+ */
+ public AnnotationEncodedValue(TypeIdItem annotationType, StringIdItem[] names, EncodedValue[] values) {
+ super(annotationType, names, values);
+ }
+
+ /** {@inheritDoc} */
+ public void writeValue(AnnotatedOutput out) {
+ out.writeByte(ValueType.VALUE_ANNOTATION.value);
+ super.writeValue(out);
+ }
+
+ /** {@inheritDoc} */
+ public int placeValue(int offset) {
+ return super.placeValue(offset + 1);
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/EncodedValue/ArrayEncodedSubValue.java b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/ArrayEncodedSubValue.java
new file mode 100644
index 00000000..cc1a2343
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/ArrayEncodedSubValue.java
@@ -0,0 +1,129 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2009 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.EncodedValue;
+
+import org.jf.dexlib.DexFile;
+import org.jf.dexlib.Util.Input;
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.Leb128Utils;
+
+/**
+ * An ArrayEncodedSubValue
is identical to an ArrayEncodedValue
, except that it
+ * doesn't have the initial valueType/valueArg byte. This is used in the EncodedArrayItem
object
+ */
+public class ArrayEncodedSubValue extends EncodedValue {
+ private int hashCode = 0;
+
+ public final EncodedValue[] values;
+
+ /**
+ * Constructs a new ArrayEncodedSubValue
by reading the value from the given Input
object.
+ * The Input
's cursor should be set to the 2nd byte of the encoded value
+ * @param dexFile The DexFile
that is being read in
+ * @param in The Input
object to read from
+ */
+ public ArrayEncodedSubValue(DexFile dexFile, Input in) {
+ values = new EncodedValue[in.readUnsignedLeb128()];
+
+ for (int i=0; iInput
object.
+ * The Input
's cursor should be set to the 2nd byte of the encoded value
+ * @param dexFile The DexFile
that is being read in
+ * @param in The Input
object to read from
+ */
+ protected ArrayEncodedValue(DexFile dexFile, Input in) {
+ super(dexFile, in);
+ }
+
+ /**
+ * Constructs a new ArrayEncodedValue
with the given values
+ * @param values The array values
+ */
+ public ArrayEncodedValue(EncodedValue[] values) {
+ super(values);
+ }
+
+ /** {@inheritDoc} */
+ public void writeValue(AnnotatedOutput out) {
+ out.writeByte(ValueType.VALUE_ARRAY.value);
+ super.writeValue(out);
+ }
+
+ /** {@inheritDoc} */
+ public int placeValue(int offset) {
+ return super.placeValue(offset + 1);
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/EncodedValue/BooleanEncodedValue.java b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/BooleanEncodedValue.java
new file mode 100644
index 00000000..946ff3a3
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/BooleanEncodedValue.java
@@ -0,0 +1,106 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2009 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.EncodedValue;
+
+import org.jf.dexlib.Util.AnnotatedOutput;
+
+public class BooleanEncodedValue extends EncodedValue {
+ /**
+ * The dupliton values
+ */
+ public static final BooleanEncodedValue TrueValue = new BooleanEncodedValue(true);
+ public static final BooleanEncodedValue FalseValue = new BooleanEncodedValue(false);
+
+ public final boolean value;
+
+ /**
+ * Constructs a new BooleanEncodedValue
with the given value
+ * @param value The value
+ */
+ private BooleanEncodedValue(boolean value) {
+ this.value = value;
+ }
+
+ /**
+ * Gets the BooleanEncodedValue
for the given valueArg value. The high 3 bits of the first byte should
+ * be passed as the valueArg parameter
+ * @param valueArg The high 3 bits of the first byte of this encoded value
+ * @return the BooleanEncodedValue
for the given valueArg value
+ */
+ protected static BooleanEncodedValue getBooleanEncodedValue(byte valueArg) {
+ if (valueArg == 0) {
+ return FalseValue;
+ } else if (valueArg == 1) {
+ return TrueValue;
+ }
+ throw new RuntimeException("valueArg must be either 0 or 1");
+ }
+
+ /**
+ * Gets the BooleanEncodedValue
for the given boolean value
+ * @param value the boolean value
+ * @return the BooleanEncodedValue
for the given boolean value
+ */
+ public static BooleanEncodedValue getBooleanEncodedValue(boolean value) {
+ if (value) {
+ return TrueValue;
+ }
+ return FalseValue;
+ }
+
+ /** {@inheritDoc} */
+ public void writeValue(AnnotatedOutput out) {
+ out.writeByte(ValueType.VALUE_BOOLEAN.value | (value?1:0 << 5));
+ }
+
+ /** {@inheritDoc} */
+ public int placeValue(int offset) {
+ return offset + 1;
+ }
+
+ /** {@inheritDoc} */
+ protected int compareValue(EncodedValue o) {
+ BooleanEncodedValue other = (BooleanEncodedValue)o;
+ if (value == other.value)
+ return 0;
+ if (value)
+ return 1;
+ return -1;
+ }
+
+ /** {@inheritDoc} */
+ public ValueType getValueType() {
+ return ValueType.VALUE_BOOLEAN;
+ }
+
+ @Override
+ public int hashCode() {
+ return value?1:0;
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/EncodedValue/ByteEncodedValue.java b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/ByteEncodedValue.java
new file mode 100644
index 00000000..ce66d9a8
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/ByteEncodedValue.java
@@ -0,0 +1,82 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2009 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.EncodedValue;
+
+import org.jf.dexlib.Util.Input;
+import org.jf.dexlib.Util.EncodedValueUtils;
+import org.jf.dexlib.Util.AnnotatedOutput;
+
+public class ByteEncodedValue extends EncodedValue {
+ public final byte value;
+
+ /**
+ * Constructs a new ByteEncodedValue
by reading the value from the given Input
object.
+ * The Input
's cursor should be set to the 2nd byte of the encoded value
+ * @param in The Input
object to read from
+ */
+ protected ByteEncodedValue(Input in) {
+ value = (byte)EncodedValueUtils.decodeSignedIntegralValue(in.readBytes(1));
+ }
+
+ /**
+ * Constructs a new ByteEncodedValue
with the given value
+ * @param value The value
+ */
+ public ByteEncodedValue(byte value) {
+ this.value = value;
+ }
+
+ /** {@inheritDoc} */
+ public void writeValue(AnnotatedOutput out) {
+ out.writeByte(ValueType.VALUE_BYTE.value);
+ out.writeByte(value);
+ }
+
+ /** {@inheritDoc} */
+ public int placeValue(int offset) {
+ return offset + 2;
+ }
+
+ /** {@inheritDoc} */
+ protected int compareValue(EncodedValue o) {
+ ByteEncodedValue other = (ByteEncodedValue)o;
+
+ return value - other.value;
+ }
+
+ /** {@inheritDoc} */
+ public ValueType getValueType() {
+ return ValueType.VALUE_BYTE;
+ }
+
+ @Override
+ public int hashCode() {
+ return value;
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/EncodedValue/CharEncodedValue.java b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/CharEncodedValue.java
new file mode 100644
index 00000000..3cb0b1e0
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/CharEncodedValue.java
@@ -0,0 +1,85 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2009 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.EncodedValue;
+
+import org.jf.dexlib.Util.Input;
+import org.jf.dexlib.Util.EncodedValueUtils;
+import org.jf.dexlib.Util.AnnotatedOutput;
+
+public class CharEncodedValue extends EncodedValue {
+ public final char value;
+
+ /**
+ * Constructs a new CharEncodedValue
by reading the value from the given Input
object.
+ * The Input
's cursor should be set to the 2nd byte of the encoded value, and the high 3 bits of
+ * the first byte should be passed as the valueArg parameter
+ * @param in The Input
object to read from
+ * @param valueArg The high 3 bits of the first byte of this encoded value
+ */
+ protected CharEncodedValue(Input in, byte valueArg) {
+ value = (char)EncodedValueUtils.decodeUnsignedIntegralValue(in.readBytes(valueArg+1));
+ }
+
+ /**
+ * Constructs a new CharEncodedValue
with the given value
+ * @param value The value
+ */
+ public CharEncodedValue(char value) {
+ this.value = value;
+ }
+
+ /** {@inheritDoc} */
+ public void writeValue(AnnotatedOutput out) {
+ byte[] bytes = EncodedValueUtils.encodeUnsignedIntegralValue(value);
+ out.writeByte(ValueType.VALUE_CHAR.value | ((bytes.length - 1) << 5));
+ out.write(bytes);
+ }
+
+ /** {@inheritDoc} */
+ public int placeValue(int offset) {
+ return offset + EncodedValueUtils.getRequiredBytesForUnsignedIntegralValue(value) + 1;
+ }
+
+ /** {@inheritDoc} */
+ protected int compareValue(EncodedValue o) {
+ CharEncodedValue other = (CharEncodedValue)o;
+
+ return value - other.value;
+ }
+
+ /** {@inheritDoc} */
+ public ValueType getValueType() {
+ return ValueType.VALUE_CHAR;
+ }
+
+ @Override
+ public int hashCode() {
+ return value;
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/EncodedValue/DoubleEncodedValue.java b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/DoubleEncodedValue.java
new file mode 100644
index 00000000..d38f007e
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/DoubleEncodedValue.java
@@ -0,0 +1,88 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2009 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.EncodedValue;
+
+import org.jf.dexlib.Util.Input;
+import org.jf.dexlib.Util.EncodedValueUtils;
+import org.jf.dexlib.Util.AnnotatedOutput;
+
+public class DoubleEncodedValue extends EncodedValue {
+ public final double value;
+
+ /**
+ * Constructs a new DoubleEncodedValue
by reading the value from the given Input
object.
+ * The Input
's cursor should be set to the 2nd byte of the encoded value, and the high 3 bits of
+ * the first byte should be passed as the valueArg parameter
+ * @param in The Input
object to read from
+ * @param valueArg The high 3 bits of the first byte of this encoded value
+ */
+ protected DoubleEncodedValue(Input in, byte valueArg) {
+ long longValue = EncodedValueUtils.decodeRightZeroExtendedValue(in.readBytes(valueArg + 1));
+ value = Double.longBitsToDouble(longValue);
+ }
+
+ /**
+ * Constructs a new DoubleEncodedValue
with the given value
+ * @param value The value
+ */
+ public DoubleEncodedValue(double value) {
+ this.value = value;
+ }
+
+ /** {@inheritDoc} */
+ public void writeValue(AnnotatedOutput out) {
+ byte[] bytes = EncodedValueUtils.encodeRightZeroExtendedValue(Double.doubleToRawLongBits(value));
+ out.writeByte(ValueType.VALUE_DOUBLE.value | ((bytes.length - 1) << 5));
+ out.write(bytes);
+ }
+
+ /** {@inheritDoc} */
+ public int placeValue(int offset) {
+ return offset + 1 + EncodedValueUtils.getRequiredBytesForRightZeroExtendedValue(
+ Double.doubleToRawLongBits(value));
+ }
+
+ /** {@inheritDoc} */
+ protected int compareValue(EncodedValue o) {
+ DoubleEncodedValue other = (DoubleEncodedValue)o;
+
+ return Double.compare(value, other.value);
+ }
+
+ /** {@inheritDoc} */
+ public ValueType getValueType() {
+ return ValueType.VALUE_DOUBLE;
+ }
+
+ @Override
+ public int hashCode() {
+ return (int)Double.doubleToRawLongBits(value);
+ }
+}
+
diff --git a/dexlib/src/main/java/org/jf/dexlib/EncodedValue/EncodedValue.java b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/EncodedValue.java
new file mode 100644
index 00000000..67a9a5ca
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/EncodedValue.java
@@ -0,0 +1,125 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2009 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.EncodedValue;
+
+import org.jf.dexlib.Util.Input;
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.DexFile;
+
+public abstract class EncodedValue implements ComparableEncodedValue
to the given AnnotatedOutput
object
+ * @param out the AnnotatedOutput
object to write to
+ */
+ public abstract void writeValue(AnnotatedOutput out);
+
+ /**
+ * Calculates the size of this encoded value and returns offset + size;
+ * @param offset The offset to place this encoded value
+ * @return the offset immediately after this encoded value
+ */
+ public abstract int placeValue(int offset);
+
+
+ public static EncodedValue readEncodedValue(DexFile dexFile, Input in) {
+ Byte b = in.readByte();
+ ValueType valueType = ValueType.fromByte((byte)(b & 0x1f));
+ byte valueArg = (byte)((b & 0xFF) >> 5);
+
+ switch (valueType) {
+ case VALUE_BYTE:
+ return new ByteEncodedValue(in);
+ case VALUE_SHORT:
+ return new ShortEncodedValue(in, valueArg);
+ case VALUE_CHAR:
+ return new CharEncodedValue(in, valueArg);
+ case VALUE_INT:
+ return new IntEncodedValue(in, valueArg);
+ case VALUE_LONG:
+ return new LongEncodedValue(in, valueArg);
+ case VALUE_FLOAT:
+ return new FloatEncodedValue(in, valueArg);
+ case VALUE_DOUBLE:
+ return new DoubleEncodedValue(in, valueArg);
+ case VALUE_STRING:
+ return new StringEncodedValue(dexFile, in, valueArg);
+ case VALUE_TYPE:
+ return new TypeEncodedValue(dexFile, in, valueArg);
+ case VALUE_FIELD:
+ return new FieldEncodedValue(dexFile, in, valueArg);
+ case VALUE_METHOD:
+ return new MethodEncodedValue(dexFile, in, valueArg);
+ case VALUE_ENUM:
+ return new EnumEncodedValue(dexFile, in, valueArg);
+ case VALUE_ARRAY:
+ return new ArrayEncodedValue(dexFile, in);
+ case VALUE_ANNOTATION:
+ return new AnnotationEncodedValue(dexFile, in);
+ case VALUE_NULL:
+ return NullEncodedValue.NullValue;
+ case VALUE_BOOLEAN:
+ return BooleanEncodedValue.getBooleanEncodedValue(valueArg);
+ }
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ public int compareTo(EncodedValue o) {
+ int comp = getValueType().compareTo(o.getValueType());
+ if (comp == 0) {
+ comp = compareValue(o);
+ }
+ return comp;
+ }
+
+ /**
+ * Compare the value of this EncodedValue
against the value of the given EncodedValue
+ * @param o The EncodedValue
to compare against
+ * @return A standard comparison integer value
+ */
+ protected abstract int compareValue(EncodedValue o);
+
+ /**
+ * @return the ValueType
representing the type of this EncodedValue
+ */
+ public abstract ValueType getValueType();
+
+ @Override
+ public boolean equals(Object o) {
+ if (this==o) {
+ return true;
+ }
+ if (o==null || !(o instanceof EncodedValue)) {
+ return false;
+ }
+
+ return this.compareTo((EncodedValue)o) == 0;
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/EncodedValue/EnumEncodedValue.java b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/EnumEncodedValue.java
new file mode 100644
index 00000000..1735d5c6
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/EnumEncodedValue.java
@@ -0,0 +1,89 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2009 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.EncodedValue;
+
+import org.jf.dexlib.FieldIdItem;
+import org.jf.dexlib.DexFile;
+import org.jf.dexlib.Util.Input;
+import org.jf.dexlib.Util.EncodedValueUtils;
+import org.jf.dexlib.Util.AnnotatedOutput;
+
+public class EnumEncodedValue extends EncodedValue {
+ public final FieldIdItem value;
+
+ /**
+ * Constructs a new EnumEncodedValue
by reading the field index from the given Input
+ * object. The Input
's cursor should be set to the 2nd byte of the encoded value, and the high 3 bits
+ * of the first byte should be passed as the valueArg parameter
+ * @param dexFile The DexFile
that is being read in
+ * @param in The Input
object to read from
+ * @param valueArg The high 3 bits of the first byte of this encoded value
+ */
+ protected EnumEncodedValue(DexFile dexFile, Input in, byte valueArg) {
+ int index = (int) EncodedValueUtils.decodeUnsignedIntegralValue(in.readBytes(valueArg+1));
+ value = dexFile.FieldIdsSection.getItemByIndex(index);
+ }
+
+ /**
+ * Constructs a new EnumEncodedValue
with the given FieldIdItem
value
+ * @param value The FieldIdItem
value
+ */
+ public EnumEncodedValue(FieldIdItem value) {
+ this.value = value;
+ }
+
+ /** {@inheritDoc} */
+ public void writeValue(AnnotatedOutput out) {
+ byte[] bytes = EncodedValueUtils.encodeUnsignedIntegralValue(value.getIndex());
+ out.writeByte(ValueType.VALUE_ENUM.value | ((bytes.length - 1) << 5));
+ out.write(bytes);
+ }
+
+ /** {@inheritDoc} */
+ public int placeValue(int offset) {
+ return offset + EncodedValueUtils.getRequiredBytesForUnsignedIntegralValue(value.getIndex()) + 1;
+ }
+
+ /** {@inheritDoc} */
+ protected int compareValue(EncodedValue o) {
+ EnumEncodedValue other = (EnumEncodedValue)o;
+
+ return value.getIndex() - other.value.getIndex();
+ }
+
+ /** {@inheritDoc} */
+ public ValueType getValueType() {
+ return ValueType.VALUE_ENUM;
+ }
+
+ @Override
+ public int hashCode() {
+ return value.hashCode();
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/EncodedValue/FieldEncodedValue.java b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/FieldEncodedValue.java
new file mode 100644
index 00000000..912fe381
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/FieldEncodedValue.java
@@ -0,0 +1,89 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2009 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.EncodedValue;
+
+import org.jf.dexlib.DexFile;
+import org.jf.dexlib.FieldIdItem;
+import org.jf.dexlib.Util.Input;
+import org.jf.dexlib.Util.EncodedValueUtils;
+import org.jf.dexlib.Util.AnnotatedOutput;
+
+public class FieldEncodedValue extends EncodedValue {
+ public final FieldIdItem value;
+
+ /**
+ * Constructs a new FieldEncodedValue
by reading the field index from the given Input
+ * object. The Input
's cursor should be set to the 2nd byte of the encoded value, and the high 3 bits
+ * of the first byte should be passed as the valueArg parameter
+ * @param dexFile The DexFile
that is being read in
+ * @param in The Input
object to read from
+ * @param valueArg The high 3 bits of the first byte of this encoded value
+ */
+ protected FieldEncodedValue(DexFile dexFile, Input in, byte valueArg) {
+ int index = (int) EncodedValueUtils.decodeUnsignedIntegralValue(in.readBytes(valueArg+1));
+ value = dexFile.FieldIdsSection.getItemByIndex(index);
+ }
+
+ /**
+ * Constructs a new FieldEncodedValue
with the given FieldIdItem
value
+ * @param value The FieldIdItem
value
+ */
+ public FieldEncodedValue(FieldIdItem value) {
+ this.value = value;
+ }
+
+ /** {@inheritDoc} */
+ public void writeValue(AnnotatedOutput out) {
+ byte[] bytes = EncodedValueUtils.encodeUnsignedIntegralValue(value.getIndex());
+ out.writeByte(ValueType.VALUE_FIELD.value | ((bytes.length - 1) << 5));
+ out.write(bytes);
+ }
+
+ /** {@inheritDoc} */
+ public int placeValue(int offset) {
+ return offset + EncodedValueUtils.getRequiredBytesForUnsignedIntegralValue(value.getIndex()) + 1;
+ }
+
+ /** {@inheritDoc} */
+ protected int compareValue(EncodedValue o) {
+ FieldEncodedValue other = (FieldEncodedValue)o;
+
+ return value.getIndex()-other.value.getIndex();
+ }
+
+ /** {@inheritDoc} */
+ public ValueType getValueType() {
+ return ValueType.VALUE_FIELD;
+ }
+
+ @Override
+ public int hashCode() {
+ return value.hashCode();
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/EncodedValue/FloatEncodedValue.java b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/FloatEncodedValue.java
new file mode 100644
index 00000000..40af710d
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/FloatEncodedValue.java
@@ -0,0 +1,87 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2009 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.EncodedValue;
+
+import org.jf.dexlib.Util.Input;
+import org.jf.dexlib.Util.EncodedValueUtils;
+import org.jf.dexlib.Util.AnnotatedOutput;
+
+public class FloatEncodedValue extends EncodedValue {
+ public final float value;
+
+ /**
+ * Constructs a new FloatEncodedValue
by reading the value from the given Input
object.
+ * The Input
's cursor should be set to the 2nd byte of the encoded value, and the high 3 bits of
+ * the first byte should be passed as the valueArg parameter
+ * @param in The Input
object to read from
+ * @param valueArg The high 3 bits of the first byte of this encoded value
+ */
+ protected FloatEncodedValue(Input in, byte valueArg) {
+ long longValue = EncodedValueUtils.decodeRightZeroExtendedValue(in.readBytes(valueArg + 1));
+ value = Float.intBitsToFloat((int)((longValue >> 32) & 0xFFFFFFFFL));
+ }
+
+ /**
+ * Constructs a new FloatEncodedValue
with the given value
+ * @param value The value
+ */
+ public FloatEncodedValue(float value) {
+ this.value = value;
+ }
+
+ /** {@inheritDoc} */
+ public void writeValue(AnnotatedOutput out) {
+ byte[] bytes = EncodedValueUtils.encodeRightZeroExtendedValue(((long)Float.floatToRawIntBits(value)) << 32);
+ out.writeByte(ValueType.VALUE_FLOAT.value | ((bytes.length - 1) << 5));
+ out.write(bytes);
+ }
+
+ /** {@inheritDoc} */
+ public int placeValue(int offset) {
+ return offset + 1 + EncodedValueUtils.getRequiredBytesForRightZeroExtendedValue(
+ ((long)Float.floatToRawIntBits(value)) << 32);
+ }
+
+ /** {@inheritDoc} */
+ protected int compareValue(EncodedValue o) {
+ FloatEncodedValue other = (FloatEncodedValue)o;
+
+ return Float.compare(value, other.value);
+ }
+
+ /** {@inheritDoc} */
+ public ValueType getValueType() {
+ return ValueType.VALUE_FLOAT;
+ }
+
+ @Override
+ public int hashCode() {
+ return Float.floatToRawIntBits(value);
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/EncodedValue/IntEncodedValue.java b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/IntEncodedValue.java
new file mode 100644
index 00000000..9cf06246
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/IntEncodedValue.java
@@ -0,0 +1,85 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2009 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.EncodedValue;
+
+import org.jf.dexlib.Util.Input;
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.EncodedValueUtils;
+
+public class IntEncodedValue extends EncodedValue {
+ public final int value;
+
+ /**
+ * Constructs a new IntEncodedValue
by reading the value from the given Input
object.
+ * The Input
's cursor should be set to the 2nd byte of the encoded value, and the high 3 bits of
+ * the first byte should be passed as the valueArg parameter
+ * @param in The Input
object to read from
+ * @param valueArg The high 3 bits of the first byte of this encoded value
+ */
+ protected IntEncodedValue(Input in, byte valueArg) {
+ value = (int)EncodedValueUtils.decodeSignedIntegralValue(in.readBytes(valueArg+1));
+ }
+
+ /**
+ * Constructs a new IntEncodedValue
with the given value
+ * @param value The value
+ */
+ public IntEncodedValue(int value) {
+ this.value = value;
+ }
+
+ /** {@inheritDoc} */
+ public void writeValue(AnnotatedOutput out) {
+ byte[] bytes = EncodedValueUtils.encodeSignedIntegralValue(value);
+ out.writeByte(ValueType.VALUE_INT.value | ((bytes.length - 1) << 5));
+ out.write(bytes);
+ }
+
+ /** {@inheritDoc} */
+ public int placeValue(int offset) {
+ return offset + EncodedValueUtils.getRequiredBytesForSignedIntegralValue(value) + 1;
+ }
+
+ /** {@inheritDoc} */
+ protected int compareValue(EncodedValue o) {
+ IntEncodedValue other = (IntEncodedValue)o;
+
+ return value - other.value;
+ }
+
+ /** {@inheritDoc} */
+ public ValueType getValueType() {
+ return ValueType.VALUE_INT;
+ }
+
+ @Override
+ public int hashCode() {
+ return value;
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/EncodedValue/LongEncodedValue.java b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/LongEncodedValue.java
new file mode 100644
index 00000000..efd40d9d
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/LongEncodedValue.java
@@ -0,0 +1,85 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2009 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.EncodedValue;
+
+import org.jf.dexlib.Util.Input;
+import org.jf.dexlib.Util.EncodedValueUtils;
+import org.jf.dexlib.Util.AnnotatedOutput;
+
+public class LongEncodedValue extends EncodedValue {
+ public final long value;
+
+ /**
+ * Constructs a new LongEncodedValue
by reading the value from the given Input
object.
+ * The Input
's cursor should be set to the 2nd byte of the encoded value, and the high 3 bits of
+ * the first byte should be passed as the valueArg parameter
+ * @param in The Input
object to read from
+ * @param valueArg The high 3 bits of the first byte of this encoded value
+ */
+ protected LongEncodedValue(Input in, byte valueArg) {
+ value = EncodedValueUtils.decodeSignedIntegralValue(in.readBytes(valueArg+1));
+ }
+
+ /**
+ * Constructs a new LongEncodedValue
with the given value
+ * @param value The value
+ */
+ public LongEncodedValue(long value) {
+ this.value = value;
+ }
+
+ /** {@inheritDoc} */
+ public void writeValue(AnnotatedOutput out) {
+ byte[] bytes = EncodedValueUtils.encodeSignedIntegralValue(value);
+ out.writeByte(ValueType.VALUE_LONG.value | ((bytes.length - 1) << 5));
+ out.write(bytes);
+ }
+
+ /** {@inheritDoc} */
+ public int placeValue(int offset) {
+ return offset + EncodedValueUtils.getRequiredBytesForSignedIntegralValue(value) + 1;
+ }
+
+ /** {@inheritDoc} */
+ protected int compareValue(EncodedValue o) {
+ LongEncodedValue other = (LongEncodedValue)o;
+
+ return Long.signum(value - other.value);
+ }
+
+ /** {@inheritDoc} */
+ public ValueType getValueType() {
+ return ValueType.VALUE_LONG;
+ }
+
+ @Override
+ public int hashCode() {
+ return (int)value;
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/EncodedValue/MethodEncodedValue.java b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/MethodEncodedValue.java
new file mode 100644
index 00000000..346423cb
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/MethodEncodedValue.java
@@ -0,0 +1,89 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2009 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.EncodedValue;
+
+import org.jf.dexlib.DexFile;
+import org.jf.dexlib.MethodIdItem;
+import org.jf.dexlib.Util.Input;
+import org.jf.dexlib.Util.EncodedValueUtils;
+import org.jf.dexlib.Util.AnnotatedOutput;
+
+public class MethodEncodedValue extends EncodedValue {
+ public final MethodIdItem value;
+
+ /**
+ * Constructs a new MethodEncodedValue
by reading the method index from the given Input
+ * object. The Input
's cursor should be set to the 2nd byte of the encoded value, and the high 3 bits
+ * of the first byte should be passed as the valueArg parameter
+ * @param dexFile The DexFile
that is being read in
+ * @param in The Input
object to read from
+ * @param valueArg The high 3 bits of the first byte of this encoded value
+ */
+ protected MethodEncodedValue(DexFile dexFile, Input in, byte valueArg) {
+ int index = (int) EncodedValueUtils.decodeUnsignedIntegralValue(in.readBytes(valueArg+1));
+ value = dexFile.MethodIdsSection.getItemByIndex(index);
+ }
+
+ /**
+ * Constructs a new MethodEncodedValue
with the given MethodIdItem
value
+ * @param value The MethodIdItem
value
+ */
+ public MethodEncodedValue(MethodIdItem value) {
+ this.value = value;
+ }
+
+ /** {@inheritDoc} */
+ public void writeValue(AnnotatedOutput out) {
+ byte[] bytes = EncodedValueUtils.encodeUnsignedIntegralValue(value.getIndex());
+ out.writeByte(ValueType.VALUE_METHOD.value | ((bytes.length - 1) << 5));
+ out.write(bytes);
+ }
+
+ /** {@inheritDoc} */
+ public int placeValue(int offset) {
+ return offset + EncodedValueUtils.getRequiredBytesForUnsignedIntegralValue(value.getIndex()) + 1;
+ }
+
+ /** {@inheritDoc} */
+ protected int compareValue(EncodedValue o) {
+ MethodEncodedValue other = (MethodEncodedValue)o;
+
+ return value.getIndex() - other.value.getIndex();
+ }
+
+ /** {@inheritDoc} */
+ public ValueType getValueType() {
+ return ValueType.VALUE_METHOD;
+ }
+
+ @Override
+ public int hashCode() {
+ return value.hashCode();
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/EncodedValue/NullEncodedValue.java b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/NullEncodedValue.java
new file mode 100644
index 00000000..98ded9e9
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/NullEncodedValue.java
@@ -0,0 +1,69 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2009 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.EncodedValue;
+
+import org.jf.dexlib.Util.AnnotatedOutput;
+
+public class NullEncodedValue extends EncodedValue {
+ /**
+ * The singleton value
+ */
+ public static final NullEncodedValue NullValue = new NullEncodedValue();
+
+ /**
+ * Constructs a new NullEncodedValue
+ */
+ private NullEncodedValue() {
+ }
+
+ /** {@inheritDoc} */
+ public void writeValue(AnnotatedOutput out) {
+ out.writeByte(ValueType.VALUE_NULL.value);
+ }
+
+ /** {@inheritDoc} */
+ public int placeValue(int offset) {
+ return offset + 1;
+ }
+
+ /** {@inheritDoc} */
+ protected int compareValue(EncodedValue o) {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ public ValueType getValueType() {
+ return ValueType.VALUE_NULL;
+ }
+
+ @Override
+ public int hashCode() {
+ return 1;
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/EncodedValue/ShortEncodedValue.java b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/ShortEncodedValue.java
new file mode 100644
index 00000000..7f9fc501
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/ShortEncodedValue.java
@@ -0,0 +1,85 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2009 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.EncodedValue;
+
+import org.jf.dexlib.Util.Input;
+import org.jf.dexlib.Util.EncodedValueUtils;
+import org.jf.dexlib.Util.AnnotatedOutput;
+
+public class ShortEncodedValue extends EncodedValue {
+ public final short value;
+
+ /**
+ * Constructs a new ShortEncodedValue
by reading the value from the given Input
object.
+ * The Input
's cursor should be set to the 2nd byte of the encoded value, and the high 3 bits of
+ * the first byte should be passed as the valueArg parameter
+ * @param in The Input
object to read from
+ * @param valueArg The high 3 bits of the first byte of this encoded value
+ */
+ protected ShortEncodedValue(Input in, byte valueArg) {
+ value = (short) EncodedValueUtils.decodeSignedIntegralValue(in.readBytes(valueArg+1));
+ }
+
+ /**
+ * Constructs a new ShortEncodedValue
with the given value
+ * @param value The value
+ */
+ public ShortEncodedValue(short value) {
+ this.value = value;
+ }
+
+ /** {@inheritDoc} */
+ public void writeValue(AnnotatedOutput out) {
+ byte[] bytes = EncodedValueUtils.encodeSignedIntegralValue(value);
+ out.writeByte(ValueType.VALUE_SHORT.value | ((bytes.length - 1) << 5));
+ out.write(bytes);
+ }
+
+ /** {@inheritDoc} */
+ public int placeValue(int offset) {
+ return offset + EncodedValueUtils.getRequiredBytesForSignedIntegralValue(value) + 1;
+ }
+
+ /** {@inheritDoc} */
+ protected int compareValue(EncodedValue o) {
+ ShortEncodedValue other = (ShortEncodedValue)o;
+
+ return value - other.value;
+ }
+
+ /** {@inheritDoc} */
+ public ValueType getValueType() {
+ return ValueType.VALUE_SHORT;
+ }
+
+ @Override
+ public int hashCode() {
+ return value;
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/EncodedValue/StringEncodedValue.java b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/StringEncodedValue.java
new file mode 100644
index 00000000..96d1ddcf
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/StringEncodedValue.java
@@ -0,0 +1,89 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2009 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.EncodedValue;
+
+import org.jf.dexlib.Util.Input;
+import org.jf.dexlib.Util.EncodedValueUtils;
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.StringIdItem;
+import org.jf.dexlib.DexFile;
+
+public class StringEncodedValue extends EncodedValue {
+ public final StringIdItem value;
+
+ /**
+ * Constructs a new StringEncodedValue
by reading the string index from the given Input
+ * object. The Input
's cursor should be set to the 2nd byte of the encoded value, and the high 3 bits
+ * of the first byte should be passed as the valueArg parameter
+ * @param dexFile The DexFile
that is being read in
+ * @param in The Input
object to read from
+ * @param valueArg The high 3 bits of the first byte of this encoded value
+ */
+ protected StringEncodedValue(DexFile dexFile, Input in, byte valueArg) {
+ int index = (int)EncodedValueUtils.decodeUnsignedIntegralValue(in.readBytes(valueArg+1));
+ value = dexFile.StringIdsSection.getItemByIndex(index);
+ }
+
+ /**
+ * Constructs a new StringEncodedValue
with the given StringIdItem
value
+ * @param value The StringIdItem
value
+ */
+ public StringEncodedValue(StringIdItem value) {
+ this.value = value;
+ }
+
+ /** {@inheritDoc} */
+ public void writeValue(AnnotatedOutput out) {
+ byte[] bytes = EncodedValueUtils.encodeUnsignedIntegralValue(value.getIndex());
+ out.writeByte(ValueType.VALUE_STRING.value | ((bytes.length - 1) << 5));
+ out.write(bytes);
+ }
+
+ /** {@inheritDoc} */
+ public int placeValue(int offset) {
+ return offset + EncodedValueUtils.getRequiredBytesForUnsignedIntegralValue(value.getIndex()) + 1;
+ }
+
+ /** {@inheritDoc} */
+ protected int compareValue(EncodedValue o) {
+ StringEncodedValue other = (StringEncodedValue)o;
+
+ return value.getIndex() - other.value.getIndex();
+ }
+
+ /** {@inheritDoc} */
+ public ValueType getValueType() {
+ return ValueType.VALUE_STRING;
+ }
+
+ @Override
+ public int hashCode() {
+ return value.hashCode();
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/EncodedValue/TypeEncodedValue.java b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/TypeEncodedValue.java
new file mode 100644
index 00000000..5e8857b0
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/TypeEncodedValue.java
@@ -0,0 +1,89 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2009 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.EncodedValue;
+
+import org.jf.dexlib.DexFile;
+import org.jf.dexlib.TypeIdItem;
+import org.jf.dexlib.Util.Input;
+import org.jf.dexlib.Util.EncodedValueUtils;
+import org.jf.dexlib.Util.AnnotatedOutput;
+
+public class TypeEncodedValue extends EncodedValue {
+ public final TypeIdItem value;
+
+ /**
+ * Constructs a new TypeEncodedValue
by reading the type index from the given Input
+ * object. The Input
's cursor should be set to the 2nd byte of the encoded value, and the high 3 bits
+ * of the first byte should be passed as the valueArg parameter
+ * @param dexFile The DexFile
that is being read in
+ * @param in The Input
object to read from
+ * @param valueArg The high 3 bits of the first byte of this encoded value
+ */
+ protected TypeEncodedValue(DexFile dexFile, Input in, byte valueArg) {
+ int index = (int) EncodedValueUtils.decodeUnsignedIntegralValue(in.readBytes(valueArg+1));
+ value = dexFile.TypeIdsSection.getItemByIndex(index);
+ }
+
+ /**
+ * Constructs a new TypeEncodedValue
with the given TypeIdItem
value
+ * @param value The TypeIdItem
value
+ */
+ public TypeEncodedValue(TypeIdItem value) {
+ this.value = value;
+ }
+
+ /** {@inheritDoc} */
+ public void writeValue(AnnotatedOutput out) {
+ byte[] bytes = EncodedValueUtils.encodeUnsignedIntegralValue(value.getIndex());
+ out.writeByte(ValueType.VALUE_TYPE.value | ((bytes.length - 1) << 5));
+ out.write(bytes);
+ }
+
+ /** {@inheritDoc} */
+ public int placeValue(int offset) {
+ return offset + EncodedValueUtils.getRequiredBytesForUnsignedIntegralValue(value.getIndex()) + 1;
+ }
+
+ /** {@inheritDoc} */
+ protected int compareValue(EncodedValue o) {
+ TypeEncodedValue other = (TypeEncodedValue)o;
+
+ return value.getIndex() - other.value.getIndex();
+ }
+
+ /** {@inheritDoc} */
+ public ValueType getValueType() {
+ return ValueType.VALUE_TYPE;
+ }
+
+ @Override
+ public int hashCode() {
+ return value.hashCode();
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/EncodedValue/ValueType.java b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/ValueType.java
new file mode 100644
index 00000000..a0729c2f
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/ValueType.java
@@ -0,0 +1,86 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2009 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.EncodedValue;
+
+import org.jf.dexlib.Util.SparseArray;
+
+public enum ValueType {
+
+ VALUE_BYTE((byte) 0x00),
+ VALUE_SHORT((byte) 0x02),
+ VALUE_CHAR((byte) 0x03),
+ VALUE_INT((byte) 0x04),
+ VALUE_LONG((byte) 0x06),
+ VALUE_FLOAT((byte) 0x10),
+ VALUE_DOUBLE((byte) 0x11),
+ VALUE_STRING((byte) 0x17),
+ VALUE_TYPE((byte) 0x18),
+ VALUE_FIELD((byte) 0x19),
+ VALUE_METHOD((byte) 0x1a),
+ VALUE_ENUM((byte) 0x1b),
+ VALUE_ARRAY((byte) 0x1c),
+ VALUE_ANNOTATION((byte) 0x1d),
+ VALUE_NULL((byte) 0x1e),
+ VALUE_BOOLEAN((byte) 0x1f);
+
+ /**
+ * A map to facilitate looking up a ValueType
by byte value
+ */
+ private final static SparseArrayvalueTypeIntegerMap
object */
+ valueTypeIntegerMap = new SparseArrayFieldIdItem
+ * @param dexFile The DexFile
that this item belongs to
+ */
+ protected FieldIdItem(DexFile dexFile) {
+ super(dexFile);
+ }
+
+ /**
+ * Creates a new FieldIdItem
for the given class, type and name
+ * @param dexFile The DexFile
that this item belongs to
+ * @param classType the class that the field is a member of
+ * @param fieldType the type of the field
+ * @param fieldName the name of the field
+ */
+ private FieldIdItem(DexFile dexFile, TypeIdItem classType, TypeIdItem fieldType, StringIdItem fieldName) {
+ this(dexFile);
+
+ assert classType.dexFile == dexFile;
+ assert fieldType.dexFile == dexFile;
+ assert fieldName.dexFile == dexFile;
+
+ this.classType = classType;
+ this.fieldType = fieldType;
+ this.fieldName = fieldName;
+ }
+
+ /**
+ * Returns a FieldIdItem
for the given values, and that has been interned into
+ * the given DexFile
+ * @param dexFile The DexFile
that this item belongs to
+ * @param classType the class that the field is a member of
+ * @param fieldType the type of the field
+ * @param fieldName the name of the field
+ * @return a FieldIdItem
for the given values, and that has been interned into
+ * the given DexFile
+ */
+ public static FieldIdItem getInternedFieldIdItem(DexFile dexFile, TypeIdItem classType, TypeIdItem fieldType,
+ StringIdItem fieldName) {
+ FieldIdItem fieldIdItem = new FieldIdItem(dexFile, classType, fieldType, fieldName);
+ return dexFile.FieldIdsSection.intern(fieldIdItem);
+ }
+
+ /** {@inheritDoc} */
+ protected void readItem(Input in, ReadContext readContext) {
+ classType = dexFile.TypeIdsSection.getItemByIndex(in.readShort());
+ fieldType = dexFile.TypeIdsSection.getItemByIndex(in.readShort());
+ fieldName = dexFile.StringIdsSection.getItemByIndex(in.readInt());
+ }
+
+ /** {@inheritDoc} */
+ protected int placeItem(int offset) {
+ return offset + 8;
+ }
+
+ /** {@inheritDoc} */
+ protected void writeItem(AnnotatedOutput out) {
+ if (out.annotates()) {
+ out.annotate(2, classType.getConciseIdentity());
+ out.annotate(2, fieldType.getConciseIdentity());
+ out.annotate(4, fieldName.getConciseIdentity());
+ }
+
+ out.writeShort(classType.getIndex());
+ out.writeShort(fieldType.getIndex());
+ out.writeInt(fieldName.getIndex());
+ }
+
+ /** {@inheritDoc} */
+ public ItemType getItemType() {
+ return ItemType.TYPE_FIELD_ID_ITEM;
+ }
+
+ /** {@inheritDoc} */
+ public String getConciseIdentity() {
+ String parentClass = classType.getTypeDescriptor();
+ //strip off the leading L and trailing ;
+ parentClass = parentClass.substring(1, parentClass.length() - 1);
+
+ return parentClass + "/" + fieldName.getStringValue() +
+ ":" + fieldType.getTypeDescriptor();
+ }
+
+ /** {@inheritDoc} */
+ public int compareTo(FieldIdItem o) {
+ int result = classType.compareTo(o.classType);
+ if (result != 0) {
+ return result;
+ }
+
+ result = fieldName.compareTo(o.fieldName);
+ if (result != 0) {
+ return result;
+ }
+
+ return fieldType.compareTo(o.fieldType);
+ }
+
+ /**
+ * @return the class that this field is a member of
+ */
+ public TypeIdItem getContainingClass() {
+ return classType;
+ }
+
+ /**
+ * @return the type of this field
+ */
+ public TypeIdItem getFieldType() {
+ return fieldType;
+ }
+
+ /**
+ * @return the field name
+ */
+ public StringIdItem getFieldName() {
+ return fieldName;
+ }
+
+ /**
+ * calculate and cache the hashcode
+ */
+ private void calcHashCode() {
+ hashCode = classType.hashCode();
+ hashCode = 31 * hashCode + fieldType.hashCode();
+ hashCode = 31 * hashCode + fieldName.hashCode();
+ }
+
+ @Override
+ public int hashCode() {
+ //there's a small possibility that the actual hash code will be 0. If so, we'll
+ //just end up recalculating it each time
+ if (hashCode == 0)
+ calcHashCode();
+ return hashCode;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this==o) {
+ return true;
+ }
+ if (o==null || !this.getClass().equals(o.getClass())) {
+ return false;
+ }
+
+ //This assumes that the referenced items have been interned in both objects.
+ //This is a valid assumption because all outside code must use the static
+ //"getInterned..." style methods to make new items, and any item created
+ //internally is guaranteed to be interned
+ FieldIdItem other = (FieldIdItem)o;
+ return (classType == other.classType &&
+ fieldType == other.fieldType &&
+ fieldName == other.fieldName);
+ }
+}
\ No newline at end of file
diff --git a/dexlib/src/main/java/org/jf/dexlib/HeaderItem.java b/dexlib/src/main/java/org/jf/dexlib/HeaderItem.java
new file mode 100644
index 00000000..14b1b81b
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/HeaderItem.java
@@ -0,0 +1,208 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2009 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib;
+
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.Input;
+
+import java.nio.charset.Charset;
+
+public class HeaderItem extends ItemHeaderItem
+ * @param dexFile The DexFile
containing this HeaderItem
+ */
+ protected HeaderItem(final DexFile dexFile) {
+ super(dexFile);
+ }
+
+ /** {@inheritDoc} */
+ protected void readItem(Input in, ReadContext readContext) {
+ byte[] expectedMagic = MAGIC.getBytes(Charset.forName("US-ASCII"));
+ byte[] readMagic = in.readBytes(8);
+
+ for (int i=0; i<8; i++) {
+ if (expectedMagic[i] != readMagic[i]) {
+ throw new RuntimeException("The magic value is not the expected value");
+ }
+ }
+
+ in.readBytes(20); //checksum
+ in.readInt(); //signature
+ in.readInt(); //filesize
+ if (in.readInt() != HEADER_SIZE) {
+ throw new RuntimeException("The header size is not the expected value (0x70)");
+ }
+
+ int endianTag = in.readInt();
+ if (endianTag == BIG_ENDIAN) {
+ throw new RuntimeException("This dex file is big endian. Only little endian is currently supported.");
+ } else if (endianTag != LITTLE_ENDIAN) {
+ throw new RuntimeException("The endian tag is not 0x12345678 or 0x78563412");
+ }
+
+ //link_size
+ if (in.readInt() != 0) {
+ throw new RuntimeException("This dex file has a link section, which is not supported");
+ }
+
+ //link_off
+ if (in.readInt() != 0) {
+ throw new RuntimeException("This dex file has a link section, which is not supported");
+ }
+
+ int sectionSize;
+ int sectionOffset;
+
+ //map_offset
+ sectionOffset = in.readInt();
+ readContext.addSection(ItemType.TYPE_MAP_LIST, 1, sectionOffset);
+
+ //string_id_item
+ sectionSize = in.readInt();
+ sectionOffset = in.readInt();
+ readContext.addSection(ItemType.TYPE_STRING_ID_ITEM, sectionSize, sectionOffset);
+
+ //type_id_item
+ sectionSize = in.readInt();
+ sectionOffset = in.readInt();
+ readContext.addSection(ItemType.TYPE_TYPE_ID_ITEM, sectionSize, sectionOffset);
+
+ //proto_id_item
+ sectionSize = in.readInt();
+ sectionOffset = in.readInt();
+ readContext.addSection(ItemType.TYPE_PROTO_ID_ITEM, sectionSize, sectionOffset);
+
+ //field_id_item
+ sectionSize = in.readInt();
+ sectionOffset = in.readInt();
+ readContext.addSection(ItemType.TYPE_FIELD_ID_ITEM, sectionSize, sectionOffset);
+
+ //method_id_item
+ sectionSize = in.readInt();
+ sectionOffset = in.readInt();
+ readContext.addSection(ItemType.TYPE_METHOD_ID_ITEM, sectionSize, sectionOffset);
+
+ //class_data_item
+ sectionSize = in.readInt();
+ sectionOffset = in.readInt();
+ readContext.addSection(ItemType.TYPE_CLASS_DEF_ITEM, sectionSize, sectionOffset);
+
+ in.readInt(); //data_size
+ in.readInt(); //data_off
+ }
+
+ /** {@inheritDoc} */
+ protected int placeItem(int offset) {
+ return HEADER_SIZE;
+ }
+
+ /** {@inheritDoc} */
+ protected void writeItem(AnnotatedOutput out) {
+ if (out.annotates()) {
+ //TODO: add human readable representations of the underlying data where appropriate
+ out.annotate(8, "magic");
+ out.annotate(4, "checksum");
+ out.annotate(20, "signature");
+ out.annotate(4, "file_size");
+ out.annotate(4, "header_size");
+ out.annotate(4, "endian_tag");
+ out.annotate(4, "link_size");
+ out.annotate(4, "link_off");
+ out.annotate(4, "map_off");
+ out.annotate(4, "string_ids_size");
+ out.annotate(4, "string_ids_off");
+ out.annotate(4, "type_ids_size");
+ out.annotate(4, "type_ids_off");
+ out.annotate(4, "proto_ids_size");
+ out.annotate(4, "proto_ids_off");
+ out.annotate(4, "field_ids_size");
+ out.annotate(4, "field_ids_off");
+ out.annotate(4, "method_ids_size");
+ out.annotate(4, "method_ids_off");
+ out.annotate(4, "class_defs_size");
+ out.annotate(4, "class_defs_off");
+ out.annotate(4, "data_size");
+ out.annotate(4, "data_off");
+ }
+
+ out.write(MAGIC.getBytes(Charset.forName("US-ASCII")));
+ out.writeInt(0); //checksum
+ out.write(new byte[20]); //signature
+ out.writeInt(dexFile.getFileSize());
+ out.writeInt(HEADER_SIZE);
+ out.writeInt(LITTLE_ENDIAN);
+ out.writeInt(0); //link_size
+ out.writeInt(0); //link_off
+ out.writeInt(dexFile.MapItem.getOffset());
+ out.writeInt(dexFile.StringIdsSection.getItems().size());
+ out.writeInt(dexFile.StringIdsSection.getOffset());
+ out.writeInt(dexFile.TypeIdsSection.getItems().size());
+ out.writeInt(dexFile.TypeIdsSection.getOffset());
+ out.writeInt(dexFile.ProtoIdsSection.getItems().size());
+ out.writeInt(dexFile.ProtoIdsSection.getOffset());
+ out.writeInt(dexFile.FieldIdsSection.getItems().size());
+ out.writeInt(dexFile.FieldIdsSection.getOffset());
+ out.writeInt(dexFile.MethodIdsSection.getItems().size());
+ out.writeInt(dexFile.MethodIdsSection.getOffset());
+ out.writeInt(dexFile.ClassDefsSection.getItems().size());
+ out.writeInt(dexFile.ClassDefsSection.getOffset());
+ out.writeInt(dexFile.getDataSize());
+ out.writeInt(dexFile.getDataOffset());
+ }
+
+ /** {@inheritDoc} */
+ public ItemType getItemType() {
+ return ItemType.TYPE_HEADER_ITEM;
+ }
+
+ /** {@inheritDoc} */
+ public String getConciseIdentity() {
+ return "header_item";
+ }
+
+ /** {@inheritDoc} */
+ public int compareTo(HeaderItem o) {
+ //there is only 1 header item
+ return 0;
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/IndexedSection.java b/dexlib/src/main/java/org/jf/dexlib/IndexedSection.java
new file mode 100644
index 00000000..35f0d295
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/IndexedSection.java
@@ -0,0 +1,67 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2009 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib;
+
+import org.jf.dexlib.Util.Input;
+
+public class IndexedSectionDexFile
that this section belongs to
+ * @param itemType The itemType that this section will hold
+ */
+ public IndexedSection(DexFile dexFile, ItemType itemType) {
+ super(dexFile, itemType);
+ }
+
+ /** {@inheritDoc} */
+ protected void readItems(Input in, ReadContext readContext) {
+ for (int i = 0; i < items.size(); i++) {
+ T item = (T)ItemFactory.makeItem(ItemType, DexFile);
+ items.set(i, item);
+ item.readFrom(in, i, readContext);
+ }
+ }
+
+ /**
+ * Gets the item at the specified index in this section
+ * @param index the index of the item to get
+ * @return the item at the specified index in this section
+ * @throws IndexOutOfBoundsException if index is outside the bounds of this section
+ */
+ public T getItemByIndex(int index) {
+ if (index == -1) {
+ return null;
+ }
+
+ //if index is out of bounds, just let it throw an exception
+ return items.get(index);
+ }
+}
\ No newline at end of file
diff --git a/dexlib/src/main/java/org/jf/dexlib/Item.java b/dexlib/src/main/java/org/jf/dexlib/Item.java
new file mode 100644
index 00000000..e029a3e7
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Item.java
@@ -0,0 +1,185 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2009 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib;
+
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.Input;
+import org.jf.dexlib.Util.AlignmentUtils;
+
+public abstract class ItemDexFile
+ * @param dexFile the DexFile
that this item is associated with
+ */
+ protected Item(DexFile dexFile) {
+ this.dexFile = dexFile;
+ }
+
+ /**
+ * Read in the item from the given input stream, and initialize the index
+ * @param in the Input
object to read from
+ * @param index the index within the containing section of the item being read in
+ * @param readContext a ReadContext
object to hold information that is
+ * only needed while reading in a file
+ */
+ protected void readFrom(Input in, int index, ReadContext readContext) {
+ assert in.getCursor() % getItemType().ItemAlignment == 0:"The Input cursor is not aligned";
+
+ this.offset = in.getCursor();
+ this.index = index;
+ this.readItem(in, readContext);
+ }
+
+ /**
+ * Place the item at the given offset and index, and return the offset of the byte following this item
+ * @param offset The offset to place the item at
+ * @param index The index of the item within the containing section
+ * @return The offset of the byte following this item
+ */
+ protected int placeAt(int offset, int index) {
+ assert offset % getItemType().ItemAlignment == 0:"The offset is not aligned";
+
+ this.offset = offset;
+ this.index = index;
+ return this.placeItem(offset);
+ }
+
+ /**
+ * Write and annotate this item to the output stream
+ * @param out The output stream to write and annotate to
+ */
+ protected void writeTo(AnnotatedOutput out) {
+ assert out.getCursor() % getItemType().ItemAlignment == 0:"The Output cursor is not aligned";
+
+ if (out.getCursor() != offset) {
+ throw new RuntimeException("Item was placed at offset 0x" + Integer.toHexString(offset) +
+ "but is being written to offset 0x" + Integer.toHexString(out.getCursor()));
+ }
+
+ if (out.annotates()) {
+ out.annotate(0, "[0x" + Integer.toHexString(index) + "] " + this.getItemType().TypeName);
+ }
+
+ out.indent();
+ writeItem(out);
+ out.deindent();
+ }
+
+ /**
+ * Returns a human readable form of this item
+ * @return a human readable form of this item
+ */
+ public String toString() {
+ return getConciseIdentity();
+ }
+
+ /**
+ * The method in the concrete item subclass that actually reads in the data for the item
+ *
+ * The logic in this method can assume that the given Input object is valid and is
+ * aligned as neccessary.
+ *
+ * This method is for internal use only
+ * @param in the Input
object to read from
+ * @param readContext a ReadContext
object to hold information that is
+ * only needed while reading in a file
+ */
+ protected abstract void readItem(Input in, ReadContext readContext);
+
+ /**
+ * The method should finalize the layout of the item and return the offset of the byte
+ * immediately following the item.
+ *
+ * The implementation of this method can assume that the offset argument has already been
+ * aligned based on the item's alignment requirements
+ *
+ * This method is for internal use only
+ * @param offset the (pre-aligned) offset to place the item at
+ * @return the size of the item, in bytes
+ */
+ protected abstract int placeItem(int offset);
+
+ /**
+ * The method in the concrete item subclass that actually writes and annotates the data
+ * for the item.
+ *
+ * The logic in this method can assume that the given Output object is valid and is
+ * aligned as neccessary
+ *
+ * @param out The AnnotatedOutput
object to write/annotate to
+ */
+ protected abstract void writeItem(AnnotatedOutput out);
+
+ /**
+ * @return An ItemType enum that represents the item type of this item
+ */
+ public abstract ItemType getItemType();
+
+ /**
+ * @return A concise (human-readable) string value that conveys the identity of this item
+ */
+ public abstract String getConciseIdentity();
+
+
+ /**
+ * @return the offset in the dex file where this item is located
+ */
+ public int getOffset() {
+ return offset;
+ }
+
+ /**
+ * @return the index of this item within the item's containing section
+ */
+ public int getIndex() {
+ return index;
+ }
+
+ /**
+ * @return the DexFile
that contains this item
+ */
+ public DexFile getDexFile() {
+ return dexFile;
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/ItemFactory.java b/dexlib/src/main/java/org/jf/dexlib/ItemFactory.java
new file mode 100644
index 00000000..af33ad38
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/ItemFactory.java
@@ -0,0 +1,71 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2009 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib;
+
+class ItemFactory {
+ protected static Item makeItem(ItemType itemType, DexFile dexFile) {
+ switch (itemType) {
+ case TYPE_STRING_ID_ITEM:
+ return new StringIdItem(dexFile);
+ case TYPE_TYPE_ID_ITEM:
+ return new TypeIdItem(dexFile);
+ case TYPE_PROTO_ID_ITEM:
+ return new ProtoIdItem(dexFile);
+ case TYPE_FIELD_ID_ITEM:
+ return new FieldIdItem(dexFile);
+ case TYPE_METHOD_ID_ITEM:
+ return new MethodIdItem(dexFile);
+ case TYPE_CLASS_DEF_ITEM:
+ return new ClassDefItem(dexFile);
+ case TYPE_TYPE_LIST:
+ return new TypeListItem(dexFile);
+ case TYPE_ANNOTATION_SET_REF_LIST:
+ return new AnnotationSetRefList(dexFile);
+ case TYPE_ANNOTATION_SET_ITEM:
+ return new AnnotationSetItem(dexFile);
+ case TYPE_CLASS_DATA_ITEM:
+ return new ClassDataItem(dexFile);
+ case TYPE_CODE_ITEM:
+ return new CodeItem(dexFile);
+ case TYPE_STRING_DATA_ITEM:
+ return new StringDataItem(dexFile);
+ case TYPE_DEBUG_INFO_ITEM:
+ return new DebugInfoItem(dexFile);
+ case TYPE_ANNOTATION_ITEM:
+ return new AnnotationItem(dexFile);
+ case TYPE_ENCODED_ARRAY_ITEM:
+ return new EncodedArrayItem(dexFile);
+ case TYPE_ANNOTATIONS_DIRECTORY_ITEM:
+ return new AnnotationDirectoryItem(dexFile);
+ default:
+ assert false;
+ }
+ return null;
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/ItemType.java b/dexlib/src/main/java/org/jf/dexlib/ItemType.java
new file mode 100644
index 00000000..39378c44
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/ItemType.java
@@ -0,0 +1,123 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2009 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib;
+
+import java.util.TreeMap;
+
+/**
+ * Enumeration of all the top-level item types.
+ */
+public enum ItemType {
+ TYPE_HEADER_ITEM( 0x0000, 17, 4, "header_item"),
+ TYPE_STRING_ID_ITEM( 0x0001, 0, 4, "string_id_item"),
+ TYPE_TYPE_ID_ITEM( 0x0002, 1, 4, "type_id_item"),
+ TYPE_PROTO_ID_ITEM( 0x0003, 2, 4, "proto_id_item"),
+ TYPE_FIELD_ID_ITEM( 0x0004, 3, 4, "field_id_item"),
+ TYPE_METHOD_ID_ITEM( 0x0005, 4, 4, "method_id_item"),
+ TYPE_CLASS_DEF_ITEM( 0x0006, 5, 4, "class_def_item"),
+ TYPE_MAP_LIST( 0x1000, 16, 4, "map_list"),
+ TYPE_TYPE_LIST( 0x1001, 6, 4, "type_list"),
+ TYPE_ANNOTATION_SET_REF_LIST( 0x1002, 7, 4, "annotation_set_ref_list"),
+ TYPE_ANNOTATION_SET_ITEM( 0x1003, 8, 4, "annotation_set_item"),
+ TYPE_CLASS_DATA_ITEM( 0x2000, 9, 1, "class_data_item"),
+ TYPE_CODE_ITEM( 0x2001, 10, 4, "code_item"),
+ TYPE_STRING_DATA_ITEM( 0x2002, 11, 1, "string_data_item"),
+ TYPE_DEBUG_INFO_ITEM( 0x2003, 12, 1, "debug_info_item"),
+ TYPE_ANNOTATION_ITEM( 0x2004, 13, 1, "annotation_item"),
+ TYPE_ENCODED_ARRAY_ITEM( 0x2005, 14, 1, "encoded_array_item"),
+ TYPE_ANNOTATIONS_DIRECTORY_ITEM(0x2006, 15, 4, "annotations_directory_item");
+
+ /** A map to facilitate looking up an ItemType
by ordinal */
+ private final static TreeMapitemTypeIntegerMap
object */
+ static {
+ itemTypeIntegerMap = new TreeMapMapItem
+ * @param dexFile The DexFile
that this item belongs to
+ */
+ protected MapItem(final DexFile dexFile) {
+ super(dexFile);
+ }
+
+ /** {@inheritDoc} */
+ protected int placeItem(int offset) {
+ Section[] sections = dexFile.getOrderedSections();
+ //the list returned by getOrderedSections doesn't contain the header
+ //or map section, so add 2 to the length
+ return offset + (sections.length + 2) * 12;
+ }
+
+ /** {@inheritDoc} */
+ protected void readItem(Input in, ReadContext readContext) {
+ int size = in.readInt();
+
+ for (int i=0; iMethodIdItem
+ * @param dexFile The DexFile
that this item belongs to
+ */
+ protected MethodIdItem(DexFile dexFile) {
+ super(dexFile);
+ }
+
+ /**
+ * Creates a new MethodIdItem
for the given class, type and name
+ * @param dexFile The DexFile
that this item belongs to
+ * @param classType the class that the method is a member of
+ * @param methodPrototype the type of the method
+ * @param methodName the name of the method
+ */
+ private MethodIdItem(DexFile dexFile, TypeIdItem classType, ProtoIdItem methodPrototype, StringIdItem methodName) {
+ this(dexFile);
+ this.classType = classType;
+ this.methodPrototype = methodPrototype;
+ this.methodName = methodName;
+ }
+
+ /**
+ * Returns a MethodIdItem
for the given values, and that has been interned into
+ * the given DexFile
+ * @param dexFile The DexFile
that this item belongs to
+ * @param classType the class that the method is a member of
+ * @param methodPrototype the type of the method
+ * @param methodName the name of the method
+ * @return a MethodIdItem
for the given values, and that has been interned into
+ * the given DexFile
+ */
+ public static MethodIdItem getInternedMethodIdItem(DexFile dexFile, TypeIdItem classType,
+ ProtoIdItem methodPrototype, StringIdItem methodName) {
+ MethodIdItem methodIdItem = new MethodIdItem(dexFile, classType, methodPrototype, methodName);
+ return dexFile.MethodIdsSection.intern(methodIdItem);
+ }
+
+ /** {@inheritDoc} */
+ protected void readItem(Input in, ReadContext readContext) {
+ classType = dexFile.TypeIdsSection.getItemByIndex(in.readShort());
+ methodPrototype = dexFile.ProtoIdsSection.getItemByIndex(in.readShort());
+ methodName = dexFile.StringIdsSection.getItemByIndex(in.readInt());
+ }
+
+ /** {@inheritDoc} */
+ protected int placeItem(int offset) {
+ return offset + 8;
+ }
+
+ /** {@inheritDoc} */
+ protected void writeItem(AnnotatedOutput out) {
+ if (out.annotates()) {
+ out.annotate(2, classType.getConciseIdentity());
+ out.annotate(2, methodPrototype.getConciseIdentity());
+ out.annotate(4, methodName.getConciseIdentity());
+ }
+
+ out.writeShort(classType.getIndex());
+ out.writeShort(methodPrototype.getIndex());
+ out.writeInt(methodName.getIndex());
+ }
+
+ /** {@inheritDoc} */
+ public ItemType getItemType() {
+ return ItemType.TYPE_METHOD_ID_ITEM;
+ }
+
+ /** {@inheritDoc} */
+ public String getConciseIdentity() {
+ return "method_id_item: " + getMethodString();
+ }
+
+ /** {@inheritDoc} */
+ public int compareTo(MethodIdItem o) {
+ int result = classType.compareTo(o.classType);
+ if (result != 0) {
+ return result;
+ }
+
+ result = methodName.compareTo(o.methodName);
+ if (result != 0) {
+ return result;
+ }
+
+ return methodPrototype.compareTo(o.methodPrototype);
+ }
+
+ private String cachedMethodString = null;
+ /**
+ * @return a string formatted like LclassName;->methodName(TTTT..)R
+ */
+ public String getMethodString() {
+ if (cachedMethodString == null) {
+ cachedMethodString = classType.getTypeDescriptor() + "->" + methodName.getStringValue() +
+ methodPrototype.getPrototypeString();
+ }
+ return cachedMethodString;
+ }
+
+ /**
+ * @return the method prototype
+ */
+ public ProtoIdItem getPrototype() {
+ return methodPrototype;
+ }
+
+ /**
+ * @return the name of the method
+ */
+ public StringIdItem getMethodName() {
+ return methodName;
+ }
+
+ /**
+ * @return the class this method is a member of
+ */
+ public TypeIdItem getContainingClass() {
+ return classType;
+ }
+
+ /**
+ * calculate and cache the hashcode
+ */
+ private void calcHashCode() {
+ hashCode = classType.hashCode();
+ hashCode = 31 * hashCode + methodPrototype.hashCode();
+ hashCode = 31 * hashCode + methodName.hashCode();
+ }
+
+ @Override
+ public int hashCode() {
+ //there's a small possibility that the actual hash code will be 0. If so, we'll
+ //just end up recalculating it each time
+ if (hashCode == 0)
+ calcHashCode();
+ return hashCode;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this==o) {
+ return true;
+ }
+ if (o==null || !this.getClass().equals(o.getClass())) {
+ return false;
+ }
+
+ //This assumes that the referenced items have been interned in both objects.
+ //This is a valid assumption because all outside code must use the static
+ //"getInterned..." style methods to make new items, and any item created
+ //internally is guaranteed to be interned
+ MethodIdItem other = (MethodIdItem)o;
+ return (classType == other.classType &&
+ methodPrototype == other.methodPrototype &&
+ methodName == other.methodName);
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/OffsettedSection.java b/dexlib/src/main/java/org/jf/dexlib/OffsettedSection.java
new file mode 100644
index 00000000..c34137fe
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/OffsettedSection.java
@@ -0,0 +1,83 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2009 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib;
+
+import org.jf.dexlib.Util.Input;
+import org.jf.dexlib.Util.SparseArray;
+import org.jf.dexlib.Util.Hex;
+
+import junit.framework.Assert;
+
+public class OffsettedSectionProtoIdItem
+ * @param dexFile The DexFile
that this item belongs to
+ */
+ protected ProtoIdItem(DexFile dexFile) {
+ super(dexFile);
+ }
+
+ /**
+ * Creates a new ProtoIdItem
with the given values
+ * @param dexFile The DexFile
that this item belongs to
+ * @param returnType the return type
+ * @param parameters a TypeListItem
containing a list of the parameter types
+ */
+ private ProtoIdItem(DexFile dexFile, TypeIdItem returnType, TypeListItem parameters) {
+ this(dexFile);
+
+ String shortyString = returnType.toShorty();
+ if (parameters != null) {
+ shortyString += parameters.getShortyString();
+ }
+ this.shortyDescriptor = StringIdItem.getInternedStringIdItem(dexFile, shortyString);
+ this.returnType = returnType;
+ this.parameters = parameters;
+ }
+
+ /**
+ * Returns a ProtoIdItem
for the given values, and that has been interned into
+ * the given DexFile
+ * @param dexFile The DexFile
that this item belongs to
+ * @param returnType the return type
+ * @param parameters a TypeListItem
containing a list of the parameter types
+ * @return a ProtoIdItem
for the given values, and that has been interned into
+ * the given DexFile
+ */
+ public static ProtoIdItem getInternedProtoIdItem(DexFile dexFile, TypeIdItem returnType, TypeListItem parameters) {
+ ProtoIdItem protoIdItem = new ProtoIdItem(dexFile, returnType, parameters);
+ return dexFile.ProtoIdsSection.intern(protoIdItem);
+ }
+
+ /** {@inheritDoc} */
+ protected void readItem(Input in, ReadContext readContext) {
+ shortyDescriptor = dexFile.StringIdsSection.getItemByIndex(in.readInt());
+ returnType = dexFile.TypeIdsSection.getItemByIndex(in.readInt());
+ parameters = (TypeListItem)readContext.getOffsettedItemByOffset(ItemType.TYPE_TYPE_LIST, in.readInt());
+ }
+
+ /** {@inheritDoc} */
+ protected int placeItem(int offset) {
+ return offset + 12;
+ }
+
+ /** {@inheritDoc} */
+ protected void writeItem(AnnotatedOutput out) {
+ if (out.annotates()) {
+ out.annotate(4, "shorty_descriptor: " + shortyDescriptor.getStringValue());
+ out.annotate(4, "return_type: " + returnType.getTypeDescriptor());
+
+ if (parameters == null) {
+ out.annotate(4, "parameters:");
+ } else {
+ out.annotate(4, "parameters: " + parameters.getTypeListString());
+ }
+ }
+
+ out.writeInt(shortyDescriptor.getIndex());
+ out.writeInt(returnType.getIndex());
+ out.writeInt(parameters == null?0:parameters.getOffset());
+ }
+
+ /** {@inheritDoc} */
+ public ItemType getItemType() {
+ return ItemType.TYPE_PROTO_ID_ITEM;
+ }
+
+ /** {@inheritDoc} */
+ public int compareTo(ProtoIdItem o) {
+ int result = returnType.compareTo(o.returnType);
+ if (result != 0) {
+ return result;
+ }
+
+ if (parameters == null) {
+ if (o.parameters == null) {
+ return 0;
+ }
+ return -1;
+ } else if (o.parameters == null) {
+ return 1;
+ }
+
+ return parameters.compareTo(o.parameters);
+ }
+
+ /** {@inheritDoc} */
+ public String getConciseIdentity() {
+ return "proto_id_item: " + getPrototypeString();
+ }
+
+ private String cachedPrototypeString = null;
+ /**
+ * @return a string in the format (TTTT..)R where TTTT.. are the parameter types and R is the return type
+ */
+ public String getPrototypeString() {
+ if (cachedPrototypeString == null) {
+ StringBuilder sb = new StringBuilder("(");
+ if (parameters != null) {
+ sb.append(parameters.getTypeListString());
+ }
+ sb.append(")");
+ sb.append(returnType.getTypeDescriptor());
+
+ cachedPrototypeString = sb.toString();
+ }
+ return cachedPrototypeString;
+ }
+
+ /**
+ * @return the number of registers required for the parameters of this ProtoIdItem
+ */
+ public int getParameterRegisterCount() {
+ if (parameters == null) {
+ return 0;
+ } else {
+ return parameters.getRegisterCount();
+ }
+ }
+
+ /**
+ * calculate and cache the hashcode
+ */
+ private void calcHashCode() {
+ hashCode = returnType.hashCode();
+ hashCode = 31 * hashCode + parameters.hashCode();
+ }
+
+ @Override
+ public int hashCode() {
+ //there's a small possibility that the actual hash code will be 0. If so, we'll
+ //just end up recalculating it each time
+ if (hashCode == 0)
+ calcHashCode();
+ return hashCode;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this==o) {
+ return true;
+ }
+ if (o==null || !this.getClass().equals(o.getClass())) {
+ return false;
+ }
+
+ //This assumes that the referenced items have been interned in both objects.
+ //This is a valid assumption because all outside code must use the static
+ //"getInterned..." style methods to make new items, and any item created
+ //internally is guaranteed to be interned
+ ProtoIdItem other = (ProtoIdItem)o;
+ return (returnType == other.returnType &&
+ parameters == other.parameters);
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/ReadContext.java b/dexlib/src/main/java/org/jf/dexlib/ReadContext.java
new file mode 100644
index 00000000..5ec8dd83
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/ReadContext.java
@@ -0,0 +1,219 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2009 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib;
+
+import org.jf.dexlib.Util.SparseArray;
+import junit.framework.Assert;
+
+import java.util.List;
+
+
+/**
+ * This class stores context information that is only needed when reading in a dex file
+ * Namely, it handles "pre-creating" items when an item needs to resolve some other item
+ * that it references, and keeps track of those pre-created items, so the corresponding section
+ * for the pre-created items uses them, instead of creating new items
+ */
+class ReadContext {
+ private final DexFile dexFile;
+
+ private SparseArrayDexFile
+ */
+ protected int offset = -1;
+
+ /**
+ * The type of item that this section holds
+ */
+ public final ItemType ItemType;
+
+ /**
+ * The DexFile
that this section belongs to
+ */
+ public final DexFile DexFile;
+
+ /**
+ * Create a new section
+ * @param dexFile The DexFile
that this section belongs to
+ * @param itemType The itemType that this section will hold
+ */
+ protected Section(DexFile dexFile, ItemType itemType) {
+ this.DexFile = dexFile;
+ items = new ArrayListAnnotatedOutput
+ * @param out the AnnotatedOutput
object to write to
+ */
+ protected void writeTo(AnnotatedOutput out) {
+ for (Item item: items) {
+ Assert.assertTrue("This section contains a null item", item != null);
+ out.alignTo(ItemType.ItemAlignment);
+ item.writeTo(out);
+ out.annotate(0, " ");
+ }
+ out.annotate(0, " ");
+ }
+
+ /**
+ * Read the specified number of items from the given Input
object
+ * @param size The number of items to read
+ * @param in The Input
object to read from
+ * @param readContext a ReadContext
object to hold information that is
+ * only needed while reading in a file
+ */
+ protected void readFrom(int size, Input in, ReadContext readContext) {
+ //readItems() expects that the list will already be the correct size, so add null items
+ //until we reach the specified size
+ items.ensureCapacity(size);
+ for (int i = items.size(); i < size; i++) {
+ items.add(null);
+ }
+
+ in.alignTo(ItemType.ItemAlignment);
+ offset = in.getCursor();
+
+ //call the subclass's method that actually reads in the items
+ readItems(in, readContext);
+ }
+
+ /**
+ * This method in the concrete item subclass should read in all the items from the given Input
+ * object, using any pre-created items as applicable (i.e. items that were created prior to reading in the
+ * section, by other items requesting items from this section that they reference by index/offset)
+ * @param in the Input
+ * @param readContext a ReadContext
object to hold information that is
+ * only needed while reading in a file
+ */
+ protected abstract void readItems(Input in, ReadContext readContext);
+
+ /**
+ * Gets the offset where the first item in this section is placed
+ * @return the ofset where the first item in this section is placed
+ */
+ public int getOffset() {
+ return offset;
+ }
+
+ /**
+ * Gets a the items contained in this section as a read-only list
+ * @return A read-only List
object containing the items in this section
+ */
+ public ListStringDataItem
+ * @param dexFile The DexFile
that this item belongs to
+ */
+ protected StringDataItem(DexFile dexFile) {
+ super(dexFile);
+ }
+
+ /**
+ * Creates a new StringDataItem
for the given string
+ * @param dexFile The DexFile
that this item belongs to
+ * @param stringValue The string value that this item represents
+ */
+ private StringDataItem(DexFile dexFile, String stringValue) {
+ super(dexFile);
+
+ this.stringValue = stringValue;
+ }
+
+ /**
+ * Returns a StringDataItem
for the given values, and that has been interned into
+ * the given DexFile
+ * @param dexFile The DexFile
that this item belongs to
+ * @param value The string value that this item represents
+ * @return a StringDataItem
for the given values, and that has been interned into
+ * the given DexFile
+ */
+ public static StringDataItem getInternedStringDataItem(DexFile dexFile, String value) {
+ StringDataItem StringDataItem = new StringDataItem(dexFile, value);
+ return dexFile.StringDataSection.intern(StringDataItem);
+ }
+
+ /** {@inheritDoc} */
+ protected void readItem(Input in, ReadContext readContext) {
+ in.readUnsignedLeb128(); //string length
+ stringValue = Utf8Utils.utf8BytesToString(in.readNullTerminatedBytes());
+ }
+
+ /** {@inheritDoc} */
+ protected int placeItem(int offset) {
+ return offset + 4 + Utf8Utils.stringToUtf8Bytes(stringValue).length + 1;
+ }
+
+ /** {@inheritDoc} */
+ protected void writeItem(AnnotatedOutput out) {
+ byte[] encodedValue = Utf8Utils.stringToUtf8Bytes(stringValue);
+ if (out.annotates()) {
+ out.annotate(stringValue.length(), "string_size");
+ out.annotate(encodedValue.length + 1, "string_data (" + Utf8Utils.escapeString(stringValue) + ")");
+ }
+
+ out.writeUnsignedLeb128(stringValue.length());
+ out.write(encodedValue);
+ out.writeByte(0);
+ }
+
+ /** {@inheritDoc} */
+ public ItemType getItemType() {
+ return ItemType.TYPE_STRING_DATA_ITEM;
+ }
+
+ /** {@inheritDoc} */
+ public String getConciseIdentity() {
+ return "string_data_item: " + Utf8Utils.escapeString(getStringValue());
+ }
+
+ /** {@inheritDoc} */
+ public int compareTo(StringDataItem o) {
+ return getStringValue().compareTo(o.getStringValue());
+ }
+
+ /**
+ * Get the string value of this item as a String
+ * @return the string value of this item as a String
+ */
+ public String getStringValue() {
+ return stringValue;
+ }
+
+ /**
+ * calculate and cache the hashcode
+ */
+ private void calcHashCode() {
+ hashCode = getStringValue().hashCode();
+ }
+
+ @Override
+ public int hashCode() {
+ //there's a small possibility that the actual hash code will be 0. If so, we'll
+ //just end up recalculating it each time
+ if (hashCode == 0)
+ calcHashCode();
+ return hashCode;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this==o) {
+ return true;
+ }
+ if (o==null || !this.getClass().equals(o.getClass())) {
+ return false;
+ }
+
+ //This assumes that the referenced items have been interned in both objects.
+ //This is a valid assumption because all outside code must use the static
+ //"getInterned..." style methods to make new items, and any item created
+ //internally is guaranteed to be interned
+ StringDataItem other = (StringDataItem)o;
+ return getStringValue().equals(other.getStringValue());
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/StringIdItem.java b/dexlib/src/main/java/org/jf/dexlib/StringIdItem.java
new file mode 100644
index 00000000..8847dc24
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/StringIdItem.java
@@ -0,0 +1,154 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2009 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib;
+
+import org.jf.dexlib.Util.Utf8Utils;
+import org.jf.dexlib.Util.Input;
+import org.jf.dexlib.Util.AnnotatedOutput;
+
+public class StringIdItem extends ItemStringIdItem
+ * @param dexFile The DexFile
that this item belongs to
+ */
+ protected StringIdItem(DexFile dexFile) {
+ super(dexFile);
+ }
+
+ /**
+ * Creates a new StringIdItem
for the given StringDataItem
+ * @param dexFile The DexFile
that this item belongs to
+ * @param stringDataItem The StringDataItem
that this StringIdItem
represents
+ */
+ protected StringIdItem(DexFile dexFile, StringDataItem stringDataItem) {
+ super(dexFile);
+ this.stringDataItem = stringDataItem;
+ }
+
+ /**
+ * Creates a new StringIdItem
and associated StringDataItem
+ * for the given String
value
+ * @param dexFile The DexFile
that this item will belong to
+ * @param stringValue The string value that this item represents
+ */
+ private StringIdItem(DexFile dexFile, String stringValue) {
+ this(dexFile, StringDataItem.getInternedStringDataItem(dexFile, stringValue));
+ }
+
+ /**
+ * Returns a StringIdItem
for the given values, and that has been interned into
+ * the given DexFile
+ * @param dexFile The DexFile
that this item will belong to
+ * @param stringValue The string value that this item represents
+ * @return a StringIdItem
for the given values, and that has been interned into
+ * the given DexFile
+ */
+ public static StringIdItem getInternedStringIdItem(DexFile dexFile, String stringValue) {
+ StringIdItem stringIdItem = new StringIdItem(dexFile, stringValue);
+ return dexFile.StringIdsSection.intern(stringIdItem);
+ }
+
+ /** {@inheritDoc} */
+ protected void readItem(Input in, ReadContext readContext) {
+ int stringDataOffset = in.readInt();
+
+ stringDataItem = (StringDataItem)readContext.getOffsettedItemByOffset(ItemType.TYPE_STRING_DATA_ITEM,
+ stringDataOffset);
+ }
+
+ /** {@inheritDoc} */
+ protected int placeItem(int offset) {
+ return offset + 4;
+ }
+
+ /** {@inheritDoc} */
+ protected void writeItem(AnnotatedOutput out) {
+ if (out.annotates()) {
+ out.annotate(4, stringDataItem.getConciseIdentity());
+ }
+
+ out.writeInt(stringDataItem.getOffset());
+ }
+
+ /** {@inheritDoc} */
+ public ItemType getItemType() {
+ return ItemType.TYPE_STRING_ID_ITEM;
+ }
+
+ /** {@inheritDoc} */
+ public String getConciseIdentity() {
+ return "string_id_item: " + Utf8Utils.escapeString(getStringValue());
+ }
+
+ /** {@inheritDoc} */
+ public int compareTo(StringIdItem o) {
+ //sort by the string value
+ return getStringValue().compareTo(o.getStringValue());
+ }
+
+ /**
+ * Get the String
value that this StringIdItem
represents
+ * @return the String
value that this StringIdItem
represents
+ */
+ public String getStringValue() {
+ return stringDataItem.getStringValue();
+ }
+
+ /**
+ * Get the StringDataItem
that this StringIdItem
references
+ * @return the StringDataItem
that this StringIdItem
references
+ */
+ public StringDataItem getStringDataItem() {
+ return stringDataItem;
+ }
+
+ @Override
+ public int hashCode() {
+ return stringDataItem.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this==o) {
+ return true;
+ }
+ if (o==null || !this.getClass().equals(o.getClass())) {
+ return false;
+ }
+
+ //This assumes that the referenced items have been interned in both objects.
+ //This is a valid assumption because all outside code must use the static
+ //"getInterned..." style methods to make new items, and any item created
+ //internally is guaranteed to be interned
+ StringIdItem other = (StringIdItem)o;
+ return stringDataItem == other.stringDataItem;
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/TypeIdItem.java b/dexlib/src/main/java/org/jf/dexlib/TypeIdItem.java
new file mode 100644
index 00000000..34c9da6b
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/TypeIdItem.java
@@ -0,0 +1,163 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2009 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib;
+
+import org.jf.dexlib.Util.Input;
+import org.jf.dexlib.Util.AnnotatedOutput;
+
+public class TypeIdItem extends ItemTypeIdItem
+ * @param dexFile The DexFile
that this item belongs to
+ */
+ protected TypeIdItem(DexFile dexFile) {
+ super(dexFile);
+ }
+
+ /**
+ * Creates a new TypeIdItem
for the given StringIdItem
+ * @param dexFile The DexFile
that this item will belong to
+ * @param typeDescriptor The StringIdItem
containing the type descriptor that
+ * this TypeIdItem
represents
+ */
+ private TypeIdItem(DexFile dexFile, StringIdItem typeDescriptor) {
+ super(dexFile);
+ this.typeDescriptor = typeDescriptor;
+ }
+
+ /**
+ * Returns a TypeIdItem
for the given values, and that has been interned into
+ * the given DexFile
+ * @param dexFile The DexFile
that this item will belong to
+ * @param typeDescriptor The StringIdItem
containing the type descriptor that
+ * @return a TypeIdItem
for the given values, and that has been interned into
+ * the given DexFile
+ */
+ public static TypeIdItem getInternedTypeIdItem(DexFile dexFile, StringIdItem typeDescriptor) {
+ TypeIdItem typeIdItem = new TypeIdItem(dexFile, typeDescriptor);
+ return dexFile.TypeIdsSection.intern(typeIdItem);
+ }
+
+ /** {@inheritDoc} */
+ protected void readItem(Input in, ReadContext readContext) {
+ int stringIdIndex = in.readInt();
+ this.typeDescriptor = dexFile.StringIdsSection.getItemByIndex(stringIdIndex);
+ }
+
+ /** {@inheritDoc} */
+ protected int placeItem(int offset) {
+ return offset + 4;
+ }
+
+ /** {@inheritDoc} */
+ protected void writeItem(AnnotatedOutput out) {
+ if (out.annotates()) {
+ out.annotate(4, typeDescriptor.getConciseIdentity());
+ }
+
+ out.writeInt(typeDescriptor.getIndex());
+ }
+
+ /** {@inheritDoc} */
+ public ItemType getItemType() {
+ return ItemType.TYPE_TYPE_ID_ITEM;
+ }
+
+ /** {@inheritDoc} */
+ public String getConciseIdentity() {
+ return "type_id_item: " + getTypeDescriptor();
+ }
+
+ /** {@inheritDoc} */
+ public int compareTo(TypeIdItem o) {
+ //sort by the index of the StringIdItem
+ return typeDescriptor.compareTo(o.typeDescriptor);
+ }
+
+ /**
+ * Returns the type descriptor as a String
for this type
+ * @return the type descriptor as a String
for this type
+ */
+ public String getTypeDescriptor() {
+ return typeDescriptor.getStringValue();
+ }
+
+ /**
+ * Returns the "shorty" representation of this type, used to create the shorty prototype string for a method
+ * @return the "shorty" representation of this type, used to create the shorty prototype string for a method
+ */
+ public String toShorty() {
+ String type = getTypeDescriptor();
+ if (type.length() > 1) {
+ return "L";
+ } else {
+ return type;
+ }
+ }
+
+ /**
+ * Calculates the number of 2-byte registers that an instance of this type requires
+ * @return The number of 2-byte registers that an instance of this type requires
+ */
+ public int getRegisterCount() {
+ String type = this.getTypeDescriptor();
+ /** Only the long and double primitive types are 2 words,
+ * everything else is a single word
+ */
+ if (type.equals("J") || type.equals("D")) {
+ return 2;
+ } else {
+ return 1;
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return typeDescriptor.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this==o) {
+ return true;
+ }
+ if (o==null || !this.getClass().equals(o.getClass())) {
+ return false;
+ }
+
+ //This assumes that the referenced items have been interned in both objects.
+ //This is a valid assumption because all outside code must use the static
+ //"getInterned..." style methods to make new items, and any item created
+ //internally is guaranteed to be interned
+ TypeIdItem other = (TypeIdItem)o;
+ return typeDescriptor == other.typeDescriptor;
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/TypeListItem.java b/dexlib/src/main/java/org/jf/dexlib/TypeListItem.java
new file mode 100644
index 00000000..9ded7807
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/TypeListItem.java
@@ -0,0 +1,255 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2009 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib;
+
+import org.jf.dexlib.Util.Input;
+import org.jf.dexlib.Util.AnnotatedOutput;
+
+import java.util.List;
+
+public class TypeListItem extends ItemTypeListItem
+ * @param dexFile The DexFile
that this item belongs to
+ */
+ protected TypeListItem(DexFile dexFile) {
+ super(dexFile);
+ }
+
+ /**
+ * Creates a new TypeListItem
for the given string
+ * @param dexFile The DexFile
that this item belongs to
+ * @param typeList A list of the types that this TypeListItem
represents
+ */
+ private TypeListItem(DexFile dexFile, ListTypeListItem
for the given values, and that has been interned into
+ * the given DexFile
+ * @param dexFile The DexFile
that this item belongs to
+ * @param typeList A list of the types that this TypeListItem
represents
+ * @return a TypeListItem
for the given values, and that has been interned into
+ * the given DexFile
+ */
+ public static TypeListItem getInternedTypeListItem(DexFile dexFile, ListTypeListItem
+ */
+ public int getRegisterCount() {
+ int wordCount = 0;
+ for (TypeIdItem typeIdItem: typeList) {
+ wordCount += typeIdItem.getRegisterCount();
+ }
+ return wordCount;
+ }
+
+ private String cachedTypeListString = null;
+ /**
+ * @return a string consisting of the type descriptors in this TypeListItem
+ * that are directly concatenated together
+ */
+ public String getTypeListString() {
+
+ if (cachedTypeListString == null) {
+ StringBuilder sb = new StringBuilder();
+
+ for (TypeIdItem typeIdItem: typeList) {
+ sb.append(typeIdItem.getTypeDescriptor());
+ }
+ cachedTypeListString = sb.toString();
+ }
+ return cachedTypeListString;
+ }
+
+ /**
+ * @return a string consisting of the shorty form of the type descriptors in this
+ * TypeListItem
that are directly concatenated together
+ */
+ public String getShortyString() {
+ StringBuilder sb = new StringBuilder();
+ for (TypeIdItem typeIdItem: typeList) {
+ sb.append(typeIdItem.toShorty());
+ }
+ return sb.toString();
+ }
+
+ /**
+ * @param index the index of the TypeIdItem
to get
+ * @return the TypeIdItem
at the given index
+ */
+ public TypeIdItem getTypeIdItem(int index) {
+ return typeList[index];
+ }
+
+ /**
+ * @return the number of types in this TypeListItem
+ */
+ public int getTypeCount() {
+ return typeList.length;
+ }
+
+ /**
+ * @return an array of the TypeIdItems
in this TypeListItem
+ */
+ public TypeIdItem[] getTypes() {
+ return typeList.clone();
+ }
+
+ /**
+ * calculate and cache the hashcode
+ */
+ private void calcHashCode() {
+ int hashCode = 1;
+
+ for (TypeIdItem typeIdItem: typeList) {
+ hashCode = 31 * hashCode + typeIdItem.hashCode();
+ }
+ this.hashCode = hashCode;
+ }
+
+ @Override
+ public int hashCode() {
+ //there's a small possibility that the actual hash code will be 0. If so, we'll
+ //just end up recalculating it each time
+ if (hashCode == 0)
+ calcHashCode();
+ return hashCode;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this==o) {
+ return true;
+ }
+ if (o==null || !this.getClass().equals(o.getClass())) {
+ return false;
+ }
+
+ //This assumes that the referenced items have been interned in both objects.
+ //This is a valid assumption because all outside code must use the static
+ //"getInterned..." style methods to make new items, and any item created
+ //internally is guaranteed to be interned
+ TypeListItem other = (TypeListItem)o;
+ if (typeList.length != other.typeList.length) {
+ return false;
+ }
+
+ for (int i=0; itrue
iff annotations are being kept
+ */
+ public boolean annotates();
+
+ /**
+ * Get whether this instance is intended to keep verbose annotations.
+ * Annotators may use the result of calling this method to inform their
+ * annotation activity.
+ *
+ * @return true
iff annotations are to be verbose
+ */
+ public boolean isVerbose();
+
+ /**
+ * Add an annotation for the subsequent output. Any previously
+ * open annotation will be closed by this call, and the new
+ * annotation marks all subsequent output until another annotation
+ * call.
+ *
+ * @param msg non-null; the annotation message
+ */
+ public void annotate(String msg);
+
+ /**
+ * Add an annotation for a specified amount of subsequent
+ * output. Any previously open annotation will be closed by this
+ * call. If there is already pending annotation from one or more
+ * previous calls to this method, the new call "consumes" output
+ * after all the output covered by the previous calls.
+ *
+ * @param amt >= 0; the amount of output for this annotation to
+ * cover
+ * @param msg non-null; the annotation message
+ */
+ public void annotate(int amt, String msg);
+
+ /**
+ * End the most recent annotation. Subsequent output will be unannotated,
+ * until the next call to {@link #annotate}.
+ */
+ public void endAnnotation();
+
+ /**
+ * Get the maximum width of the annotated output. This is advisory:
+ * Implementations of this interface are encouraged to deal with too-wide
+ * output, but annotaters are encouraged to attempt to avoid exceeding
+ * the indicated width.
+ *
+ * @return >= 1; the maximum width
+ */
+ public int getAnnotationWidth();
+
+ public void setIndentAmount(int indentAmount);
+ public void indent();
+ public void deindent();
+}
\ No newline at end of file
diff --git a/dexlib/src/main/java/org/jf/dexlib/Util/ArrayUtils.java b/dexlib/src/main/java/org/jf/dexlib/Util/ArrayUtils.java
new file mode 100644
index 00000000..c612f213
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Util/ArrayUtils.java
@@ -0,0 +1,74 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2009 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Util;
+
+import java.lang.reflect.Array;
+import java.util.TreeMap;
+import java.util.Collections;
+import java.util.Arrays;
+import java.util.Comparator;
+
+public class ArrayUtils {
+ /**
+ * Utility method to sort two related arrays - that is, two arrays where the elements are related to each other
+ * by their position in the array. i.e. firstArray[0] is related to secondArray[0], firstArray[1] is related to
+ * secondArray[1], and so on. The first array is sorted based on its implementation of Comparable, and the 2nd
+ * array is sorted by it's related item in the first array, so that the same elements are still related to each
+ * other after the sort
+ * @param firstArray The first array, which contains the values to sort
+ * @param secondArray The second array, which will be sorted based on the values in the first array
+ * @param The type of element in the first array
+ * @param the type of element in the second array
+ */
+ public static , B> void sortTwoArrays(A[] firstArray, B[] secondArray)
+ {
+ if (firstArray.length != secondArray.length) {
+ throw new RuntimeException("Both arrays must be of the same length");
+ }
+
+ class element
+ {
+ public A first;
+ public B second;
+ }
+
+ element[] elements = new element[firstArray.length];
+
+ Arrays.sort(elements, new Comparator>= 0
; start index of the slice (inclusive) */
+ private final int start;
+
+ /** >= 0, <= bytes.length
; size computed as
+ * end - start
(in the constructor) */
+ private final int size;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param bytes non-null; the underlying array
+ * @param start >= 0
; start index of the slice (inclusive)
+ * @param end >= start, <= bytes.length
; end index of
+ * the slice (exclusive)
+ */
+ public ByteArray(byte[] bytes, int start, int end) {
+ if (bytes == null) {
+ throw new NullPointerException("bytes == null");
+ }
+
+ if (start < 0) {
+ throw new IllegalArgumentException("start < 0");
+ }
+
+ if (end < start) {
+ throw new IllegalArgumentException("end < start");
+ }
+
+ if (end > bytes.length) {
+ throw new IllegalArgumentException("end > bytes.length");
+ }
+
+ this.bytes = bytes;
+ this.start = start;
+ this.size = end - start;
+ }
+
+ /**
+ * Constructs an instance from an entire byte[]
.
+ *
+ * @param bytes non-null; the underlying array
+ */
+ public ByteArray(byte[] bytes) {
+ this(bytes, 0, bytes.length);
+ }
+
+ /**
+ * Gets the size of the array, in bytes.
+ *
+ * @return >= 0; the size
+ */
+ public int size() {
+ return size;
+ }
+
+ /**
+ * Returns a slice (that is, a sub-array) of this instance.
+ *
+ * @param start >= 0
; start index of the slice (inclusive)
+ * @param end >= start, <= size()
; end index of
+ * the slice (exclusive)
+ * @return non-null; the slice
+ */
+ public ByteArray slice(int start, int end) {
+ checkOffsets(start, end);
+ return new ByteArray(bytes, start + this.start, end + this.start);
+ }
+
+ /**
+ * Returns the offset into the given array represented by the given
+ * offset into this instance.
+ *
+ * @param offset offset into this instance
+ * @param bytes non-null; (alleged) underlying array
+ * @return corresponding offset into bytes
+ * @throws IllegalArgumentException thrown if bytes
is
+ * not the underlying array of this instance
+ */
+ public int underlyingOffset(int offset, byte[] bytes) {
+ if (bytes != this.bytes) {
+ throw new IllegalArgumentException("wrong bytes");
+ }
+
+ return start + offset;
+ }
+
+ /**
+ * Gets the signed byte
value at a particular offset.
+ *
+ * @param off >= 0, < size(); offset to fetch
+ * @return signed byte
at that offset
+ */
+ public int getByte(int off) {
+ checkOffsets(off, off + 1);
+ return getByte0(off);
+ }
+
+ /**
+ * Gets the signed short
value at a particular offset.
+ *
+ * @param off >= 0, < (size() - 1); offset to fetch
+ * @return signed short
at that offset
+ */
+ public int getShort(int off) {
+ checkOffsets(off, off + 2);
+ return (getByte0(off) << 8) | getUnsignedByte0(off + 1);
+ }
+
+ /**
+ * Gets the signed int
value at a particular offset.
+ *
+ * @param off >= 0, < (size() - 3); offset to fetch
+ * @return signed int
at that offset
+ */
+ public int getInt(int off) {
+ checkOffsets(off, off + 4);
+ return (getByte0(off) << 24) |
+ (getUnsignedByte0(off + 1) << 16) |
+ (getUnsignedByte0(off + 2) << 8) |
+ getUnsignedByte0(off + 3);
+ }
+
+ /**
+ * Gets the signed long
value at a particular offset.
+ *
+ * @param off >= 0, < (size() - 7); offset to fetch
+ * @return signed int
at that offset
+ */
+ public long getLong(int off) {
+ checkOffsets(off, off + 8);
+ int part1 = (getByte0(off) << 24) |
+ (getUnsignedByte0(off + 1) << 16) |
+ (getUnsignedByte0(off + 2) << 8) |
+ getUnsignedByte0(off + 3);
+ int part2 = (getByte0(off + 4) << 24) |
+ (getUnsignedByte0(off + 5) << 16) |
+ (getUnsignedByte0(off + 6) << 8) |
+ getUnsignedByte0(off + 7);
+
+ return (part2 & 0xffffffffL) | ((long) part1) << 32;
+ }
+
+ /**
+ * Gets the unsigned byte
value at a particular offset.
+ *
+ * @param off >= 0, < size(); offset to fetch
+ * @return unsigned byte
at that offset
+ */
+ public int getUnsignedByte(int off) {
+ checkOffsets(off, off + 1);
+ return getUnsignedByte0(off);
+ }
+
+ /**
+ * Gets the unsigned short
value at a particular offset.
+ *
+ * @param off >= 0, < (size() - 1); offset to fetch
+ * @return unsigned short
at that offset
+ */
+ public int getUnsignedShort(int off) {
+ checkOffsets(off, off + 2);
+ return (getUnsignedByte0(off) << 8) | getUnsignedByte0(off + 1);
+ }
+
+ /**
+ * Copies the contents of this instance into the given raw
+ * byte[]
at the given offset. The given array must be
+ * large enough.
+ *
+ * @param out non-null; array to hold the output
+ * @param offset non-null; index into out
for the first
+ * byte of output
+ */
+ public void getBytes(byte[] out, int offset) {
+ if ((out.length - offset) < size) {
+ throw new IndexOutOfBoundsException("(out.length - offset) < " +
+ "size()");
+ }
+
+ System.arraycopy(bytes, start, out, offset, size);
+ }
+
+ /**
+ * Checks a range of offsets for validity, throwing if invalid.
+ *
+ * @param s start offset (inclusive)
+ * @param e end offset (exclusive)
+ */
+ private void checkOffsets(int s, int e) {
+ if ((s < 0) || (e < s) || (e > size)) {
+ throw new IllegalArgumentException("bad range: " + s + ".." + e +
+ "; actual size " + size);
+ }
+ }
+
+ /**
+ * Gets the signed byte
value at the given offset,
+ * without doing any argument checking.
+ *
+ * @param off offset to fetch
+ * @return byte at that offset
+ */
+ private int getByte0(int off) {
+ return bytes[start + off];
+ }
+
+ /**
+ * Gets the unsigned byte
value at the given offset,
+ * without doing any argument checking.
+ *
+ * @param off offset to fetch
+ * @return byte at that offset
+ */
+ private int getUnsignedByte0(int off) {
+ return bytes[start + off] & 0xff;
+ }
+}
\ No newline at end of file
diff --git a/dexlib/src/main/java/org/jf/dexlib/Util/ByteArrayAnnotatedOutput.java b/dexlib/src/main/java/org/jf/dexlib/Util/ByteArrayAnnotatedOutput.java
new file mode 100644
index 00000000..6edcce54
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Util/ByteArrayAnnotatedOutput.java
@@ -0,0 +1,682 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2009 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Util;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.ArrayList;
+
+/**
+ * Implementation of {@link AnnotatedOutput} which stores the written data
+ * into a byte[]
.
+ *
+ * Note: As per the {@link Output} interface, multi-byte
+ * writes all use little-endian order.
+ */
+public final class ByteArrayAnnotatedOutput
+ implements AnnotatedOutput {
+ /** default size for stretchy instances */
+ private static final int DEFAULT_SIZE = 1000;
+
+ /**
+ * whether the instance is stretchy, that is, whether its array
+ * may be resized to increase capacity
+ */
+ private final boolean stretchy;
+
+ /** non-null; the data itself */
+ private byte[] data;
+
+ /** >= 0; current output cursor */
+ private int cursor;
+
+ /** whether annotations are to be verbose */
+ private boolean verbose;
+
+ /**
+ * null-ok; list of annotations, or null
if this instance
+ * isn't keeping them
+ */
+ private ArrayList annotations;
+
+ /** >= 40 (if used); the desired maximum annotation width */
+ private int annotationWidth;
+
+ /**
+ * >= 8 (if used); the number of bytes of hex output to use
+ * in annotations
+ */
+ private int hexCols;
+
+ private int currentIndent = 0;
+ private int indentAmount = 2;
+
+ /**
+ * Constructs an instance with a fixed maximum size. Note that the
+ * given array is the only one that will be used to store data. In
+ * particular, no reallocation will occur in order to expand the
+ * capacity of the resulting instance. Also, the constructed
+ * instance does not keep annotations by default.
+ *
+ * @param data non-null; data array to use for output
+ */
+ public ByteArrayAnnotatedOutput(byte[] data) {
+ this(data, false);
+ }
+
+ /**
+ * Constructs a "stretchy" instance. The underlying array may be
+ * reallocated. The constructed instance does not keep annotations
+ * by default.
+ */
+ public ByteArrayAnnotatedOutput() {
+ this(new byte[DEFAULT_SIZE], true);
+ }
+
+ /**
+ * Internal constructor.
+ *
+ * @param data non-null; data array to use for output
+ * @param stretchy whether the instance is to be stretchy
+ */
+ private ByteArrayAnnotatedOutput(byte[] data, boolean stretchy) {
+ if (data == null) {
+ throw new NullPointerException("data == null");
+ }
+
+ this.stretchy = stretchy;
+ this.data = data;
+ this.cursor = 0;
+ this.verbose = false;
+ this.annotations = null;
+ this.annotationWidth = 0;
+ this.hexCols = 0;
+ }
+
+ /**
+ * Gets the underlying byte[]
of this instance, which
+ * may be larger than the number of bytes written
+ *
+ * @see #toByteArray
+ *
+ * @return non-null; the byte[]
+ */
+ public byte[] getArray() {
+ return data;
+ }
+
+ /**
+ * Constructs and returns a new byte[]
that contains
+ * the written contents exactly (that is, with no extra unwritten
+ * bytes at the end).
+ *
+ * @see #getArray
+ *
+ * @return non-null; an appropriately-constructed array
+ */
+ public byte[] toByteArray() {
+ byte[] result = new byte[cursor];
+ System.arraycopy(data, 0, result, 0, cursor);
+ return result;
+ }
+
+ /** {@inheritDoc} */
+ public int getCursor() {
+ return cursor;
+ }
+
+ /** {@inheritDoc} */
+ public void assertCursor(int expectedCursor) {
+ if (cursor != expectedCursor) {
+ throw new ExceptionWithContext("expected cursor " +
+ expectedCursor + "; actual value: " + cursor);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void writeByte(int value) {
+ int writeAt = cursor;
+ int end = writeAt + 1;
+
+ if (stretchy) {
+ ensureCapacity(end);
+ } else if (end > data.length) {
+ throwBounds();
+ return;
+ }
+
+ data[writeAt] = (byte) value;
+ cursor = end;
+ }
+
+ /** {@inheritDoc} */
+ public void writeShort(int value) {
+ int writeAt = cursor;
+ int end = writeAt + 2;
+
+ if (stretchy) {
+ ensureCapacity(end);
+ } else if (end > data.length) {
+ throwBounds();
+ return;
+ }
+
+ data[writeAt] = (byte) value;
+ data[writeAt + 1] = (byte) (value >> 8);
+ cursor = end;
+ }
+
+ /** {@inheritDoc} */
+ public void writeInt(int value) {
+ int writeAt = cursor;
+ int end = writeAt + 4;
+
+ if (stretchy) {
+ ensureCapacity(end);
+ } else if (end > data.length) {
+ throwBounds();
+ return;
+ }
+
+ data[writeAt] = (byte) value;
+ data[writeAt + 1] = (byte) (value >> 8);
+ data[writeAt + 2] = (byte) (value >> 16);
+ data[writeAt + 3] = (byte) (value >> 24);
+ cursor = end;
+ }
+
+ /** {@inheritDoc} */
+ public void writeLong(long value) {
+ int writeAt = cursor;
+ int end = writeAt + 8;
+
+ if (stretchy) {
+ ensureCapacity(end);
+ } else if (end > data.length) {
+ throwBounds();
+ return;
+ }
+
+ int half = (int) value;
+ data[writeAt] = (byte) half;
+ data[writeAt + 1] = (byte) (half >> 8);
+ data[writeAt + 2] = (byte) (half >> 16);
+ data[writeAt + 3] = (byte) (half >> 24);
+
+ half = (int) (value >> 32);
+ data[writeAt + 4] = (byte) half;
+ data[writeAt + 5] = (byte) (half >> 8);
+ data[writeAt + 6] = (byte) (half >> 16);
+ data[writeAt + 7] = (byte) (half >> 24);
+
+ cursor = end;
+ }
+
+ /** {@inheritDoc} */
+ public int writeUnsignedLeb128(int value) {
+ long remaining = (value & 0xFFFFFFFFL) >> 7;
+ long lValue = value;
+ int count = 0;
+
+ while (remaining != 0) {
+ writeByte((int)(lValue & 0x7f) | 0x80);
+ lValue = remaining;
+ remaining >>= 7;
+ count++;
+ }
+
+ writeByte((int)(lValue & 0x7f));
+ return count + 1;
+ }
+
+ /** {@inheritDoc} */
+ public int writeSignedLeb128(int value) {
+ int remaining = value >> 7;
+ int count = 0;
+ boolean hasMore = true;
+ int end = ((value & Integer.MIN_VALUE) == 0) ? 0 : -1;
+
+ while (hasMore) {
+ hasMore = (remaining != end)
+ || ((remaining & 1) != ((value >> 6) & 1));
+
+ writeByte((value & 0x7f) | (hasMore ? 0x80 : 0));
+ value = remaining;
+ remaining >>= 7;
+ count++;
+ }
+
+ return count;
+ }
+
+ /** {@inheritDoc} */
+ public void write(ByteArray bytes) {
+ int blen = bytes.size();
+ int writeAt = cursor;
+ int end = writeAt + blen;
+
+ if (stretchy) {
+ ensureCapacity(end);
+ } else if (end > data.length) {
+ throwBounds();
+ return;
+ }
+
+ bytes.getBytes(data, writeAt);
+ cursor = end;
+ }
+
+ /** {@inheritDoc} */
+ public void write(byte[] bytes, int offset, int length) {
+ int writeAt = cursor;
+ int end = writeAt + length;
+ int bytesEnd = offset + length;
+
+ // twos-complement math trick: ((x < 0) || (y < 0)) <=> ((x|y) < 0)
+ if (((offset | length | end) < 0) || (bytesEnd > bytes.length)) {
+ throw new IndexOutOfBoundsException("bytes.length " +
+ bytes.length + "; " +
+ offset + "..!" + end);
+ }
+
+ if (stretchy) {
+ ensureCapacity(end);
+ } else if (end > data.length) {
+ throwBounds();
+ return;
+ }
+
+ System.arraycopy(bytes, offset, data, writeAt, length);
+ cursor = end;
+ }
+
+ /** {@inheritDoc} */
+ public void write(byte[] bytes) {
+ write(bytes, 0, bytes.length);
+ }
+
+ /** {@inheritDoc} */
+ public void writeZeroes(int count) {
+ if (count < 0) {
+ throw new IllegalArgumentException("count < 0");
+ }
+
+ int end = cursor + count;
+
+ if (stretchy) {
+ ensureCapacity(end);
+ } else if (end > data.length) {
+ throwBounds();
+ return;
+ }
+
+ /*
+ * There is no need to actually write zeroes, since the array is
+ * already preinitialized with zeroes.
+ */
+
+ cursor = end;
+ }
+
+ /** {@inheritDoc} */
+ public void alignTo(int alignment) {
+ int mask = alignment - 1;
+
+ if ((alignment < 0) || ((mask & alignment) != 0)) {
+ throw new IllegalArgumentException("bogus alignment");
+ }
+
+ int end = (cursor + mask) & ~mask;
+
+ if (stretchy) {
+ ensureCapacity(end);
+ } else if (end > data.length) {
+ throwBounds();
+ return;
+ }
+
+ /*
+ * There is no need to actually write zeroes, since the array is
+ * already preinitialized with zeroes.
+ */
+
+ cursor = end;
+ }
+
+ /** {@inheritDoc} */
+ public boolean annotates() {
+ return (annotations != null);
+ }
+
+ /** {@inheritDoc} */
+ public boolean isVerbose() {
+ return verbose;
+ }
+
+ /** {@inheritDoc} */
+ public void annotate(String msg) {
+ if (annotations == null) {
+ return;
+ }
+
+ endAnnotation();
+ annotations.add(new Annotation(cursor, msg, currentIndent));
+ }
+
+ public void indent() {
+ currentIndent++;
+ }
+
+ public void deindent() {
+ currentIndent--;
+ if (currentIndent < 0) {
+ currentIndent = 0;
+ }
+ }
+
+ public void setIndentAmount(int indentAmount) {
+ this.indentAmount = indentAmount;
+ }
+
+ /** {@inheritDoc} */
+ public void annotate(int amt, String msg) {
+ if (annotations == null) {
+ return;
+ }
+
+ endAnnotation();
+
+ int asz = annotations.size();
+ int lastEnd = (asz == 0) ? 0 : annotations.get(asz - 1).getEnd();
+ int startAt;
+
+ if (lastEnd <= cursor) {
+ startAt = cursor;
+ } else {
+ startAt = lastEnd;
+ }
+
+ annotations.add(new Annotation(startAt, startAt + amt, msg, currentIndent));
+ }
+
+ /** {@inheritDoc} */
+ public void endAnnotation() {
+ if (annotations == null) {
+ return;
+ }
+
+ int sz = annotations.size();
+
+ if (sz != 0) {
+ annotations.get(sz - 1).setEndIfUnset(cursor);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public int getAnnotationWidth() {
+ int leftWidth = 8 + (hexCols * 2) + (hexCols / 2);
+
+ return annotationWidth - leftWidth;
+ }
+
+ /**
+ * Indicates that this instance should keep annotations. This method may
+ * be called only once per instance, and only before any data has been
+ * written to the it.
+ *
+ * @param annotationWidth >= 40; the desired maximum annotation width
+ * @param verbose whether or not to indicate verbose annotations
+ */
+ public void enableAnnotations(int annotationWidth, boolean verbose) {
+ if ((annotations != null) || (cursor != 0)) {
+ throw new RuntimeException("cannot enable annotations");
+ }
+
+ if (annotationWidth < 40) {
+ throw new IllegalArgumentException("annotationWidth < 40");
+ }
+
+ int hexCols = (((annotationWidth - 7) / 15) + 1) & ~1;
+ if (hexCols < 6) {
+ hexCols = 6;
+ } else if (hexCols > 10) {
+ hexCols = 10;
+ }
+
+ this.annotations = new ArrayList(1000);
+ this.annotationWidth = annotationWidth;
+ this.hexCols = hexCols;
+ this.verbose = verbose;
+ }
+
+ /**
+ * Finishes up annotation processing. This closes off any open
+ * annotations and removes annotations that don't refer to written
+ * data.
+ */
+ public void finishAnnotating() {
+ // Close off the final annotation, if any.
+ endAnnotation();
+
+ // Remove annotations that refer to unwritten data.
+ if (annotations != null) {
+ int asz = annotations.size();
+ while (asz > 0) {
+ Annotation last = annotations.get(asz - 1);
+ if (last.getStart() > cursor) {
+ annotations.remove(asz - 1);
+ asz--;
+ } else if (last.getEnd() > cursor) {
+ last.setEnd(cursor);
+ break;
+ } else {
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Writes the annotated content of this instance to the given writer.
+ *
+ * @param out non-null; where to write to
+ */
+ public void writeAnnotationsTo(Writer out) throws IOException {
+ int width2 = getAnnotationWidth();
+ int width1 = annotationWidth - width2 - 1;
+
+ StringBuilder padding = new StringBuilder();
+ for (int i=0; i<1000; i++) {
+ padding.append(' ');
+ }
+
+ TwoColumnOutput twoc = new TwoColumnOutput(out, width1, width2, "|");
+ Writer left = twoc.getLeft();
+ Writer right = twoc.getRight();
+ int leftAt = 0; // left-hand byte output cursor
+ int rightAt = 0; // right-hand annotation index
+ int rightSz = annotations.size();
+
+ while ((leftAt < cursor) && (rightAt < rightSz)) {
+ Annotation a = annotations.get(rightAt);
+ int start = a.getStart();
+ int end;
+ String text;
+
+ if (leftAt < start) {
+ // This is an area with no annotation.
+ end = start;
+ start = leftAt;
+ text = "";
+ } else {
+ // This is an area with an annotation.
+ end = a.getEnd();
+ text = padding.substring(0, a.getIndent() * this.indentAmount) + a.getText();
+ rightAt++;
+ }
+
+ left.write(Hex.dump(data, start, end - start, start, hexCols, 6));
+ right.write(text);
+ twoc.flush();
+ leftAt = end;
+ }
+
+ if (leftAt < cursor) {
+ // There is unannotated output at the end.
+ left.write(Hex.dump(data, leftAt, cursor - leftAt, leftAt,
+ hexCols, 6));
+ }
+
+ while (rightAt < rightSz) {
+ // There are zero-byte annotations at the end.
+ right.write(annotations.get(rightAt).getText());
+ rightAt++;
+ }
+
+ twoc.flush();
+ }
+
+ /**
+ * Throws the excpetion for when an attempt is made to write past the
+ * end of the instance.
+ */
+ private static void throwBounds() {
+ throw new IndexOutOfBoundsException("attempt to write past the end");
+ }
+
+ /**
+ * Reallocates the underlying array if necessary. Calls to this method
+ * should be guarded by a test of {@link #stretchy}.
+ *
+ * @param desiredSize >= 0; the desired minimum total size of the array
+ */
+ private void ensureCapacity(int desiredSize) {
+ if (data.length < desiredSize) {
+ byte[] newData = new byte[desiredSize * 2 + 1000];
+ System.arraycopy(data, 0, newData, 0, cursor);
+ data = newData;
+ }
+ }
+
+ /**
+ * Annotation on output.
+ */
+ private static class Annotation {
+ /** >= 0; start of annotated range (inclusive) */
+ private final int start;
+
+ /**
+ * >= 0; end of annotated range (exclusive);
+ * Integer.MAX_VALUE
if unclosed
+ */
+ private int end;
+
+ /** non-null; annotation text */
+ private final String text;
+
+ private int indent;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param start >= 0; start of annotated range
+ * @param end >= start; end of annotated range (exclusive) or
+ * Integer.MAX_VALUE
if unclosed
+ * @param text non-null; annotation text
+ */
+ public Annotation(int start, int end, String text, int indent) {
+ this.start = start;
+ this.end = end;
+ this.text = text;
+ this.indent = indent;
+ }
+
+ /**
+ * Constructs an instance. It is initally unclosed.
+ *
+ * @param start >= 0; start of annotated range
+ * @param text non-null; annotation text
+ */
+ public Annotation(int start, String text, int indent) {
+ this(start, Integer.MAX_VALUE, text, indent);
+ }
+
+ /**
+ * Sets the end as given, but only if the instance is unclosed;
+ * otherwise, do nothing.
+ *
+ * @param end >= start; the end
+ */
+ public void setEndIfUnset(int end) {
+ if (this.end == Integer.MAX_VALUE) {
+ this.end = end;
+ }
+ }
+
+ /**
+ * Sets the end as given.
+ *
+ * @param end >= start; the end
+ */
+ public void setEnd(int end) {
+ this.end = end;
+ }
+
+ /**
+ * Gets the start.
+ *
+ * @return the start
+ */
+ public int getStart() {
+ return start;
+ }
+
+ /**
+ * Gets the end.
+ *
+ * @return the end
+ */
+ public int getEnd() {
+ return end;
+ }
+
+ /**
+ * Gets the text.
+ *
+ * @return non-null; the text
+ */
+ public String getText() {
+ return text;
+ }
+
+ public int getIndent() {
+ return indent;
+ }
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Util/ByteArrayInput.java b/dexlib/src/main/java/org/jf/dexlib/Util/ByteArrayInput.java
new file mode 100644
index 00000000..59b539df
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Util/ByteArrayInput.java
@@ -0,0 +1,318 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2009 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Util;
+
+/**
+ * Implementation of {@link Input} which reads the data from a
+ * byte[]
instance.
+ *
+ * Note: As per the {@link Input } interface, multi-byte
+ * reads all use little-endian order.
+ */
+public class ByteArrayInput
+ implements Input {
+
+ /** non-null; the data itself */
+ private byte[] data;
+
+ /** >= 0; current read cursor */
+ private int cursor;
+
+ /**
+ * Constructs an instance with the given data
+ *
+ * @param data non-null; data array to use for input
+ */
+ public ByteArrayInput(byte[] data) {
+ if (data == null) {
+ throw new NullPointerException("data == null");
+ }
+
+ this.data = data;
+ this.cursor = 0;
+ }
+
+ /**
+ * Gets the underlying byte[]
of this instance
+ *
+ * @return non-null; the byte[]
+ */
+ public byte[] getArray() {
+ return data;
+ }
+
+ /** {@inheritDoc} */
+ public int getCursor() {
+ return cursor;
+ }
+
+ /** {@inheritDoc} */
+ public void setCursor(int cursor) {
+ if (cursor < 0 || cursor >= data.length)
+ throw new IndexOutOfBoundsException("The provided cursor value " +
+ "is not within the bounds of this instance's data array");
+ this.cursor = cursor;
+ }
+
+ /** {@inheritDoc} */
+ public void assertCursor(int expectedCursor) {
+ if (cursor != expectedCursor) {
+ throw new ExceptionWithContext("expected cursor " +
+ expectedCursor + "; actual value: " + cursor);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public byte readByte() {
+ int readAt = cursor;
+ int end = readAt + 1;
+
+ if (end > data.length) {
+ throwBounds();
+ }
+
+ cursor = end;
+ return data[readAt];
+ }
+
+ /** {@inheritDoc} */
+ public int readShort() {
+ int readAt = cursor;
+ int end = readAt + 2;
+
+ if (end > data.length) {
+ throwBounds();
+ }
+
+ cursor = end;
+ return (int)((data[readAt] & 0xff) +
+ ((data[readAt + 1] & 0xff) << 8));
+ }
+
+ /** {@inheritDoc} */
+ public int readInt() {
+ int readAt = cursor;
+ int end = readAt + 4;
+
+ if (end > data.length) {
+ throwBounds();
+ }
+
+ cursor = end;
+ return (data[readAt] & 0xff) +
+ ((data[readAt + 1] & 0xff) << 8) +
+ ((data[readAt + 2] & 0xff) << 16) +
+ ((data[readAt + 3] & 0xff) << 24);
+ }
+
+ /** {@inheritDoc} */
+ public long readLong() {
+ int readAt = cursor;
+ int end = readAt + 8;
+
+ if (end > data.length) {
+ throwBounds();
+ }
+
+ cursor = end;
+
+ return (data[readAt] & 0xffL) |
+ ((data[readAt + 1] & 0xffL) << 8) |
+ ((data[readAt + 2] & 0xffL) << 16) |
+ ((data[readAt + 3] & 0xffL) << 24) |
+ ((data[readAt + 4] & 0xffL) << 32) |
+ ((data[readAt + 5] & 0xffL) << 40) |
+ ((data[readAt + 6] & 0xffL) << 48) |
+ ((data[readAt + 7] & 0xffL) << 58);
+ }
+
+ /** {@inheritDoc} */
+ public int readUnsignedLeb128() {
+ int end = cursor;
+ int currentByteValue;
+ int result;
+
+ result = data[end++] & 0xff;
+ if (result > 0x7f) {
+ currentByteValue = data[end++] & 0xff;
+ result = (result & 0x7f) | ((currentByteValue & 0x7f) << 7);
+ if (currentByteValue > 0x7f) {
+ currentByteValue = data[end++] & 0xff;
+ result |= (currentByteValue & 0x7f) << 14;
+ if (currentByteValue > 0x7f) {
+ currentByteValue = data[end++] & 0xff;
+ result |= (currentByteValue & 0x7f) << 21;
+ if (currentByteValue > 0x7f) {
+ currentByteValue = data[end++] & 0xff;
+ if (currentByteValue > 0x0f) {
+ throwInvalidLeb();
+ }
+ result |= currentByteValue << 28;
+ }
+ }
+ }
+ }
+
+ cursor = end;
+ return result;
+ }
+
+ /** {@inheritDoc} */
+ public int readSignedLeb128() {
+ int end = cursor;
+ int currentByteValue;
+ int result;
+
+ result = data[end++] & 0xff;
+ if (result <= 0x7f) {
+ result = (result << 25) >> 25;
+ } else {
+ currentByteValue = data[end++] & 0xff;
+ result = (result & 0x7f) | ((currentByteValue & 0x7f) << 7);
+ if (currentByteValue <= 0x7f) {
+ result = (result << 18) >> 18;
+ } else {
+ currentByteValue = data[end++] & 0xff;
+ result |= (currentByteValue & 0x7f) << 14;
+ if (currentByteValue <= 0x7f) {
+ result = (result << 11) >> 11;
+ } else {
+ currentByteValue = data[end++] & 0xff;
+ result |= (currentByteValue & 0x7f) << 21;
+ if (currentByteValue <= 0x7f) {
+ result = (result << 4) >> 4;
+ } else {
+ currentByteValue = data[end++] & 0xff;
+ if (currentByteValue > 0x0f) {
+ throwInvalidLeb();
+ }
+ result |= currentByteValue << 28;
+ }
+ }
+ }
+ }
+
+ cursor = end;
+ return result;
+ }
+
+ /** {@inheritDoc} */
+ public void read(byte[] bytes, int offset, int length) {
+ int end = cursor + length;
+
+ if (end > data.length) {
+ throwBounds();
+ }
+
+ System.arraycopy(data, cursor, bytes, offset, length);
+ cursor = end;
+ }
+
+ /** {@inheritDoc} */
+ public void read(byte[] bytes) {
+ int length = bytes.length;
+ int end = cursor + length;
+
+ if (end > data.length) {
+ throwBounds();
+ }
+
+ System.arraycopy(data, cursor, bytes, 0, length);
+ cursor = end;
+ }
+
+ /** {@inheritDoc} */
+ public byte[] readBytes(int length) {
+ int end = cursor + length;
+
+ if (end > data.length) {
+ throwBounds();
+ }
+
+ byte[] result = new byte[length];
+ System.arraycopy(data, cursor, result, 0, length);
+ cursor = end;
+ return result;
+ }
+
+ /** {@inheritDoc} */
+ public byte[] readNullTerminatedBytes() {
+ int startPosition = cursor;
+ while (data[cursor] != 0) {
+ cursor++;
+ if (cursor >= data.length) {
+ throwBounds();
+ }
+ }
+ int byteCount = cursor - startPosition;
+ //skip the terminating null
+ cursor++;
+
+ byte[] result = new byte[byteCount];
+ System.arraycopy(data, startPosition, result, 0, byteCount);
+ return result;
+ }
+
+ /** {@inheritDoc} */
+ public void skipBytes(int count) {
+ int end = cursor + count;
+
+ if (end > data.length) {
+ throwBounds();
+ }
+
+ cursor = end;
+ }
+
+ /** {@inheritDoc} */
+ public void alignTo(int alignment) {
+ int end = AlignmentUtils.alignOffset(cursor, alignment);
+
+ if (end > data.length) {
+ throwBounds();
+ }
+
+ cursor = end;
+ }
+
+ /**
+ * Throws the excpetion for when an attempt is made to read past the
+ * end of the instance.
+ */
+ private static void throwBounds() {
+ throw new IndexOutOfBoundsException("attempt to read past the end");
+ }
+
+ /**
+ * Throws the exception for when an invalid LEB128 value is encountered
+ */
+ private static void throwInvalidLeb() {
+ throw new RuntimeException("invalid LEB128 integer encountered");
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Util/ByteArrayOutput.java b/dexlib/src/main/java/org/jf/dexlib/Util/ByteArrayOutput.java
new file mode 100644
index 00000000..e286beb6
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Util/ByteArrayOutput.java
@@ -0,0 +1,570 @@
+/*
+ * 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 org.jf.dexlib.Util;
+
+import java.util.ArrayList;
+
+/**
+ * Implementation of {@link AnnotatedOutput} which stores the written data
+ * into a byte[]
.
+ *
+ * Note: As per the {@link Output} interface, multi-byte
+ * writes all use little-endian order.
+ */
+public final class ByteArrayOutput implements Output
+{
+ /** default size for stretchy instances */
+ private static final int DEFAULT_SIZE = 1000;
+
+ /**
+ * whether the instance is stretchy, that is, whether its array
+ * may be resized to increase capacity
+ */
+ private final boolean stretchy;
+
+ /** non-null; the data itself */
+ private byte[] data;
+
+ /** >= 0; current output cursor */
+ private int cursor;
+
+ /** whether annotations are to be verbose */
+ private boolean verbose;
+
+ /**
+ * null-ok; list of annotations, or null
if this instance
+ * isn't keeping them
+ */
+ private ArrayList annotations;
+
+ /** >= 40 (if used); the desired maximum annotation width */
+ private int annotationWidth;
+
+ /**
+ * >= 8 (if used); the number of bytes of hex output to use
+ * in annotations
+ */
+ private int hexCols;
+
+ /**
+ * Constructs an instance with a fixed maximum size. Note that the
+ * given array is the only one that will be used to store data. In
+ * particular, no reallocation will occur in order to expand the
+ * capacity of the resulting instance. Also, the constructed
+ * instance does not keep annotations by default.
+ *
+ * @param data non-null; data array to use for output
+ */
+ public ByteArrayOutput(byte[] data) {
+ this(data, false);
+ }
+
+ /**
+ * Constructs a "stretchy" instance. The underlying array may be
+ * reallocated. The constructed instance does not keep annotations
+ * by default.
+ */
+ public ByteArrayOutput() {
+ this(new byte[DEFAULT_SIZE], true);
+ }
+
+ /**
+ * Internal constructor.
+ *
+ * @param data non-null; data array to use for output
+ * @param stretchy whether the instance is to be stretchy
+ */
+ private ByteArrayOutput(byte[] data, boolean stretchy) {
+ if (data == null) {
+ throw new NullPointerException("data == null");
+ }
+
+ this.stretchy = stretchy;
+ this.data = data;
+ this.cursor = 0;
+ this.verbose = false;
+ this.annotations = null;
+ this.annotationWidth = 0;
+ this.hexCols = 0;
+ }
+
+ /**
+ * Gets the underlying byte[]
of this instance, which
+ * may be larger than the number of bytes written
+ *
+ * @see #toByteArray
+ *
+ * @return non-null; the byte[]
+ */
+ public byte[] getArray() {
+ return data;
+ }
+
+ /**
+ * Constructs and returns a new byte[]
that contains
+ * the written contents exactly (that is, with no extra unwritten
+ * bytes at the end).
+ *
+ * @see #getArray
+ *
+ * @return non-null; an appropriately-constructed array
+ */
+ public byte[] toByteArray() {
+ byte[] result = new byte[cursor];
+ System.arraycopy(data, 0, result, 0, cursor);
+ return result;
+ }
+
+ /** {@inheritDoc} */
+ public int getCursor() {
+ return cursor;
+ }
+
+ /** {@inheritDoc} */
+ public void assertCursor(int expectedCursor) {
+ if (cursor != expectedCursor) {
+ throw new ExceptionWithContext("expected cursor " +
+ expectedCursor + "; actual value: " + cursor);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void writeByte(int value) {
+ int writeAt = cursor;
+ int end = writeAt + 1;
+
+ if (stretchy) {
+ ensureCapacity(end);
+ } else if (end > data.length) {
+ throwBounds();
+ return;
+ }
+
+ data[writeAt] = (byte) value;
+ cursor = end;
+ }
+
+ /** {@inheritDoc} */
+ public void writeShort(int value) {
+ int writeAt = cursor;
+ int end = writeAt + 2;
+
+ if (stretchy) {
+ ensureCapacity(end);
+ } else if (end > data.length) {
+ throwBounds();
+ return;
+ }
+
+ data[writeAt] = (byte) value;
+ data[writeAt + 1] = (byte) (value >> 8);
+ cursor = end;
+ }
+
+ /** {@inheritDoc} */
+ public void writeInt(int value) {
+ int writeAt = cursor;
+ int end = writeAt + 4;
+
+ if (stretchy) {
+ ensureCapacity(end);
+ } else if (end > data.length) {
+ throwBounds();
+ return;
+ }
+
+ data[writeAt] = (byte) value;
+ data[writeAt + 1] = (byte) (value >> 8);
+ data[writeAt + 2] = (byte) (value >> 16);
+ data[writeAt + 3] = (byte) (value >> 24);
+ cursor = end;
+ }
+
+ /** {@inheritDoc} */
+ public void writeLong(long value) {
+ int writeAt = cursor;
+ int end = writeAt + 8;
+
+ if (stretchy) {
+ ensureCapacity(end);
+ } else if (end > data.length) {
+ throwBounds();
+ return;
+ }
+
+ int half = (int) value;
+ data[writeAt] = (byte) half;
+ data[writeAt + 1] = (byte) (half >> 8);
+ data[writeAt + 2] = (byte) (half >> 16);
+ data[writeAt + 3] = (byte) (half >> 24);
+
+ half = (int) (value >> 32);
+ data[writeAt + 4] = (byte) half;
+ data[writeAt + 5] = (byte) (half >> 8);
+ data[writeAt + 6] = (byte) (half >> 16);
+ data[writeAt + 7] = (byte) (half >> 24);
+
+ cursor = end;
+ }
+
+ /** {@inheritDoc} */
+ public int writeUnsignedLeb128(int value) {
+ int remaining = value >> 7;
+ int count = 0;
+
+ while (remaining != 0) {
+ writeByte((value & 0x7f) | 0x80);
+ value = remaining;
+ remaining >>= 7;
+ count++;
+ }
+
+ writeByte(value & 0x7f);
+ return count + 1;
+ }
+
+ /** {@inheritDoc} */
+ public int writeSignedLeb128(int value) {
+ int remaining = value >> 7;
+ int count = 0;
+ boolean hasMore = true;
+ int end = ((value & Integer.MIN_VALUE) == 0) ? 0 : -1;
+
+ while (hasMore) {
+ hasMore = (remaining != end)
+ || ((remaining & 1) != ((value >> 6) & 1));
+
+ writeByte((value & 0x7f) | (hasMore ? 0x80 : 0));
+ value = remaining;
+ remaining >>= 7;
+ count++;
+ }
+
+ return count;
+ }
+
+ /** {@inheritDoc} */
+ public void write(ByteArray bytes) {
+ int blen = bytes.size();
+ int writeAt = cursor;
+ int end = writeAt + blen;
+
+ if (stretchy) {
+ ensureCapacity(end);
+ } else if (end > data.length) {
+ throwBounds();
+ return;
+ }
+
+ bytes.getBytes(data, writeAt);
+ cursor = end;
+ }
+
+ /** {@inheritDoc} */
+ public void write(byte[] bytes, int offset, int length) {
+ int writeAt = cursor;
+ int end = writeAt + length;
+ int bytesEnd = offset + length;
+
+ // twos-complement math trick: ((x < 0) || (y < 0)) <=> ((x|y) < 0)
+ if (((offset | length | end) < 0) || (bytesEnd > bytes.length)) {
+ throw new IndexOutOfBoundsException("bytes.length " +
+ bytes.length + "; " +
+ offset + "..!" + end);
+ }
+
+ if (stretchy) {
+ ensureCapacity(end);
+ } else if (end > data.length) {
+ throwBounds();
+ return;
+ }
+
+ System.arraycopy(bytes, offset, data, writeAt, length);
+ cursor = end;
+ }
+
+ /** {@inheritDoc} */
+ public void write(byte[] bytes) {
+ write(bytes, 0, bytes.length);
+ }
+
+ /** {@inheritDoc} */
+ public void writeZeroes(int count) {
+ if (count < 0) {
+ throw new IllegalArgumentException("count < 0");
+ }
+
+ int end = cursor + count;
+
+ if (stretchy) {
+ ensureCapacity(end);
+ } else if (end > data.length) {
+ throwBounds();
+ return;
+ }
+
+ /*
+ * There is no need to actually write zeroes, since the array is
+ * already preinitialized with zeroes.
+ */
+
+ cursor = end;
+ }
+
+ /** {@inheritDoc} */
+ public void alignTo(int alignment) {
+ int end = AlignmentUtils.alignOffset(cursor, alignment);
+
+ if (stretchy) {
+ ensureCapacity(end);
+ } else if (end > data.length) {
+ throwBounds();
+ return;
+ }
+ cursor = end;
+ }
+
+ /** {@inheritDoc} */
+ public boolean annotates() {
+ return (annotations != null);
+ }
+
+ /** {@inheritDoc} */
+ public boolean isVerbose() {
+ return verbose;
+ }
+
+ /** {@inheritDoc} */
+ public void annotate(String msg) {
+ if (annotations == null) {
+ return;
+ }
+
+ endAnnotation();
+ annotations.add(new Annotation(cursor, msg));
+ }
+
+ /** {@inheritDoc} */
+ public void annotate(int amt, String msg) {
+ if (annotations == null) {
+ return;
+ }
+
+ endAnnotation();
+
+ int asz = annotations.size();
+ int lastEnd = (asz == 0) ? 0 : annotations.get(asz - 1).getEnd();
+ int startAt;
+
+ if (lastEnd <= cursor) {
+ startAt = cursor;
+ } else {
+ startAt = lastEnd;
+ }
+
+ annotations.add(new Annotation(startAt, startAt + amt, msg));
+ }
+
+ /** {@inheritDoc} */
+ public void endAnnotation() {
+ if (annotations == null) {
+ return;
+ }
+
+ int sz = annotations.size();
+
+ if (sz != 0) {
+ annotations.get(sz - 1).setEndIfUnset(cursor);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public int getAnnotationWidth() {
+ int leftWidth = 8 + (hexCols * 2) + (hexCols / 2);
+
+ return annotationWidth - leftWidth;
+ }
+
+ /**
+ * Indicates that this instance should keep annotations. This method may
+ * be called only once per instance, and only before any data has been
+ * written to the it.
+ *
+ * @param annotationWidth >= 40; the desired maximum annotation width
+ * @param verbose whether or not to indicate verbose annotations
+ */
+ public void enableAnnotations(int annotationWidth, boolean verbose) {
+ if ((annotations != null) || (cursor != 0)) {
+ throw new RuntimeException("cannot enable annotations");
+ }
+
+ if (annotationWidth < 40) {
+ throw new IllegalArgumentException("annotationWidth < 40");
+ }
+
+ int hexCols = (((annotationWidth - 7) / 15) + 1) & ~1;
+ if (hexCols < 6) {
+ hexCols = 6;
+ } else if (hexCols > 10) {
+ hexCols = 10;
+ }
+
+ this.annotations = new ArrayList(1000);
+ this.annotationWidth = annotationWidth;
+ this.hexCols = hexCols;
+ this.verbose = verbose;
+ }
+
+ /**
+ * Finishes up annotation processing. This closes off any open
+ * annotations and removes annotations that don't refer to written
+ * data.
+ */
+ public void finishAnnotating() {
+ // Close off the final annotation, if any.
+ endAnnotation();
+
+ // Remove annotations that refer to unwritten data.
+ if (annotations != null) {
+ int asz = annotations.size();
+ while (asz > 0) {
+ Annotation last = annotations.get(asz - 1);
+ if (last.getStart() > cursor) {
+ annotations.remove(asz - 1);
+ asz--;
+ } else if (last.getEnd() > cursor) {
+ last.setEnd(cursor);
+ break;
+ } else {
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Throws the excpetion for when an attempt is made to write past the
+ * end of the instance.
+ */
+ private static void throwBounds() {
+ throw new IndexOutOfBoundsException("attempt to write past the end");
+ }
+
+ /**
+ * Reallocates the underlying array if necessary. Calls to this method
+ * should be guarded by a test of {@link #stretchy}.
+ *
+ * @param desiredSize >= 0; the desired minimum total size of the array
+ */
+ private void ensureCapacity(int desiredSize) {
+ if (data.length < desiredSize) {
+ byte[] newData = new byte[desiredSize * 2 + 1000];
+ System.arraycopy(data, 0, newData, 0, cursor);
+ data = newData;
+ }
+ }
+
+ /**
+ * Annotation on output.
+ */
+ private static class Annotation {
+ /** >= 0; start of annotated range (inclusive) */
+ private final int start;
+
+ /**
+ * >= 0; end of annotated range (exclusive);
+ * Integer.MAX_VALUE
if unclosed
+ */
+ private int end;
+
+ /** non-null; annotation text */
+ private final String text;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param start >= 0; start of annotated range
+ * @param end >= start; end of annotated range (exclusive) or
+ * Integer.MAX_VALUE
if unclosed
+ * @param text non-null; annotation text
+ */
+ public Annotation(int start, int end, String text) {
+ this.start = start;
+ this.end = end;
+ this.text = text;
+ }
+
+ /**
+ * Constructs an instance. It is initally unclosed.
+ *
+ * @param start >= 0; start of annotated range
+ * @param text non-null; annotation text
+ */
+ public Annotation(int start, String text) {
+ this(start, Integer.MAX_VALUE, text);
+ }
+
+ /**
+ * Sets the end as given, but only if the instance is unclosed;
+ * otherwise, do nothing.
+ *
+ * @param end >= start; the end
+ */
+ public void setEndIfUnset(int end) {
+ if (this.end == Integer.MAX_VALUE) {
+ this.end = end;
+ }
+ }
+
+ /**
+ * Sets the end as given.
+ *
+ * @param end >= start; the end
+ */
+ public void setEnd(int end) {
+ this.end = end;
+ }
+
+ /**
+ * Gets the start.
+ *
+ * @return the start
+ */
+ public int getStart() {
+ return start;
+ }
+
+ /**
+ * Gets the end.
+ *
+ * @return the end
+ */
+ public int getEnd() {
+ return end;
+ }
+
+ /**
+ * Gets the text.
+ *
+ * @return non-null; the text
+ */
+ public String getText() {
+ return text;
+ }
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Util/EncodedValueUtils.java b/dexlib/src/main/java/org/jf/dexlib/Util/EncodedValueUtils.java
new file mode 100644
index 00000000..5d0f3d2d
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Util/EncodedValueUtils.java
@@ -0,0 +1,143 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2009 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Util;
+
+public class EncodedValueUtils {
+ public static byte getRequiredBytesForSignedIntegralValue(long value) {
+ /*
+ * Figure out how many bits are needed to represent the value,
+ * including a sign bit: The bit count is subtracted from 65
+ * and not 64 to account for the sign bit. The xor operation
+ * has the effect of leaving non-negative values alone and
+ * unary complementing negative values (so that a leading zero
+ * count always returns a useful number for our present
+ * purpose).
+ */
+ int requiredBits =
+ 65 - Long.numberOfLeadingZeros(value ^ (value >> 63));
+
+ // Round up the requiredBits to a number of bytes.
+ return (byte)((requiredBits + 0x07) >> 3);
+ }
+
+ public static long decodeSignedIntegralValue(byte[] bytes) {
+ long value = 0;
+ for (int i = 0; i < bytes.length; i++) {
+ value |= (((long)(bytes[i] & 0xFF)) << (i * 8));
+ }
+
+ int shift = (8 - bytes.length) * 8;
+ return value << shift >> shift;
+ }
+
+ public static byte[] encodeSignedIntegralValue(long value) {
+ int requiredBytes = getRequiredBytesForSignedIntegralValue(value);
+
+ byte[] bytes = new byte[requiredBytes];
+
+ for (int i = 0; i < requiredBytes; i++) {
+ bytes[i] = (byte) value;
+ value >>= 8;
+ }
+ return bytes;
+ }
+
+
+
+
+
+ public static byte getRequiredBytesForUnsignedIntegralValue(long value) {
+ // Figure out how many bits are needed to represent the value.
+ int requiredBits = 64 - Long.numberOfLeadingZeros(value);
+ if (requiredBits == 0) {
+ requiredBits = 1;
+ }
+
+ // Round up the requiredBits to a number of bytes.
+ return (byte)((requiredBits + 0x07) >> 3);
+ }
+
+ public static long decodeUnsignedIntegralValue(byte[] bytes) {
+ long value = 0;
+ for (int i = 0; i < bytes.length; i++) {
+ value |= (((long)(bytes[i] & 0xFF)) << i * 8);
+ }
+ return value;
+ }
+
+ public static byte[] encodeUnsignedIntegralValue(long value) {
+ int requiredBytes = getRequiredBytesForUnsignedIntegralValue(value);
+
+ byte[] bytes = new byte[requiredBytes];
+
+ for (int i = 0; i < requiredBytes; i++) {
+ bytes[i] = (byte) value;
+ value >>= 8;
+ }
+ return bytes;
+ }
+
+
+
+
+
+ public static int getRequiredBytesForRightZeroExtendedValue(long value) {
+ // Figure out how many bits are needed to represent the value.
+ int requiredBits = 64 - Long.numberOfTrailingZeros(value);
+ if (requiredBits == 0) {
+ requiredBits = 1;
+ }
+
+ // Round up the requiredBits to a number of bytes.
+ return (requiredBits + 0x07) >> 3;
+ }
+
+ public static long decodeRightZeroExtendedValue(byte[] bytes) {
+ long value = 0;
+ for (int i = 0; i < bytes.length; i++) {
+ value |= (((long)(bytes[i] & 0xFF)) << (i * 8));
+ }
+ return value << (8 - bytes.length) * 8;
+ }
+
+ public static byte[] encodeRightZeroExtendedValue(long value) {
+ int requiredBytes = getRequiredBytesForRightZeroExtendedValue(value);
+
+ // Scootch the first bits to be written down to the low-order bits.
+ value >>= 64 - (requiredBytes * 8);
+
+ byte[] bytes = new byte[requiredBytes];
+
+ for(int i = 0; i < requiredBytes; i++) {
+ bytes[i] = (byte)value;
+ value >>= 8;
+ }
+ return bytes;
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Util/ExceptionWithContext.java b/dexlib/src/main/java/org/jf/dexlib/Util/ExceptionWithContext.java
new file mode 100644
index 00000000..2dd1d34c
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Util/ExceptionWithContext.java
@@ -0,0 +1,161 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2009 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Util;
+
+import java.io.PrintStream;
+import java.io.PrintWriter;
+
+/**
+ * Exception which carries around structured context.
+ */
+public class ExceptionWithContext
+ extends RuntimeException {
+ /** non-null; human-oriented context of the exception */
+ private StringBuffer context;
+
+ /**
+ * Augments the given exception with the given context, and return the
+ * result. The result is either the given exception if it was an
+ * {@link ExceptionWithContext}, or a newly-constructed exception if it
+ * was not.
+ *
+ * @param ex non-null; the exception to augment
+ * @param str non-null; context to add
+ * @return non-null; an appropriate instance
+ */
+ public static ExceptionWithContext withContext(Throwable ex, String str) {
+ ExceptionWithContext ewc;
+
+ if (ex instanceof ExceptionWithContext) {
+ ewc = (ExceptionWithContext) ex;
+ } else {
+ ewc = new ExceptionWithContext(ex);
+ }
+
+ ewc.addContext(str);
+ return ewc;
+ }
+
+ /**
+ * Constructs an instance.
+ *
+ * @param message human-oriented message
+ */
+ public ExceptionWithContext(String message) {
+ this(message, null);
+ }
+
+ /**
+ * Constructs an instance.
+ *
+ * @param cause null-ok; exception that caused this one
+ */
+ public ExceptionWithContext(Throwable cause) {
+ this(null, cause);
+ }
+
+ /**
+ * Constructs an instance.
+ *
+ * @param message human-oriented message
+ * @param cause null-ok; exception that caused this one
+ */
+ public ExceptionWithContext(String message, Throwable cause) {
+ super((message != null) ? message :
+ (cause != null) ? cause.getMessage() : null,
+ cause);
+
+ if (cause instanceof ExceptionWithContext) {
+ String ctx = ((ExceptionWithContext) cause).context.toString();
+ context = new StringBuffer(ctx.length() + 200);
+ context.append(ctx);
+ } else {
+ context = new StringBuffer(200);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void printStackTrace(PrintStream out) {
+ super.printStackTrace(out);
+ out.println(context);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void printStackTrace(PrintWriter out) {
+ super.printStackTrace(out);
+ out.println(context);
+ }
+
+ /**
+ * Adds a line of context to this instance.
+ *
+ * @param str non-null; new context
+ */
+ public void addContext(String str) {
+ if (str == null) {
+ throw new NullPointerException("str == null");
+ }
+
+ context.append(str);
+ if (!str.endsWith("\n")) {
+ context.append('\n');
+ }
+ }
+
+ /**
+ * Gets the context.
+ *
+ * @return non-null; the context
+ */
+ public String getContext() {
+ return context.toString();
+ }
+
+ /**
+ * Prints the message and context.
+ *
+ * @param out non-null; where to print to
+ */
+ public void printContext(PrintStream out) {
+ out.println(getMessage());
+ out.print(context);
+ }
+
+ /**
+ * Prints the message and context.
+ *
+ * @param out non-null; where to print to
+ */
+ public void printContext(PrintWriter out) {
+ out.println(getMessage());
+ out.print(context);
+ }
+}
\ No newline at end of file
diff --git a/dexlib/src/main/java/org/jf/dexlib/Util/FileUtils.java b/dexlib/src/main/java/org/jf/dexlib/Util/FileUtils.java
new file mode 100644
index 00000000..2da3baec
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Util/FileUtils.java
@@ -0,0 +1,92 @@
+/*
+ * 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 org.jf.dexlib.Util;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+
+/**
+ * File I/O utilities.
+ */
+public final class FileUtils {
+ /**
+ * This class is uninstantiable.
+ */
+ private FileUtils() {
+ // This space intentionally left blank.
+ }
+
+ /**
+ * Reads the named file, translating {@link IOException} to a
+ * {@link RuntimeException} of some sort.
+ *
+ * @param fileName non-null; name of the file to read
+ * @return non-null; contents of the file
+ */
+ public static byte[] readFile(String fileName) {
+ File file = new File(fileName);
+ return readFile(file);
+ }
+
+ /**
+ * Reads the given file, translating {@link IOException} to a
+ * {@link RuntimeException} of some sort.
+ *
+ * @param file non-null; the file to read
+ * @return non-null; contents of the file
+ */
+ public static byte[] readFile(File file) {
+ if (!file.exists()) {
+ throw new RuntimeException(file + ": file not found");
+ }
+
+ if (!file.isFile()) {
+ throw new RuntimeException(file + ": not a file");
+ }
+
+ if (!file.canRead()) {
+ throw new RuntimeException(file + ": file not readable");
+ }
+
+ long longLength = file.length();
+ int length = (int) longLength;
+ if (length != longLength) {
+ throw new RuntimeException(file + ": file too long");
+ }
+
+ byte[] result = new byte[length];
+
+ try {
+ FileInputStream in = new FileInputStream(file);
+ int at = 0;
+ while (length > 0) {
+ int amt = in.read(result, at, length);
+ if (amt == -1) {
+ throw new RuntimeException(file + ": unexpected EOF");
+ }
+ at += amt;
+ length -= amt;
+ }
+ in.close();
+ } catch (IOException ex) {
+ throw new RuntimeException(file + ": trouble reading", ex);
+ }
+
+ return result;
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Util/Hex.java b/dexlib/src/main/java/org/jf/dexlib/Util/Hex.java
new file mode 100644
index 00000000..fb22a110
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Util/Hex.java
@@ -0,0 +1,315 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2009 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Util;
+
+/**
+ * Utilities for formatting numbers as hexadecimal.
+ */
+public final class Hex {
+ /**
+ * This class is uninstantiable.
+ */
+ private Hex() {
+ // This space intentionally left blank.
+ }
+
+ /**
+ * Formats a long
as an 8-byte unsigned hex value.
+ *
+ * @param v value to format
+ * @return non-null; formatted form
+ */
+ public static String u8(long v) {
+ char[] result = new char[16];
+ for (int i = 0; i < 16; i++) {
+ result[15 - i] = Character.forDigit((int) v & 0x0f, 16);
+ v >>= 4;
+ }
+
+ return new String(result);
+ }
+
+ /**
+ * Formats an int
as a 4-byte unsigned hex value.
+ *
+ * @param v value to format
+ * @return non-null; formatted form
+ */
+ public static String u4(int v) {
+ char[] result = new char[8];
+ for (int i = 0; i < 8; i++) {
+ result[7 - i] = Character.forDigit(v & 0x0f, 16);
+ v >>= 4;
+ }
+
+ return new String(result);
+ }
+
+ /**
+ * Formats an int
as a 3-byte unsigned hex value.
+ *
+ * @param v value to format
+ * @return non-null; formatted form
+ */
+ public static String u3(int v) {
+ char[] result = new char[6];
+ for (int i = 0; i < 6; i++) {
+ result[5 - i] = Character.forDigit(v & 0x0f, 16);
+ v >>= 4;
+ }
+
+ return new String(result);
+ }
+
+ /**
+ * Formats an int
as a 2-byte unsigned hex value.
+ *
+ * @param v value to format
+ * @return non-null; formatted form
+ */
+ public static String u2(int v) {
+ char[] result = new char[4];
+ for (int i = 0; i < 4; i++) {
+ result[3 - i] = Character.forDigit(v & 0x0f, 16);
+ v >>= 4;
+ }
+
+ return new String(result);
+ }
+
+ /**
+ * Formats an int
as either a 2-byte unsigned hex value
+ * (if the value is small enough) or a 4-byte unsigned hex value (if
+ * not).
+ *
+ * @param v value to format
+ * @return non-null; formatted form
+ */
+ public static String u2or4(int v) {
+ if (v == (char) v) {
+ return u2(v);
+ } else {
+ return u4(v);
+ }
+ }
+
+ /**
+ * Formats an int
as a 1-byte unsigned hex value.
+ *
+ * @param v value to format
+ * @return non-null; formatted form
+ */
+ public static String u1(int v) {
+ char[] result = new char[2];
+ for (int i = 0; i < 2; i++) {
+ result[1 - i] = Character.forDigit(v & 0x0f, 16);
+ v >>= 4;
+ }
+
+ return new String(result);
+ }
+
+ /**
+ * Formats an int
as a 4-bit unsigned hex nibble.
+ *
+ * @param v value to format
+ * @return non-null; formatted form
+ */
+ public static String uNibble(int v) {
+ char[] result = new char[1];
+
+ result[0] = Character.forDigit(v & 0x0f, 16);
+ return new String(result);
+ }
+
+ /**
+ * Formats a long
as an 8-byte signed hex value.
+ *
+ * @param v value to format
+ * @return non-null; formatted form
+ */
+ public static String s8(long v) {
+ char[] result = new char[17];
+
+ if (v < 0) {
+ result[0] = '-';
+ v = -v;
+ } else {
+ result[0] = '+';
+ }
+
+ for (int i = 0; i < 16; i++) {
+ result[16 - i] = Character.forDigit((int) v & 0x0f, 16);
+ v >>= 4;
+ }
+
+ return new String(result);
+ }
+
+ /**
+ * Formats an int
as a 4-byte signed hex value.
+ *
+ * @param v value to format
+ * @return non-null; formatted form
+ */
+ public static String s4(int v) {
+ char[] result = new char[9];
+
+ if (v < 0) {
+ result[0] = '-';
+ v = -v;
+ } else {
+ result[0] = '+';
+ }
+
+ for (int i = 0; i < 8; i++) {
+ result[8 - i] = Character.forDigit(v & 0x0f, 16);
+ v >>= 4;
+ }
+
+ return new String(result);
+ }
+
+ /**
+ * Formats an int
as a 2-byte signed hex value.
+ *
+ * @param v value to format
+ * @return non-null; formatted form
+ */
+ public static String s2(int v) {
+ char[] result = new char[5];
+
+ if (v < 0) {
+ result[0] = '-';
+ v = -v;
+ } else {
+ result[0] = '+';
+ }
+
+ for (int i = 0; i < 4; i++) {
+ result[4 - i] = Character.forDigit(v & 0x0f, 16);
+ v >>= 4;
+ }
+
+ return new String(result);
+ }
+
+ /**
+ * Formats an int
as a 1-byte signed hex value.
+ *
+ * @param v value to format
+ * @return non-null; formatted form
+ */
+ public static String s1(int v) {
+ char[] result = new char[3];
+
+ if (v < 0) {
+ result[0] = '-';
+ v = -v;
+ } else {
+ result[0] = '+';
+ }
+
+ for (int i = 0; i < 2; i++) {
+ result[2 - i] = Character.forDigit(v & 0x0f, 16);
+ v >>= 4;
+ }
+
+ return new String(result);
+ }
+
+ /**
+ * Formats a hex dump of a portion of a byte[]
. The result
+ * is always newline-terminated, unless the passed-in length was zero,
+ * in which case the result is always the empty string (""
).
+ *
+ * @param arr non-null; array to format
+ * @param offset >= 0; offset to the part to dump
+ * @param length >= 0; number of bytes to dump
+ * @param outOffset >= 0; first output offset to print
+ * @param bpl >= 0; number of bytes of output per line
+ * @param addressLength {2,4,6,8}; number of characters for each address
+ * header
+ * @return non-null; a string of the dump
+ */
+ public static String dump(byte[] arr, int offset, int length,
+ int outOffset, int bpl, int addressLength) {
+ int end = offset + length;
+
+ // twos-complement math trick: ((x < 0) || (y < 0)) <=> ((x|y) < 0)
+ if (((offset | length | end) < 0) || (end > arr.length)) {
+ throw new IndexOutOfBoundsException("arr.length " +
+ arr.length + "; " +
+ offset + "..!" + end);
+ }
+
+ if (outOffset < 0) {
+ throw new IllegalArgumentException("outOffset < 0");
+ }
+
+ if (length == 0) {
+ return "";
+ }
+
+ StringBuffer sb = new StringBuffer(length * 4 + 6);
+ boolean bol = true;
+ int col = 0;
+
+ while (length > 0) {
+ if (col == 0) {
+ String astr;
+ switch (addressLength) {
+ case 2: astr = Hex.u1(outOffset); break;
+ case 4: astr = Hex.u2(outOffset); break;
+ case 6: astr = Hex.u3(outOffset); break;
+ default: astr = Hex.u4(outOffset); break;
+ }
+ sb.append(astr);
+ sb.append(": ");
+ } else if ((col & 1) == 0) {
+ sb.append(' ');
+ }
+ sb.append(Hex.u1(arr[offset]));
+ outOffset++;
+ offset++;
+ col++;
+ if (col == bpl) {
+ sb.append('\n');
+ col = 0;
+ }
+ length--;
+ }
+
+ if (col != 0) {
+ sb.append('\n');
+ }
+
+ return sb.toString();
+ }
+}
\ No newline at end of file
diff --git a/dexlib/src/main/java/org/jf/dexlib/Util/IndentingWriter.java b/dexlib/src/main/java/org/jf/dexlib/Util/IndentingWriter.java
new file mode 100644
index 00000000..913ab326
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Util/IndentingWriter.java
@@ -0,0 +1,181 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2009 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Util;
+
+import java.io.FilterWriter;
+import java.io.IOException;
+import java.io.Writer;
+
+/**
+ * Writer that wraps another writer and passes width-limited and
+ * optionally-prefixed output to its subordinate. When lines are
+ * wrapped they are automatically indented based on the start of the
+ * line.
+ */
+public final class IndentingWriter extends FilterWriter {
+ /** null-ok; optional prefix for every line */
+ private final String prefix;
+
+ /** > 0; the maximum output width */
+ private final int width;
+
+ /** > 0; the maximum indent */
+ private final int maxIndent;
+
+ /** >= 0; current output column (zero-based) */
+ private int column;
+
+ /** whether indent spaces are currently being collected */
+ private boolean collectingIndent;
+
+ /** >= 0; current indent amount */
+ private int indent;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param out non-null; writer to send final output to
+ * @param width >= 0; the maximum output width (not including
+ * prefix
), or 0
for no maximum
+ * @param prefix non-null; the prefix for each line
+ */
+ public IndentingWriter(Writer out, int width, String prefix) {
+ super(out);
+
+ if (out == null) {
+ throw new NullPointerException("out == null");
+ }
+
+ if (width < 0) {
+ throw new IllegalArgumentException("width < 0");
+ }
+
+ if (prefix == null) {
+ throw new NullPointerException("prefix == null");
+ }
+
+ this.width = (width != 0) ? width : Integer.MAX_VALUE;
+ this.maxIndent = width >> 1;
+ this.prefix = (prefix.length() == 0) ? null : prefix;
+
+ bol();
+ }
+
+ /**
+ * Constructs a no-prefix instance.
+ *
+ * @param out non-null; writer to send final output to
+ * @param width >= 0; the maximum output width (not including
+ * prefix
), or 0
for no maximum
+ */
+ public IndentingWriter(Writer out, int width) {
+ this(out, width, "");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void write(int c) throws IOException {
+ synchronized (lock) {
+ if (collectingIndent) {
+ if (c == ' ') {
+ indent++;
+ if (indent >= maxIndent) {
+ indent = maxIndent;
+ collectingIndent = false;
+ }
+ } else {
+ collectingIndent = false;
+ }
+ }
+
+ if ((column == width) && (c != '\n')) {
+ out.write('\n');
+ column = 0;
+ /*
+ * Note: No else, so this should fall through to the next
+ * if statement.
+ */
+ }
+
+ if (column == 0) {
+ if (prefix != null) {
+ out.write(prefix);
+ }
+
+ if (!collectingIndent) {
+ for (int i = 0; i < indent; i++) {
+ out.write(' ');
+ }
+ column = indent;
+ }
+ }
+
+ out.write(c);
+
+ if (c == '\n') {
+ bol();
+ } else {
+ column++;
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void write(char[] cbuf, int off, int len) throws IOException {
+ synchronized (lock) {
+ while (len > 0) {
+ write(cbuf[off]);
+ off++;
+ len--;
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void write(String str, int off, int len) throws IOException {
+ synchronized (lock) {
+ while (len > 0) {
+ write(str.charAt(off));
+ off++;
+ len--;
+ }
+ }
+ }
+
+ /**
+ * Indicates that output is at the beginning of a line.
+ */
+ private void bol() {
+ column = 0;
+ collectingIndent = (maxIndent != 0);
+ indent = 0;
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Util/Input.java b/dexlib/src/main/java/org/jf/dexlib/Util/Input.java
new file mode 100644
index 00000000..6d52af66
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Util/Input.java
@@ -0,0 +1,156 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2009 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Util;
+
+/**
+ * Interface for a source for binary input. This is similar to
+ * java.util.DataInput
, but no IOExceptions
+ * are declared, and multibyte input is defined to be little-endian.
+ */
+public interface Input {
+ /**
+ * Gets the current cursor position. This is the same as the number of
+ * bytes read from this instance.
+ *
+ * @return >= 0; the cursor position
+ */
+ public int getCursor();
+
+ /**
+ * Sets the current cursor position.
+ *
+ * @return >= 0; the cursor position
+ */
+ public void setCursor(int cursor);
+
+ /**
+ * Asserts that the cursor is the given value.
+ *
+ * @param expectedCursor the expected cursor value
+ * @throws RuntimeException thrown if getCursor() !=
+ * expectedCursor
+ */
+ public void assertCursor(int expectedCursor);
+
+ /**
+ * Reads a byte
from this instance.
+ *
+ * @return the byte value that was read
+ */
+ public byte readByte();
+
+ /**
+ * Reads a short
from this instance.
+ *
+ * @return the short value that was read, as an int
+ */
+ public int readShort();
+
+ /**
+ * Reads an int
from this instance.
+ *
+ * @return the unsigned int value that was read
+ */
+ public int readInt();
+
+ /**
+ * Reads a long
from this instance.
+ *
+ * @return the long value that was read
+ */
+ public long readLong();
+
+
+ /**
+ * Reads a DWARFv3-style signed LEB128 integer. For details,
+ * see the "Dalvik Executable Format" document or DWARF v3 section
+ * 7.6.
+ *
+ * @return the integer value that was read
+ */
+ public int readSignedLeb128();
+
+ /**
+ * Reads a DWARFv3-style unsigned LEB128 integer. For details,
+ * see the "Dalvik Executable Format" document or DWARF v3 section
+ * 7.6.
+ *
+ * @return the integer value that was read
+ */
+ public int readUnsignedLeb128();
+
+ /**
+ * reads a byte[]
from this instance.
+ *
+ * @param bytes non-null; the buffer to read the data into
+ * @param offset >= 0; offset into bytes
for the first
+ * byte to write
+ * @param length >= 0; number of bytes to read
+ */
+ public void read(byte[] bytes, int offset, int length);
+
+ /**
+ * reads a byte[]
from this instance. This is just
+ * a convenient shorthand for read(bytes, 0, bytes.length)
.
+ *
+ * @param bytes non-null; the buffer to read the data into
+ */
+ public void read(byte[] bytes);
+
+
+ /**
+ * reads a byte[]
from this instance
+ *
+ * @param length >= 0; number of bytes to read
+ * @return a byte array containing length
bytes
+ */
+ public byte[] readBytes(int length);
+
+ /**
+ * reads a byte[]
from this instance, from the current cursor up to but not including
+ * the next null (0) byte. The terminating null byte is read and discarded, so that after the read,
+ * the cursor is positioned at the byte immediately after the terminating null
+ */
+ public byte[] readNullTerminatedBytes();
+
+ /**
+ * Skips the given number of bytes.
+ *
+ * @param count >= 0; the number of bytes to skip
+ */
+ public void skipBytes(int count);
+
+ /**
+ * Skip extra bytes if necessary to force alignment of the output
+ * cursor as given.
+ *
+ * @param alignment > 0; the alignment; must be a power of two
+ */
+ public void alignTo(int alignment);
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Util/Leb128Utils.java b/dexlib/src/main/java/org/jf/dexlib/Util/Leb128Utils.java
new file mode 100644
index 00000000..9ed9d367
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Util/Leb128Utils.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2008 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 org.jf.dexlib.Util;
+
+/**
+ * LEB128 (little-endian base 128) utilities.
+ */
+public final class Leb128Utils {
+ /**
+ * This class is uninstantiable.
+ */
+ private Leb128Utils() {
+ // This space intentionally left blank.
+ }
+
+ /**
+ * Gets the number of bytes in the unsigned LEB128 encoding of the
+ * given value.
+ *
+ * @param value the value in question
+ * @return its write size, in bytes
+ */
+ public static int unsignedLeb128Size(int value) {
+ // TODO: This could be much cleverer.
+
+ int remaining = value >> 7;
+ int count = 0;
+
+ while (remaining != 0) {
+ value = remaining;
+ remaining >>= 7;
+ count++;
+ }
+
+ return count + 1;
+ }
+
+ /**
+ * Gets the number of bytes in the signed LEB128 encoding of the
+ * given value.
+ *
+ * @param value the value in question
+ * @return its write size, in bytes
+ */
+ public static int signedLeb128Size(int value) {
+ // TODO: This could be much cleverer.
+
+ int remaining = value >> 7;
+ int count = 0;
+ boolean hasMore = true;
+ int end = ((value & Integer.MIN_VALUE) == 0) ? 0 : -1;
+
+ while (hasMore) {
+ hasMore = (remaining != end)
+ || ((remaining & 1) != ((value >> 6) & 1));
+
+ value = remaining;
+ remaining >>= 7;
+ count++;
+ }
+
+ return count;
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Util/NumberUtils.java b/dexlib/src/main/java/org/jf/dexlib/Util/NumberUtils.java
new file mode 100644
index 00000000..5956bb62
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Util/NumberUtils.java
@@ -0,0 +1,200 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2009 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Util;
+
+public class NumberUtils {
+
+ /**
+ * Decodes the high signed 4-bit nibble from the given byte
+ * @param b the byte to decode
+ * @return the decoded signed nibble
+ */
+ public static byte decodeHighSignedNibble(byte b) {
+ return (byte)(b >> 4);
+ }
+
+ /**
+ * Decodes the low signed 4-bit nibble from the given byte
+ * @param b the byte to decode
+ * @return the decoded signed nibble
+ */
+ public static byte decodeLowSignedNibble(byte b) {
+ return (byte)(((byte)(b << 4)) >> 4);
+ }
+
+ /**
+ * Decodes the high unsigned 4-bit nibble from the given byte
+ * @param b the byte to decode
+ * @return the decoded unsigned nibble
+ */
+ public static byte decodeHighUnsignedNibble(byte b) {
+ return (byte)((b & 0xFF) >>> 4);
+ }
+
+ /**
+ * Decodes the low unsigned 4-bit nibble from the given byte
+ * @param b the byte to decode
+ * @return the decoded unsigned nibble
+ */
+ public static byte decodeLowUnsignedNibble(byte b) {
+ return (byte)(b & 0x0F);
+ }
+
+ /**
+ * Decodes an unsigned byte from a signed byte
+ * @param b the signed byte to decode
+ * @return the decoded unsigned byte as a short
+ */
+ public static short decodeUnsignedByte(byte b) {
+ return (short)(b & 0xFF);
+ }
+
+ /**
+ * Decodes a signed short value from 2 individual bytes
+ * The parameters are in order from least significant byte to most significant byte
+ * @param lsb the least significant byte
+ * @param msb the most significant byte
+ * @return the decoded signed short value
+ */
+ public static short decodeShort(byte lsb, byte msb) {
+ return (short)
+ ( (lsb & 0xFF) |
+ (msb << 8)
+ );
+ }
+
+ /**
+ * Decodes a signed short value in little endian format from the given byte array at the given index.
+ * @param bytes the byte array
+ * @param index the index of the first byte of the signed short value to decode
+ * @return the decoded signed short value
+ */
+ public static short decodeShort(byte[] bytes, int index) {
+ return (short)
+ ( (bytes[index++] & 0xFF) |
+ (bytes[index] << 8)
+ );
+ }
+
+ /**
+ * Decodes an unsigned short value from 2 individual bytes
+ * The parameters are in order from least significant byte to most significant byte
+ * @param lsb the least significant byte
+ * @param msb the most significant byte
+ * @return the decoded unsigned short value as an int
+ */
+ public static int decodeUnsignedShort(byte lsb, byte msb) {
+ return ( (lsb & 0xFF) |
+ ((msb & 0xFF) << 8)
+ );
+ }
+
+ /**
+ * Decodes an unsigned short value in little endian format from the given byte array at the given index.
+ * @param bytes the byte array
+ * @param index the index of the first byte of the unsigned short value to decode
+ * @return the decoded unsigned short value as an int
+ */
+ public static int decodeUnsignedShort(byte[] bytes, int index) {
+ return ( (bytes[index++] & 0xFF) |
+ ((bytes[index] & 0xFF) << 8)
+ );
+ }
+
+ /**
+ * Decodes a signed integer value from 4 individual bytes
+ * The parameters are in order from least significant byte to most significant byte
+ * @param lsb the least significant byte
+ * @param mlsb the middle least significant byte
+ * @param mmsb the middle most significant byte
+ * @param msb the most significant byte
+ * @return the decoded signed integer value
+ */
+ public static int decodeInt(byte lsb, byte mlsb, byte mmsb, byte msb) {
+ return (lsb & 0xFF) |
+ ((mlsb & 0xFF) << 8) |
+ ((mmsb & 0xFF) << 16) |
+ (msb << 24);
+ }
+
+ /**
+ * Decodes a signed integer value in little endian format from the given byte array at the given index.
+ * @param bytes the byte array
+ * @param index the index of the first byte of the signed integer value to decode
+ * @return the decoded signed integer value
+ */
+ public static int decodeInt(byte[] bytes, int index) {
+ return (bytes[index++] & 0xFF) |
+ ((bytes[index++] & 0xFF) << 8) |
+ ((bytes[index++] & 0xFF) << 16) |
+ (bytes[index] << 24);
+ }
+
+ /**
+ * Decodes a signed long value from 8 individual bytes
+ * The parameters are in order from least significant byte to most significant byte
+ * @param llsb the lower least significant byte
+ * @param lmlsb the lower middle least significant byte
+ * @param lmmsb the lower middle most significant byte
+ * @param lgsb the lower greater significant byte
+ * @param glsb the greater least significant byte
+ * @param gmlsb the greater middle least significant byte
+ * @param gmmsb the greater middle most significant byte
+ * @param gmsb the greater most significant byte
+ * @return the decoded signed long value
+ */
+ public static long decodeLong(byte llsb, byte lmlsb, byte lmmsb, byte lgsb, byte glsb, byte gmlsb, byte gmmsb,
+ byte gmsb) {
+ return (llsb & 0xFFL) |
+ ((lmlsb & 0xFFL) << 8) |
+ ((lmmsb & 0xFFL) << 16) |
+ ((lgsb & 0xFFL) << 24) |
+ ((glsb & 0xFFL) << 32) |
+ ((gmlsb & 0xFFL) << 40) |
+ ((gmmsb & 0xFFL) << 48) |
+ (((long)gmsb) << 56);
+ }
+
+ /**
+ * Decodes a signed long value in little endian format from the given byte array at the given index.
+ * @param bytes the byte array
+ * @param index the index of the first byte of the signed long value to decode
+ * @return the decoded signed long value
+ */
+ public static long decodeLong(byte[] bytes, int index) {
+ return (bytes[index++] & 0xFFL) |
+ ((bytes[index++] & 0xFFL) << 8) |
+ ((bytes[index++] & 0xFFL) << 16) |
+ ((bytes[index++] & 0xFFL) << 24) |
+ ((bytes[index++] & 0xFFL) << 32) |
+ ((bytes[index++] & 0xFFL) << 40) |
+ ((bytes[index++] & 0xFFL) << 48) |
+ (((long)bytes[index]) << 56);
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Util/Output.java b/dexlib/src/main/java/org/jf/dexlib/Util/Output.java
new file mode 100644
index 00000000..dcce9d7c
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Util/Output.java
@@ -0,0 +1,141 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2009 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Util;
+
+/**
+ * Interface for a sink for binary output. This is similar to
+ * java.util.DataOutput
, but no IOExceptions
+ * are declared, and multibyte output is defined to be little-endian.
+ */
+public interface Output {
+ /**
+ * Gets the current cursor position. This is the same as the number of
+ * bytes written to this instance.
+ *
+ * @return >= 0; the cursor position
+ */
+ public int getCursor();
+
+ /**
+ * Asserts that the cursor is the given value.
+ *
+ * @param expectedCursor the expected cursor value
+ * @throws RuntimeException thrown if getCursor() !=
+ * expectedCursor
+ */
+ public void assertCursor(int expectedCursor);
+
+ /**
+ * Writes a byte
to this instance.
+ *
+ * @param value the value to write; all but the low 8 bits are ignored
+ */
+ public void writeByte(int value);
+
+ /**
+ * Writes a short
to this instance.
+ *
+ * @param value the value to write; all but the low 16 bits are ignored
+ */
+ public void writeShort(int value);
+
+ /**
+ * Writes an int
to this instance.
+ *
+ * @param value the value to write
+ */
+ public void writeInt(int value);
+
+ /**
+ * Writes a long
to this instance.
+ *
+ * @param value the value to write
+ */
+ public void writeLong(long value);
+
+ /**
+ * Writes a DWARFv3-style unsigned LEB128 integer. For details,
+ * see the "Dalvik Executable Format" document or DWARF v3 section
+ * 7.6.
+ *
+ * @param value value to write, treated as an unsigned value
+ * @return 1..5; the number of bytes actually written
+ */
+ public int writeUnsignedLeb128(int value);
+
+ /**
+ * Writes a DWARFv3-style unsigned LEB128 integer. For details,
+ * see the "Dalvik Executable Format" document or DWARF v3 section
+ * 7.6.
+ *
+ * @param value value to write
+ * @return 1..5; the number of bytes actually written
+ */
+ public int writeSignedLeb128(int value);
+
+ /**
+ * Writes a {@link org.jf.dexlib.Util.ByteArray} to this instance.
+ *
+ * @param bytes non-null; the array to write
+ */
+ public void write(ByteArray bytes);
+
+ /**
+ * Writes a portion of a byte[]
to this instance.
+ *
+ * @param bytes non-null; the array to write
+ * @param offset >= 0; offset into bytes
for the first
+ * byte to write
+ * @param length >= 0; number of bytes to write
+ */
+ public void write(byte[] bytes, int offset, int length);
+
+ /**
+ * Writes a byte[]
to this instance. This is just
+ * a convenient shorthand for write(bytes, 0, bytes.length)
.
+ *
+ * @param bytes non-null; the array to write
+ */
+ public void write(byte[] bytes);
+
+ /**
+ * Writes the given number of 0
bytes.
+ *
+ * @param count >= 0; the number of zeroes to write
+ */
+ public void writeZeroes(int count);
+
+ /**
+ * Adds extra bytes if necessary (with value 0
) to
+ * force alignment of the output cursor as given.
+ *
+ * @param alignment > 0; the alignment; must be a power of two
+ */
+ public void alignTo(int alignment);
+}
\ No newline at end of file
diff --git a/dexlib/src/main/java/org/jf/dexlib/Util/SparseArray.java b/dexlib/src/main/java/org/jf/dexlib/Util/SparseArray.java
new file mode 100644
index 00000000..f94daff3
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Util/SparseArray.java
@@ -0,0 +1,346 @@
+/*
+ * Copyright (C) 2006 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 org.jf.dexlib.Util;
+
+/**
+ * SparseArrays map integers to Objects. Unlike a normal array of Objects,
+ * there can be gaps in the indices. It is intended to be more efficient
+ * than using a HashMap to map Integers to Objects.
+ */
+public class SparseArray {
+ private static final Object DELETED = new Object();
+ private boolean mGarbage = false;
+
+ /**
+ * Creates a new SparseArray containing no mappings.
+ */
+ public SparseArray() {
+ this(10);
+ }
+
+ /**
+ * Creates a new SparseArray containing no mappings that will not
+ * require any additional memory allocation to store the specified
+ * number of mappings.
+ */
+ public SparseArray(int initialCapacity) {
+ mKeys = new int[initialCapacity];
+ mValues = new Object[initialCapacity];
+ mSize = 0;
+ }
+
+ /**
+ * Gets the Object mapped from the specified key, or null
+ * if no such mapping has been made.
+ */
+ public E get(int key) {
+ return get(key, null);
+ }
+
+ /**
+ * Gets the Object mapped from the specified key, or the specified Object
+ * if no such mapping has been made.
+ */
+ public E get(int key, E valueIfKeyNotFound) {
+ int i = binarySearch(mKeys, 0, mSize, key);
+
+ if (i < 0 || mValues[i] == DELETED) {
+ return valueIfKeyNotFound;
+ } else {
+ return (E) mValues[i];
+ }
+ }
+
+ /**
+ * Removes the mapping from the specified key, if there was any.
+ */
+ public void delete(int key) {
+ int i = binarySearch(mKeys, 0, mSize, key);
+
+ if (i >= 0) {
+ if (mValues[i] != DELETED) {
+ mValues[i] = DELETED;
+ mGarbage = true;
+ }
+ }
+ }
+
+ /**
+ * Alias for {@link #delete(int)}.
+ */
+ public void remove(int key) {
+ delete(key);
+ }
+
+ private void gc() {
+ // Log.e("SparseArray", "gc start with " + mSize);
+
+ int n = mSize;
+ int o = 0;
+ int[] keys = mKeys;
+ Object[] values = mValues;
+
+ for (int i = 0; i < n; i++) {
+ Object val = values[i];
+
+ if (val != DELETED) {
+ if (i != o) {
+ keys[o] = keys[i];
+ values[o] = val;
+ }
+
+ o++;
+ }
+ }
+
+ mGarbage = false;
+ mSize = o;
+
+ // Log.e("SparseArray", "gc end with " + mSize);
+ }
+
+ /**
+ * Adds a mapping from the specified key to the specified value,
+ * replacing the previous mapping from the specified key if there
+ * was one.
+ */
+ public void put(int key, E value) {
+ int i = binarySearch(mKeys, 0, mSize, key);
+
+ if (i >= 0) {
+ mValues[i] = value;
+ } else {
+ i = ~i;
+
+ if (i < mSize && mValues[i] == DELETED) {
+ mKeys[i] = key;
+ mValues[i] = value;
+ return;
+ }
+
+ if (mGarbage && mSize >= mKeys.length) {
+ gc();
+
+ // Search again because indices may have changed.
+ i = ~binarySearch(mKeys, 0, mSize, key);
+ }
+
+ if (mSize >= mKeys.length) {
+ int n = Math.max(mSize + 1, mKeys.length * 2);
+
+ int[] nkeys = new int[n];
+ Object[] nvalues = new Object[n];
+
+ // Log.e("SparseArray", "grow " + mKeys.length + " to " + n);
+ System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
+ System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
+
+ mKeys = nkeys;
+ mValues = nvalues;
+ }
+
+ if (mSize - i != 0) {
+ // Log.e("SparseArray", "move " + (mSize - i));
+ System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i);
+ System.arraycopy(mValues, i, mValues, i + 1, mSize - i);
+ }
+
+ mKeys[i] = key;
+ mValues[i] = value;
+ mSize++;
+ }
+ }
+
+ /**
+ * Returns the number of key-value mappings that this SparseArray
+ * currently stores.
+ */
+ public int size() {
+ if (mGarbage) {
+ gc();
+ }
+
+ return mSize;
+ }
+
+ /**
+ * Given an index in the range 0...size()-1
, returns
+ * the key from the index
th key-value mapping that this
+ * SparseArray stores.
+ */
+ public int keyAt(int index) {
+ if (mGarbage) {
+ gc();
+ }
+
+ return mKeys[index];
+ }
+
+ /**
+ * Given an index in the range 0...size()-1
, returns
+ * the value from the index
th key-value mapping that this
+ * SparseArray stores.
+ */
+ public E valueAt(int index) {
+ if (mGarbage) {
+ gc();
+ }
+
+ return (E) mValues[index];
+ }
+
+ /**
+ * Given an index in the range 0...size()-1
, sets a new
+ * value for the index
th key-value mapping that this
+ * SparseArray stores.
+ */
+ public void setValueAt(int index, E value) {
+ if (mGarbage) {
+ gc();
+ }
+
+ mValues[index] = value;
+ }
+
+ /**
+ * Returns the index for which {@link #keyAt} would return the
+ * specified key, or a negative number if the specified
+ * key is not mapped.
+ */
+ public int indexOfKey(int key) {
+ if (mGarbage) {
+ gc();
+ }
+
+ return binarySearch(mKeys, 0, mSize, key);
+ }
+
+ /**
+ * Returns an index for which {@link #valueAt} would return the
+ * specified key, or a negative number if no keys map to the
+ * specified value.
+ * Beware that this is a linear search, unlike lookups by key,
+ * and that multiple keys can map to the same value and this will
+ * find only one of them.
+ */
+ public int indexOfValue(E value) {
+ if (mGarbage) {
+ gc();
+ }
+
+ for (int i = 0; i < mSize; i++)
+ if (mValues[i] == value)
+ return i;
+
+ return -1;
+ }
+
+ /**
+ * Removes all key-value mappings from this SparseArray.
+ */
+ public void clear() {
+ int n = mSize;
+ Object[] values = mValues;
+
+ for (int i = 0; i < n; i++) {
+ values[i] = null;
+ }
+
+ mSize = 0;
+ mGarbage = false;
+ }
+
+ /**
+ * Puts a key/value pair into the array, optimizing for the case where
+ * the key is greater than all existing keys in the array.
+ */
+ public void append(int key, E value) {
+ if (mSize != 0 && key <= mKeys[mSize - 1]) {
+ put(key, value);
+ return;
+ }
+
+ if (mGarbage && mSize >= mKeys.length) {
+ gc();
+ }
+
+ int pos = mSize;
+ if (pos >= mKeys.length) {
+ int n = Math.max(pos + 1, mKeys.length * 2);
+
+ int[] nkeys = new int[n];
+ Object[] nvalues = new Object[n];
+
+ // Log.e("SparseArray", "grow " + mKeys.length + " to " + n);
+ System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
+ System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
+
+ mKeys = nkeys;
+ mValues = nvalues;
+ }
+
+ mKeys[pos] = key;
+ mValues[pos] = value;
+ mSize = pos + 1;
+ }
+
+ /**
+ * Increases the size of the underlying storage if needed, to ensure that it can
+ * hold the specified number of items without having to allocate additional memory
+ * @param capacity the number of items
+ */
+ public void ensureCapacity(int capacity) {
+ if (mGarbage && mSize >= mKeys.length) {
+ gc();
+ }
+
+ if (mKeys.length < capacity) {
+ int[] nkeys = new int[capacity];
+ Object[] nvalues = new Object[capacity];
+
+ System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
+ System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
+
+ mKeys = nkeys;
+ mValues = nvalues;
+ }
+ }
+
+ private static int binarySearch(int[] a, int start, int len, int key) {
+ int high = start + len, low = start - 1, guess;
+
+ while (high - low > 1) {
+ guess = (high + low) / 2;
+
+ if (a[guess] < key)
+ low = guess;
+ else
+ high = guess;
+ }
+
+ if (high == start + len)
+ return ~(start + len);
+ else if (a[high] == key)
+ return high;
+ else
+ return ~high;
+ }
+
+ private int[] mKeys;
+ private Object[] mValues;
+ private int mSize;
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Util/TwoColumnOutput.java b/dexlib/src/main/java/org/jf/dexlib/Util/TwoColumnOutput.java
new file mode 100644
index 00000000..243aada7
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Util/TwoColumnOutput.java
@@ -0,0 +1,262 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2009 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Util;
+
+import java.io.*;
+
+/**
+ * Class that takes a combined output destination and provides two
+ * output writers, one of which ends up writing to the left column and
+ * one which goes on the right.
+ */
+public final class TwoColumnOutput {
+ /** non-null; underlying writer for final output */
+ private final Writer out;
+
+ /** > 0; the left column width */
+ private final int leftWidth;
+
+ /** non-null; pending left column output */
+ private final StringBuffer leftBuf;
+
+ /** non-null; pending right column output */
+ private final StringBuffer rightBuf;
+
+ /** non-null; left column writer */
+ private final IndentingWriter leftColumn;
+
+ /** non-null; right column writer */
+ private final IndentingWriter rightColumn;
+
+ /**
+ * Turns the given two strings (with widths) and spacer into a formatted
+ * two-column string.
+ *
+ * @param s1 non-null; first string
+ * @param width1 > 0; width of the first column
+ * @param spacer non-null; spacer string
+ * @param s2 non-null; second string
+ * @param width2 > 0; width of the second column
+ * @return non-null; an appropriately-formatted string
+ */
+ public static String toString(String s1, int width1, String spacer,
+ String s2, int width2) {
+ int len1 = s1.length();
+ int len2 = s2.length();
+
+ StringWriter sw = new StringWriter((len1 + len2) * 3);
+ TwoColumnOutput twoOut =
+ new TwoColumnOutput(sw, width1, width2, spacer);
+
+ try {
+ twoOut.getLeft().write(s1);
+ twoOut.getRight().write(s2);
+ } catch (IOException ex) {
+ throw new RuntimeException("shouldn't happen", ex);
+ }
+
+ twoOut.flush();
+ return sw.toString();
+ }
+
+ /**
+ * Constructs an instance.
+ *
+ * @param out non-null; writer to send final output to
+ * @param leftWidth > 0; width of the left column, in characters
+ * @param rightWidth > 0; width of the right column, in characters
+ * @param spacer non-null; spacer string to sit between the two columns
+ */
+ public TwoColumnOutput(Writer out, int leftWidth, int rightWidth,
+ String spacer) {
+ if (out == null) {
+ throw new NullPointerException("out == null");
+ }
+
+ if (leftWidth < 1) {
+ throw new IllegalArgumentException("leftWidth < 1");
+ }
+
+ if (rightWidth < 1) {
+ throw new IllegalArgumentException("rightWidth < 1");
+ }
+
+ if (spacer == null) {
+ throw new NullPointerException("spacer == null");
+ }
+
+ StringWriter leftWriter = new StringWriter(1000);
+ StringWriter rightWriter = new StringWriter(1000);
+
+ this.out = out;
+ this.leftWidth = leftWidth;
+ this.leftBuf = leftWriter.getBuffer();
+ this.rightBuf = rightWriter.getBuffer();
+ this.leftColumn = new IndentingWriter(leftWriter, leftWidth);
+ this.rightColumn =
+ new IndentingWriter(rightWriter, rightWidth, spacer);
+ }
+
+ /**
+ * Constructs an instance.
+ *
+ * @param out non-null; stream to send final output to
+ * @param leftWidth >= 1; width of the left column, in characters
+ * @param rightWidth >= 1; width of the right column, in characters
+ * @param spacer non-null; spacer string to sit between the two columns
+ */
+ public TwoColumnOutput(OutputStream out, int leftWidth, int rightWidth,
+ String spacer) {
+ this(new OutputStreamWriter(out), leftWidth, rightWidth, spacer);
+ }
+
+ /**
+ * Gets the writer to use to write to the left column.
+ *
+ * @return non-null; the left column writer
+ */
+ public Writer getLeft() {
+ return leftColumn;
+ }
+
+ /**
+ * Gets the writer to use to write to the right column.
+ *
+ * @return non-null; the right column writer
+ */
+ public Writer getRight() {
+ return rightColumn;
+ }
+
+ /**
+ * Flushes the output. If there are more lines of pending output in one
+ * column, then the other column will get filled with blank lines.
+ */
+ public void flush() {
+ try {
+ appendNewlineIfNecessary(leftBuf, leftColumn);
+ appendNewlineIfNecessary(rightBuf, rightColumn);
+ outputFullLines();
+ flushLeft();
+ flushRight();
+ } catch (IOException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ /**
+ * Outputs to the final destination as many full line pairs as
+ * there are in the pending output, removing those lines from
+ * their respective buffers. This method terminates when at
+ * least one of the two column buffers is empty.
+ */
+ private void outputFullLines() throws IOException {
+ for (;;) {
+ int leftLen = leftBuf.indexOf("\n");
+ if (leftLen < 0) {
+ return;
+ }
+
+ int rightLen = rightBuf.indexOf("\n");
+ if (rightLen < 0) {
+ return;
+ }
+
+ if (leftLen != 0) {
+ out.write(leftBuf.substring(0, leftLen));
+ }
+
+ if (rightLen != 0) {
+ writeSpaces(out, leftWidth - leftLen);
+ out.write(rightBuf.substring(0, rightLen));
+ }
+
+ out.write('\n');
+
+ leftBuf.delete(0, leftLen + 1);
+ rightBuf.delete(0, rightLen + 1);
+ }
+ }
+
+ /**
+ * Flushes the left column buffer, printing it and clearing the buffer.
+ * If the buffer is already empty, this does nothing.
+ */
+ private void flushLeft() throws IOException {
+ appendNewlineIfNecessary(leftBuf, leftColumn);
+
+ while (leftBuf.length() != 0) {
+ rightColumn.write('\n');
+ outputFullLines();
+ }
+ }
+
+ /**
+ * Flushes the right column buffer, printing it and clearing the buffer.
+ * If the buffer is already empty, this does nothing.
+ */
+ private void flushRight() throws IOException {
+ appendNewlineIfNecessary(rightBuf, rightColumn);
+
+ while (rightBuf.length() != 0) {
+ leftColumn.write('\n');
+ outputFullLines();
+ }
+ }
+
+ /**
+ * Appends a newline to the given buffer via the given writer, but
+ * only if it isn't empty and doesn't already end with one.
+ *
+ * @param buf non-null; the buffer in question
+ * @param out non-null; the writer to use
+ */
+ private static void appendNewlineIfNecessary(StringBuffer buf,
+ Writer out)
+ throws IOException {
+ int len = buf.length();
+
+ if ((len != 0) && (buf.charAt(len - 1) != '\n')) {
+ out.write('\n');
+ }
+ }
+
+ /**
+ * Writes the given number of spaces to the given writer.
+ *
+ * @param out non-null; where to write
+ * @param amt >= 0; the number of spaces to write
+ */
+ private static void writeSpaces(Writer out, int amt) throws IOException {
+ while (amt > 0) {
+ out.write(' ');
+ amt--;
+ }
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Util/Utf8Utils.java b/dexlib/src/main/java/org/jf/dexlib/Util/Utf8Utils.java
new file mode 100644
index 00000000..eb59386b
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Util/Utf8Utils.java
@@ -0,0 +1,192 @@
+/*
+ * 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 org.jf.dexlib.Util;
+
+/**
+ * Constants of type CONSTANT_Utf8_info
.
+ */
+public final class Utf8Utils {
+
+
+ /**
+ * Converts a string into its Java-style UTF-8 form. Java-style UTF-8
+ * differs from normal UTF-8 in the handling of character '\0' and
+ * surrogate pairs.
+ *
+ * @param string non-null; the string to convert
+ * @return non-null; the UTF-8 bytes for it
+ */
+ public static byte[] stringToUtf8Bytes(String string) {
+ int len = string.length();
+ byte[] bytes = new byte[len * 3]; // Avoid having to reallocate.
+ int outAt = 0;
+
+ for (int i = 0; i < len; i++) {
+ char c = string.charAt(i);
+ if ((c != 0) && (c < 0x80)) {
+ bytes[outAt] = (byte) c;
+ outAt++;
+ } else if (c < 0x800) {
+ bytes[outAt] = (byte) (((c >> 6) & 0x1f) | 0xc0);
+ bytes[outAt + 1] = (byte) ((c & 0x3f) | 0x80);
+ outAt += 2;
+ } else {
+ bytes[outAt] = (byte) (((c >> 12) & 0x0f) | 0xe0);
+ bytes[outAt + 1] = (byte) (((c >> 6) & 0x3f) | 0x80);
+ bytes[outAt + 2] = (byte) ((c & 0x3f) | 0x80);
+ outAt += 3;
+ }
+ }
+
+ byte[] result = new byte[outAt];
+ System.arraycopy(bytes, 0, result, 0, outAt);
+ return result;
+ }
+
+ /**
+ * Converts an array of UTF-8 bytes into a string.
+ *
+ * @param bytes non-null; the bytes to convert
+ * @return non-null; the converted string
+ */
+ public static String utf8BytesToString(byte[] bytes) {
+ int length = bytes.length;
+ char[] chars = new char[length]; // This is sized to avoid a realloc.
+ int outAt = 0;
+
+ for (int at = 0; length > 0; /*at*/) {
+ int v0 = bytes[at] & 0xFF;
+ char out;
+ switch (v0 >> 4) {
+ case 0x00: case 0x01: case 0x02: case 0x03:
+ case 0x04: case 0x05: case 0x06: case 0x07: {
+ // 0XXXXXXX -- single-byte encoding
+ length--;
+ if (v0 == 0) {
+ // A single zero byte is illegal.
+ return throwBadUtf8(v0, at);
+ }
+ out = (char) v0;
+ at++;
+ break;
+ }
+ case 0x0c: case 0x0d: {
+ // 110XXXXX -- two-byte encoding
+ length -= 2;
+ if (length < 0) {
+ return throwBadUtf8(v0, at);
+ }
+ int v1 = bytes[at + 1] & 0xFF;
+ if ((v1 & 0xc0) != 0x80) {
+ return throwBadUtf8(v1, at + 1);
+ }
+ int value = ((v0 & 0x1f) << 6) | (v1 & 0x3f);
+ if ((value != 0) && (value < 0x80)) {
+ /*
+ * This should have been represented with
+ * one-byte encoding.
+ */
+ return throwBadUtf8(v1, at + 1);
+ }
+ out = (char) value;
+ at += 2;
+ break;
+ }
+ case 0x0e: {
+ // 1110XXXX -- three-byte encoding
+ length -= 3;
+ if (length < 0) {
+ return throwBadUtf8(v0, at);
+ }
+ int v1 = bytes[at + 1] & 0xFF;
+ if ((v1 & 0xc0) != 0x80) {
+ return throwBadUtf8(v1, at + 1);
+ }
+ int v2 = bytes[at + 2] & 0xFF;
+ if ((v1 & 0xc0) != 0x80) {
+ return throwBadUtf8(v2, at + 2);
+ }
+ int value = ((v0 & 0x0f) << 12) | ((v1 & 0x3f) << 6) |
+ (v2 & 0x3f);
+ if (value < 0x800) {
+ /*
+ * This should have been represented with one- or
+ * two-byte encoding.
+ */
+ return throwBadUtf8(v2, at + 2);
+ }
+ out = (char) value;
+ at += 3;
+ break;
+ }
+ default: {
+ // 10XXXXXX, 1111XXXX -- illegal
+ return throwBadUtf8(v0, at);
+ }
+ }
+ chars[outAt] = out;
+ outAt++;
+ }
+
+ return new String(chars, 0, outAt);
+ }
+
+ /**
+ * Helper for {@link #utf8BytesToString}, which throws the right
+ * exception for a bogus utf-8 byte.
+ *
+ * @param value the byte value
+ * @param offset the file offset
+ * @return never
+ * @throws IllegalArgumentException always thrown
+ */
+ private static String throwBadUtf8(int value, int offset) {
+ throw new IllegalArgumentException("bad utf-8 byte " + Hex.u1(value) +
+ " at offset " + Hex.u4(offset));
+ }
+
+ public static String escapeString(String value) {
+ int len = value.length();
+ StringBuilder sb = new StringBuilder(len * 3 / 2);
+
+ for (int i = 0; i < len; i++) {
+ char c = value.charAt(i);
+
+ if ((c >= ' ') && (c < 0x7f)) {
+ if ((c == '\'') || (c == '\"') || (c == '\\')) {
+ sb.append('\\');
+ }
+ sb.append(c);
+ continue;
+ } else if (c <= 0x7f) {
+ switch (c) {
+ case '\n': sb.append("\\n"); continue;
+ case '\r': sb.append("\\r"); continue;
+ case '\t': sb.append("\\t"); continue;
+ }
+ }
+
+ sb.append("\\u");
+ sb.append(Character.forDigit(c >> 12, 16));
+ sb.append(Character.forDigit((c >> 8) & 0x0f, 16));
+ sb.append(Character.forDigit((c >> 4) & 0x0f, 16));
+ sb.append(Character.forDigit(c & 0x0f, 16));
+ }
+
+ return sb.toString();
+ }
+}