diff --git a/dexlib/src/main/java/org/jf/dexlib/AnnotationDirectoryItem.java b/dexlib/src/main/java/org/jf/dexlib/AnnotationDirectoryItem.java new file mode 100644 index 00000000..af467335 --- /dev/null +++ b/dexlib/src/main/java/org/jf/dexlib/AnnotationDirectoryItem.java @@ -0,0 +1,337 @@ +/* + * [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.ArrayUtils; +import org.jf.dexlib.Util.Input; +import org.jf.dexlib.Util.AnnotatedOutput; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class AnnotationDirectoryItem extends Item { + private AnnotationSetItem classAnnotations; + + private FieldIdItem[] fieldAnnotationFields; + private AnnotationSetItem[] fieldAnnotations; + + private MethodIdItem[] methodAnnotationMethods; + private AnnotationSetItem[] methodAnnotations; + + private MethodIdItem[] parameterAnnotationMethods; + private AnnotationSetRefList[] parameterAnnotations; + + /** + * typically each AnnotationDirectoryItem will have a distinct parent. The only case that isn't true is when + * the AnnotationDirectoryItem *only* contains class annotations, with no other type of annotation. In that + * case, the same AnnotationDirectoryItem could be referenced from multiple classes. + * This isn't a problem though, because this field is only used in compareTo to determine the sort order, + * which handles it as a special case + */ + private ClassDefItem parent = null; + + /** + * Creates a new uninitialized 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; iAnnotationDirectoryItem is internable. It is only internable if it has + * only class annotations, but no field, method or parameter annotations + */ + private boolean isInternable() { + return classAnnotations != null && + fieldAnnotations.length == 0 && + methodAnnotations.length == 0 && + parameterAnnotations.length == 0; + } + + /** + * Sets the ClassDefItem 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 Item { + private int hashCode = 0; + + private AnnotationVisibility visibility; + private AnnotationEncodedSubValue annotationValue; + + /** + * Creates a new uninitialized AnnotationItem + * @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 Item { + private int hashCode = 0; + + private AnnotationItem[] annotations; + + /** + * Creates a new uninitialized AnnotationSetItem + * @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; iAnnotationItem objects in this AnnotationSetItem + */ + 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 Item { + private int hashCode = 0; + + private AnnotationSetItem[] annotationSets; + + /** + * Creates a new uninitialized AnnotationSetRefList + * @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; iAnnotationSetItem objects that make up this + * AnnotationSetRefList + */ + 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 Item { + private EncodedField[] staticFields; + private EncodedField[] instanceFields; + private EncodedMethod[] directMethods; + private EncodedMethod[] virtualMethods; + + private ClassDefItem parent = null; + + /** + * Creates a new uninitialized ClassDataItem + * @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; iClassDefItem that this ClassDataItem 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 Item { + private TypeIdItem classType; + private int accessFlags; + private TypeIdItem superType; + private TypeListItem implementedInterfaces; + private StringIdItem sourceFile; + private AnnotationDirectoryItem annotations; + private ClassDataItem classData; + private EncodedArrayItem staticFieldInitializers; + + /** + * Creates a new uninitialized ClassDefItem + * @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(IndexedSection section, int offset) { + ClassDefPlacer cdp = new ClassDefPlacer(section); + return cdp.placeSection(offset); + } + + /** + * This class places the items within a ClassDefItem section, such that superclasses and interfaces are + * placed before sub/implementing classes + */ + private static class ClassDefPlacer { + private final IndexedSection section; + private final HashMap unplacedClassDefsByType = + new HashMap(); + + private int currentIndex = 0; + private int currentOffset; + + public ClassDefPlacer(IndexedSection section) { + this.section = section; + + for (ClassDefItem classDefItem: section.items) { + TypeIdItem typeIdItem = classDefItem.classType; + unplacedClassDefsByType.put(typeIdItem, classDefItem); + } + } + + public int placeSection(int offset) { + currentOffset = offset; + + if (section.DexFile.getSortAllItems()) { + //presort the list, to guarantee a unique ordering + Collections.sort(section.items, new Comparator() { + public int compare(ClassDefItem a, ClassDefItem b) { + return a.getClassType().compareTo(b.getClassType()); + } + }); + } + + for (ClassDefItem classDefItem: section.items) { + placeClass(classDefItem); + } + + for (ClassDefItem classDefItem: unplacedClassDefsByType.values()) { + section.items.set(classDefItem.getIndex(), classDefItem); + } + + return currentOffset; + } + + private void placeClass(ClassDefItem classDefItem) { + if (classDefItem.getOffset() == -1) { + TypeIdItem superType = classDefItem.superType; + ClassDefItem superClassDefItem = unplacedClassDefsByType.get(superType); + + if (superClassDefItem != null) { + placeClass(superClassDefItem); + } + + TypeListItem interfaces = classDefItem.implementedInterfaces; + + if (interfaces != null) { + for (TypeIdItem interfaceType: interfaces.getTypes()) { + ClassDefItem interfaceClass = unplacedClassDefsByType.get(interfaceType); + if (interfaceClass != null) { + placeClass(interfaceClass); + } + } + } + + currentOffset = classDefItem.placeAt(currentIndex++, currentOffset); + unplacedClassDefsByType.remove(classDefItem.classType); + } + } + + } +} diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/ArrayDataPseudoInstruction.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/ArrayDataPseudoInstruction.java new file mode 100644 index 00000000..da70b040 --- /dev/null +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/ArrayDataPseudoInstruction.java @@ -0,0 +1,137 @@ +/* + * [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.Format; + +import org.jf.dexlib.Code.Instruction; +import org.jf.dexlib.Code.Opcode; +import org.jf.dexlib.Util.NumberUtils; +import org.jf.dexlib.DexFile; + +import java.util.Iterator; + +public class ArrayDataPseudoInstruction extends Instruction { + public static final Instruction.InstructionFactory Factory = new Factory(); + + @Override + public int getSize() { + int size = getElementWidth() * getElementCount(); + return size + (size & 0x01) + 8; + } + + public ArrayDataPseudoInstruction(int elementWidth, byte[] encodedValues) { + super(Opcode.NOP, encodedValues.length + (encodedValues.length & 1) + 8); + + if (encodedValues.length % elementWidth != 0) { + throw new RuntimeException("There are not a whole number of " + elementWidth + " byte elements"); + } + + int elementCount = buffer.length / elementWidth; + + buffer[0] = 0x00; + buffer[1] = 0x03; //fill-array-data psuedo-opcode + + buffer[2] = (byte) elementWidth; + buffer[3] = (byte) (elementWidth >> 8); + + buffer[4] = (byte) elementCount; + buffer[5] = (byte) (elementCount >> 8); + buffer[6] = (byte) (elementCount >> 16); + buffer[7] = (byte) (elementCount >> 24); + + System.arraycopy(encodedValues, 0, buffer, 8, encodedValues.length); + } + + public ArrayDataPseudoInstruction(byte[] buffer, int bufferIndex) { + super(Opcode.NOP, buffer, bufferIndex); + + byte opcodeByte = buffer[bufferIndex++]; + if (opcodeByte != 0x00) { + throw new RuntimeException("Invalid opcode byte for an ArrayData pseudo-instruction"); + } + + byte subopcodeByte = buffer[bufferIndex]; + if (subopcodeByte != 0x03) { + throw new RuntimeException("Invalid sub-opcode byte for an ArrayData pseudo-instruction"); + } + } + + public Format getFormat() { + return Format.ArrayData; + } + + public int getElementWidth() { + return NumberUtils.decodeUnsignedShort(buffer[bufferIndex+2], buffer[bufferIndex+3]); + } + + public int getElementCount() { + return NumberUtils.decodeInt(buffer, bufferIndex+4); + } + + public static class ArrayElement { + public final byte[] buffer; + public int bufferIndex; + public final int elementWidth; + public ArrayElement(byte[] buffer, int elementWidth) { + this.buffer = buffer; + this.elementWidth = elementWidth; + } + } + + public Iterator getElements() { + return new Iterator() { + final int elementCount = getElementCount(); + int i=0; + int position = bufferIndex + 8; + final ArrayElement arrayElement = new ArrayElement(buffer, getElementWidth()); + + public boolean hasNext() { + return i= 1 << 4) { + throw new RuntimeException("The register number must be less than v16"); + } + + if (litB < -(1 << 3) || + litB >= 1 << 3) { + throw new RuntimeException("The literal value must be between -8 and 7 inclusive"); + } + + buffer[0] = opcode.value; + buffer[1] = (byte) ((litB << 4) | regA); + } + + private Instruction11n(Opcode opcode, byte[] buffer, int bufferIndex) { + super(opcode, buffer, bufferIndex); + } + + public Format getFormat() { + return Format.Format11n; + } + + public byte getRegister() { + return NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 1]); + } + + public byte getLiteral() { + return NumberUtils.decodeHighSignedNibble(buffer[bufferIndex + 1]); + } + + private static class Factory implements Instruction.InstructionFactory { + public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + return new Instruction11n(opcode, buffer, bufferIndex); + } + } +} \ No newline at end of file diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction11x.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction11x.java new file mode 100644 index 00000000..d9da5578 --- /dev/null +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction11x.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.Code.Format; + +import org.jf.dexlib.Code.Instruction; +import org.jf.dexlib.Code.Opcode; +import org.jf.dexlib.DexFile; +import org.jf.dexlib.Util.NumberUtils; + +public class Instruction11x extends Instruction { + public static final Instruction.InstructionFactory Factory = new Factory(); + + public Instruction11x(Opcode opcode, short regA) { + super(opcode); + + if (regA >= 1 << 8) { + throw new RuntimeException("The register number must be less than v256"); + } + + buffer[0] = opcode.value; + buffer[1] = (byte) regA; + } + + private Instruction11x(Opcode opcode, byte[] buffer, int bufferIndex) { + super(opcode, buffer, bufferIndex); + } + + public Format getFormat() { + return Format.Format11x; + } + + public short getRegister() { + return NumberUtils.decodeUnsignedByte(buffer[bufferIndex + 1]); + } + + private static class Factory implements Instruction.InstructionFactory { + public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + return new Instruction11x(opcode, buffer, bufferIndex); + } + } +} diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction12x.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction12x.java new file mode 100644 index 00000000..0ca772f7 --- /dev/null +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction12x.java @@ -0,0 +1,72 @@ +/* + * [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.Format; + +import org.jf.dexlib.Code.Instruction; +import org.jf.dexlib.Code.Opcode; +import org.jf.dexlib.DexFile; +import org.jf.dexlib.Util.NumberUtils; + +public class Instruction12x extends Instruction { + public static final Instruction.InstructionFactory Factory = new Factory(); + + public Instruction12x(Opcode opcode, byte regA, byte regB) { + super(opcode); + + if (regA >= 1 << 4 || + regB >= 1 << 4) { + throw new RuntimeException("The register number must be less than v16"); + } + + buffer[0] = opcode.value; + buffer[1] = (byte) ((regB << 4) | regA); + } + + private Instruction12x(Opcode opcode, byte[] buffer, int bufferIndex) { + super(opcode, buffer, bufferIndex); + } + + public Format getFormat() { + return Format.Format12x; + } + + public byte getRegisterA() { + return NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 1]); + } + + public byte getRegisterB() { + return NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 1]); + } + + private static class Factory implements Instruction.InstructionFactory { + public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + return new Instruction12x(opcode, buffer, bufferIndex); + } + } +} diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction20t.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction20t.java new file mode 100644 index 00000000..c0cf09dd --- /dev/null +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction20t.java @@ -0,0 +1,72 @@ +/* + * [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.Format; + +import org.jf.dexlib.Code.Instruction; +import org.jf.dexlib.Code.Opcode; +import org.jf.dexlib.DexFile; +import org.jf.dexlib.Util.NumberUtils; + +public class Instruction20t extends Instruction { + public static final Instruction.InstructionFactory Factory = new Factory(); + + public Instruction20t(Opcode opcode, short offA) { + super(opcode); + + if (offA == 0) { + throw new RuntimeException("The offset cannot be 0. Use goto/32 instead."); + } + + buffer[0] = opcode.value; + buffer[2] = (byte) offA; + buffer[3] = (byte) (offA >> 8); + } + + private Instruction20t(Opcode opcode, byte[] buffer, int bufferIndex) { + super(opcode, buffer, bufferIndex); + + if (getOffset() == 0) { + throw new RuntimeException("The offset cannot be 0. Use goto/32 instead."); + } + } + + public Format getFormat() { + return Format.Format20t; + } + + public short getOffset() { + return NumberUtils.decodeShort(buffer, bufferIndex + 2); + } + + private static class Factory implements Instruction.InstructionFactory { + public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + return new Instruction20t(opcode, buffer, bufferIndex); + } + } +} diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21c.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21c.java new file mode 100644 index 00000000..ad849dab --- /dev/null +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21c.java @@ -0,0 +1,79 @@ +/* + * [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.Format; + +import org.jf.dexlib.Code.Instruction; +import org.jf.dexlib.Code.InstructionWithReference; +import org.jf.dexlib.Code.Opcode; +import org.jf.dexlib.DexFile; +import org.jf.dexlib.Item; +import org.jf.dexlib.TypeIdItem; +import org.jf.dexlib.Util.NumberUtils; + +public class Instruction21c extends InstructionWithReference { + public static final Instruction.InstructionFactory Factory = new Factory(); + + public Instruction21c(Opcode opcode, short regA, Item referencedItem) { + super(opcode, referencedItem); + + if (regA >= 1 << 8) { + throw new RuntimeException("The register number must be less than v256"); + } + + if (opcode == Opcode.NEW_INSTANCE && ((TypeIdItem) referencedItem).getTypeDescriptor().charAt(0) != 'L') { + throw new RuntimeException("Only class references can be used with the new-instance opcode"); + } + + buffer[0] = opcode.value; + buffer[1] = (byte) regA; + //the item index will be set later, during placement/writing + } + + private Instruction21c(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + super(dexFile, opcode, buffer, bufferIndex); + + if (opcode == Opcode.NEW_INSTANCE && ((TypeIdItem) this.getReferencedItem()).getTypeDescriptor().charAt(0) != 'L') { + throw new RuntimeException("Only class references can be used with the new-instance opcode"); + } + } + + public Format getFormat() { + return Format.Format21c; + } + + public short getRegister() { + return NumberUtils.decodeUnsignedByte(buffer[bufferIndex + 1]); + } + + private static class Factory implements Instruction.InstructionFactory { + public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + return new Instruction21c(dexFile, opcode, buffer, bufferIndex); + } + } +} diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21h.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21h.java new file mode 100644 index 00000000..37003d55 --- /dev/null +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21h.java @@ -0,0 +1,73 @@ +/* + * [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.Format; + +import org.jf.dexlib.Code.Instruction; +import org.jf.dexlib.Code.Opcode; +import org.jf.dexlib.DexFile; +import org.jf.dexlib.Util.NumberUtils; + +public class Instruction21h extends Instruction { + public static final Instruction.InstructionFactory Factory = new Factory(); + + public Instruction21h(Opcode opcode, short regA, short litB) { + super(opcode); + + if (regA >= 1 << 8) { + throw new RuntimeException("The register number must be less than v256"); + } + + buffer[0] = opcode.value; + buffer[1] = (byte) regA; + buffer[2] = (byte) litB; + buffer[3] = (byte) (litB >> 8); + } + + private Instruction21h(Opcode opcode, byte[] buffer, int bufferIndex) { + super(opcode, buffer, bufferIndex); + } + + public Format getFormat() { + return Format.Format21h; + } + + public short getRegister() { + return NumberUtils.decodeUnsignedByte(buffer[bufferIndex + 1]); + } + + public short getLiteral() { + return NumberUtils.decodeShort(buffer, bufferIndex + 2); + } + + private static class Factory implements Instruction.InstructionFactory { + public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + return new Instruction21h(opcode, buffer, bufferIndex); + } + } +} diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21s.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21s.java new file mode 100644 index 00000000..e702f017 --- /dev/null +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21s.java @@ -0,0 +1,73 @@ +/* + * [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.Format; + +import org.jf.dexlib.Code.Instruction; +import org.jf.dexlib.Code.Opcode; +import org.jf.dexlib.DexFile; +import org.jf.dexlib.Util.NumberUtils; + +public class Instruction21s extends Instruction { + public static final Instruction.InstructionFactory Factory = new Factory(); + + public Instruction21s(Opcode opcode, short regA, short litB) { + super(opcode); + + if (regA >= 1 << 8) { + throw new RuntimeException("The register number must be less than v256"); + } + + buffer[0] = opcode.value; + buffer[1] = (byte) regA; + buffer[2] = (byte) litB; + buffer[3] = (byte) (litB >> 8); + } + + private Instruction21s(Opcode opcode, byte[] buffer, int bufferIndex) { + super(opcode, buffer, bufferIndex); + } + + public Format getFormat() { + return Format.Format21s; + } + + public short getRegister() { + return NumberUtils.decodeUnsignedByte(buffer[bufferIndex + 1]); + } + + public short getLiteral() { + return NumberUtils.decodeShort(buffer, bufferIndex + 2); + } + + private static class Factory implements Instruction.InstructionFactory { + public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + return new Instruction21s(opcode, buffer, bufferIndex); + } + } +} diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21t.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21t.java new file mode 100644 index 00000000..b9673b56 --- /dev/null +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21t.java @@ -0,0 +1,81 @@ +/* + * [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.Format; + +import org.jf.dexlib.Code.Instruction; +import org.jf.dexlib.Code.Opcode; +import org.jf.dexlib.DexFile; +import org.jf.dexlib.Util.NumberUtils; + +public class Instruction21t extends Instruction { + public static final Instruction.InstructionFactory Factory = new Factory(); + + public Instruction21t(Opcode opcode, short regA, short offB) { + super(opcode); + + if (regA >= 1 << 8) { + throw new RuntimeException("The register number must be less than v256"); + } + + if (offB == 0) { + throw new RuntimeException("The offset cannot be 0."); + } + + buffer[0] = opcode.value; + buffer[1] = (byte) regA; + buffer[2] = (byte) offB; + buffer[3] = (byte) (offB >> 8); + } + + private Instruction21t(Opcode opcode, byte[] buffer, int bufferIndex) { + super(opcode, buffer, bufferIndex); + + if (getOffset() == 0) { + throw new RuntimeException("The offset cannot be 0."); + } + } + + public Format getFormat() { + return Format.Format21t; + } + + public short getRegister() { + return NumberUtils.decodeUnsignedByte(buffer[bufferIndex + 1]); + } + + public short getOffset() { + return NumberUtils.decodeShort(buffer, bufferIndex + 2); + } + + private static class Factory implements Instruction.InstructionFactory { + public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + return new Instruction21t(opcode, buffer, bufferIndex); + } + } +} diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22b.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22b.java new file mode 100644 index 00000000..96432da6 --- /dev/null +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22b.java @@ -0,0 +1,78 @@ +/* + * [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.Format; + +import org.jf.dexlib.Code.Instruction; +import org.jf.dexlib.Code.Opcode; +import org.jf.dexlib.DexFile; +import org.jf.dexlib.Util.NumberUtils; + +public class Instruction22b extends Instruction { + public static final Instruction.InstructionFactory Factory = new Factory(); + + public Instruction22b(Opcode opcode, short regA, short regB, byte litC) { + super(opcode); + + if (regA >= 1 << 8 || + regB >= 1 << 8) { + throw new RuntimeException("The register number must be less than v256"); + } + + buffer[0] = opcode.value; + buffer[1] = (byte) regA; + buffer[2] = (byte) regB; + buffer[3] = litC; + } + + private Instruction22b(Opcode opcode, byte[] buffer, int bufferIndex) { + super(opcode, buffer, bufferIndex); + } + + public Format getFormat() { + return Format.Format22b; + } + + public short getRegisterA() { + return NumberUtils.decodeUnsignedByte(buffer[bufferIndex + 1]); + } + + public short getRegisterB() { + return NumberUtils.decodeUnsignedByte(buffer[bufferIndex + 2]); + } + + public byte getLiteral() { + return buffer[bufferIndex + 3]; + } + + private static class Factory implements Instruction.InstructionFactory { + public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + return new Instruction22b(opcode, buffer, bufferIndex); + } + } +} diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22c.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22c.java new file mode 100644 index 00000000..061ea96a --- /dev/null +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22c.java @@ -0,0 +1,75 @@ +/* + * [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.Format; + +import org.jf.dexlib.Code.Instruction; +import org.jf.dexlib.Code.InstructionWithReference; +import org.jf.dexlib.Code.Opcode; +import org.jf.dexlib.DexFile; +import org.jf.dexlib.Item; +import org.jf.dexlib.Util.NumberUtils; + +public class Instruction22c extends InstructionWithReference { + public static final Instruction.InstructionFactory Factory = new Factory(); + + public Instruction22c(Opcode opcode, byte regA, byte regB, Item referencedItem) { + super(opcode, referencedItem); + + if (regA >= 1 << 4 || + regB >= 1 << 4) { + throw new RuntimeException("The register number must be less than v16"); + } + + buffer[0] = opcode.value; + buffer[1] = (byte) ((regB << 4) | regA); + //the item index will be set later, during placement/writing + } + + private Instruction22c(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + super(dexFile, opcode, buffer, bufferIndex); + } + + public Format getFormat() { + return Format.Format22c; + } + + public byte getRegisterA() { + return NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 1]); + } + + public byte getRegisterB() { + return NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 1]); + } + + private static class Factory implements Instruction.InstructionFactory { + public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + return new Instruction22c(dexFile, opcode, buffer, bufferIndex); + } + } +} diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22s.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22s.java new file mode 100644 index 00000000..9630a500 --- /dev/null +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22s.java @@ -0,0 +1,78 @@ +/* + * [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.Format; + +import org.jf.dexlib.Code.Instruction; +import org.jf.dexlib.Code.Opcode; +import org.jf.dexlib.DexFile; +import org.jf.dexlib.Util.NumberUtils; + +public class Instruction22s extends Instruction { + public static final Instruction.InstructionFactory Factory = new Factory(); + + public Instruction22s(Opcode opcode, byte regA, byte regB, short litC) { + super(opcode); + + if (regA >= 1 << 4 || + regB >= 1 << 4) { + throw new RuntimeException("The register number must be less than v16"); + } + + buffer[0] = opcode.value; + buffer[1] = (byte) ((regB << 4) | regA); + buffer[2] = (byte) litC; + buffer[3] = (byte) (litC >> 8); + } + + private Instruction22s(Opcode opcode, byte[] buffer, int bufferIndex) { + super(opcode, buffer, bufferIndex); + } + + public Format getFormat() { + return Format.Format22s; + } + + public byte getRegisterA() { + return NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 1]); + } + + public byte getRegisterB() { + return NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 1]); + } + + public short getLiteral() { + return NumberUtils.decodeShort(buffer, bufferIndex + 2); + } + + private static class Factory implements Instruction.InstructionFactory { + public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + return new Instruction22s(opcode, buffer, bufferIndex); + } + } +} diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22t.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22t.java new file mode 100644 index 00000000..4b4c583e --- /dev/null +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22t.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.Code.Format; + +import org.jf.dexlib.Code.Instruction; +import org.jf.dexlib.Code.Opcode; +import org.jf.dexlib.DexFile; +import org.jf.dexlib.Util.NumberUtils; + +public class Instruction22t extends Instruction { + public static final Instruction.InstructionFactory Factory = new Factory(); + + public Instruction22t(Opcode opcode, byte regA, byte regB, short offC) { + super(opcode); + + if (regA >= 1 << 4 || + regB >= 1 << 4) { + throw new RuntimeException("The register number must be less than v16"); + } + + if (offC == 0) { + throw new RuntimeException("The offset cannot be 0."); + } + + buffer[0] = opcode.value; + buffer[1] = (byte) ((regB << 4) | regA); + buffer[2] = (byte) offC; + buffer[3] = (byte) (offC >> 8); + } + + private Instruction22t(Opcode opcode, byte[] buffer, int bufferIndex) { + super(opcode, buffer, bufferIndex); + + if (getOffset() == 0) { + throw new RuntimeException("The offset cannot be 0."); + } + } + + public Format getFormat() { + return Format.Format22t; + } + + public byte getRegisterA() { + return NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 1]); + } + + public byte getRegisterB() { + return NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 1]); + } + + public short getOffset() { + return NumberUtils.decodeShort(buffer, bufferIndex + 2); + } + + private static class Factory implements Instruction.InstructionFactory { + public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + return new Instruction22t(opcode, buffer, bufferIndex); + } + } +} diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22x.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22x.java new file mode 100644 index 00000000..48a2543a --- /dev/null +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22x.java @@ -0,0 +1,77 @@ +/* + * [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.Format; + +import org.jf.dexlib.Code.Instruction; +import org.jf.dexlib.Code.Opcode; +import org.jf.dexlib.DexFile; +import org.jf.dexlib.Util.NumberUtils; + +public class Instruction22x extends Instruction { + public static final Instruction.InstructionFactory Factory = new Factory(); + + public Instruction22x(Opcode opcode, short regA, int regB) { + super(opcode); + + if (regA >= 1 << 8) { + throw new RuntimeException("The register number must be less than v16"); + } + + if (regB >= 1 << 16) { + throw new RuntimeException("The register number must be less than v65536"); + } + + buffer[0] = opcode.value; + buffer[1] = (byte) regA; + buffer[2] = (byte) regB; + buffer[3] = (byte) (regB >> 8); + } + + private Instruction22x(Opcode opcode, byte[] buffer, int bufferIndex) { + super(opcode, buffer, bufferIndex); + } + + public Format getFormat() { + return Format.Format22x; + } + + public short getRegisterA() { + return NumberUtils.decodeUnsignedByte(buffer[bufferIndex + 1]); + } + + public int getRegisterB() { + return NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 2); + } + + private static class Factory implements Instruction.InstructionFactory { + public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + return new Instruction22x(opcode, buffer, bufferIndex); + } + } +} diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction23x.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction23x.java new file mode 100644 index 00000000..234f9578 --- /dev/null +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction23x.java @@ -0,0 +1,79 @@ +/* + * [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.Format; + +import org.jf.dexlib.Code.Instruction; +import org.jf.dexlib.Code.Opcode; +import org.jf.dexlib.DexFile; +import org.jf.dexlib.Util.NumberUtils; + +public class Instruction23x extends Instruction { + public static final Instruction.InstructionFactory Factory = new Factory(); + + public Instruction23x(Opcode opcode, short regA, short regB, short regC) { + super(opcode); + + if (regA >= 1 << 8 || + regB >= 1 << 8 || + regC >= 1 << 8) { + throw new RuntimeException("The register number must be less than v256"); + } + + buffer[0] = opcode.value; + buffer[1] = (byte) regA; + buffer[2] = (byte) regB; + buffer[3] = (byte) regC; + } + + private Instruction23x(Opcode opcode, byte[] buffer, int bufferIndex) { + super(opcode, buffer, bufferIndex); + } + + public Format getFormat() { + return Format.Format23x; + } + + public short getRegisterA() { + return NumberUtils.decodeUnsignedByte(buffer[bufferIndex + 1]); + } + + public short getRegisterB() { + return NumberUtils.decodeUnsignedByte(buffer[bufferIndex + 2]); + } + + public short getRegisterC() { + return NumberUtils.decodeUnsignedByte(buffer[bufferIndex + 3]); + } + + private static class Factory implements Instruction.InstructionFactory { + public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + return new Instruction23x(opcode, buffer, bufferIndex); + } + } +} diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction30t.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction30t.java new file mode 100644 index 00000000..b555d097 --- /dev/null +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction30t.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.Code.Format; + +import org.jf.dexlib.DexFile; +import org.jf.dexlib.Code.Instruction; +import org.jf.dexlib.Code.Opcode; +import org.jf.dexlib.Util.NumberUtils; + +public class Instruction30t extends Instruction +{ + public static final Instruction.InstructionFactory Factory = new Factory(); + + public Instruction30t(Opcode opcode, int offA) { + super(opcode); + + buffer[0] = opcode.value; + buffer[2] = (byte)offA; + buffer[3] = (byte)(offA >> 8); + buffer[4] = (byte)(offA >> 16); + buffer[5] = (byte)(offA >> 24); + } + + private Instruction30t(Opcode opcode, byte[] buffer, int bufferIndex) { + super(opcode, buffer, bufferIndex); + } + + public Format getFormat() { + return Format.Format30t; + } + + public int getOffset() { + return NumberUtils.decodeInt(buffer, bufferIndex + 2); + } + + private static class Factory implements Instruction.InstructionFactory { + public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + return new Instruction30t(opcode, buffer, bufferIndex); + } + } +} diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction31c.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction31c.java new file mode 100644 index 00000000..9e1c747a --- /dev/null +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction31c.java @@ -0,0 +1,70 @@ +/* + * [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.Format; + +import org.jf.dexlib.Code.Instruction; +import org.jf.dexlib.Code.InstructionWithReference; +import org.jf.dexlib.Code.Opcode; +import org.jf.dexlib.DexFile; +import org.jf.dexlib.Item; +import org.jf.dexlib.Util.NumberUtils; + +public class Instruction31c extends InstructionWithReference { + public static final Instruction.InstructionFactory Factory = new Factory(); + + public Instruction31c(Opcode opcode, short regA, Item referencedItem) { + super(opcode, referencedItem); + + if (regA >= 1 << 8) { + throw new RuntimeException("The register number must be less than v256"); + } + + buffer[0] = opcode.value; + buffer[1] = (byte) regA; + //the item index will be set later, during placement/writing + } + + private Instruction31c(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + super(dexFile, opcode, buffer, bufferIndex); + } + + public Format getFormat() { + return Format.Format31c; + } + + public short getRegister() { + return NumberUtils.decodeUnsignedByte(buffer[bufferIndex + 1]); + } + + private static class Factory implements Instruction.InstructionFactory { + public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + return new Instruction31c(dexFile, opcode, buffer, bufferIndex); + } + } +} diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction31i.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction31i.java new file mode 100644 index 00000000..a5473b17 --- /dev/null +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction31i.java @@ -0,0 +1,75 @@ +/* + * [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.Format; + +import org.jf.dexlib.Code.Instruction; +import org.jf.dexlib.Code.Opcode; +import org.jf.dexlib.DexFile; +import org.jf.dexlib.Util.NumberUtils; + +public class Instruction31i extends Instruction { + public static final Instruction.InstructionFactory Factory = new Factory(); + + public Instruction31i(Opcode opcode, short regA, int litB) { + super(opcode); + + if (regA >= 1 << 8) { + throw new RuntimeException("The register number must be less than v256"); + } + + buffer[0] = opcode.value; + buffer[1] = (byte) regA; + buffer[2] = (byte) litB; + buffer[3] = (byte) (litB >> 8); + buffer[4] = (byte) (litB >> 16); + buffer[5] = (byte) (litB >> 24); + } + + private Instruction31i(Opcode opcode, byte[] buffer, int bufferIndex) { + super(opcode, buffer, bufferIndex); + } + + public Format getFormat() { + return Format.Format31i; + } + + public short getRegister() { + return NumberUtils.decodeUnsignedByte(buffer[bufferIndex + 1]); + } + + public int getLiteral() { + return NumberUtils.decodeInt(buffer, bufferIndex + 2); + } + + private static class Factory implements Instruction.InstructionFactory { + public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + return new Instruction31i(opcode, buffer, bufferIndex); + } + } +} diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction31t.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction31t.java new file mode 100644 index 00000000..182a77f3 --- /dev/null +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction31t.java @@ -0,0 +1,75 @@ +/* + * [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.Format; + +import org.jf.dexlib.Code.Instruction; +import org.jf.dexlib.Code.Opcode; +import org.jf.dexlib.DexFile; +import org.jf.dexlib.Util.NumberUtils; + +public class Instruction31t extends Instruction { + public static final Instruction.InstructionFactory Factory = new Factory(); + + public Instruction31t(Opcode opcode, short regA, int offB) { + super(opcode); + + if (regA >= 1 << 8) { + throw new RuntimeException("The register number must be less than v256"); + } + + buffer[0] = opcode.value; + buffer[1] = (byte) regA; + buffer[2] = (byte) offB; + buffer[3] = (byte) (offB >> 8); + buffer[4] = (byte) (offB >> 16); + buffer[5] = (byte) (offB >> 24); + } + + private Instruction31t(Opcode opcode, byte[] buffer, int bufferIndex) { + super(opcode, buffer, bufferIndex); + } + + public Format getFormat() { + return Format.Format31t; + } + + public short getRegister() { + return NumberUtils.decodeUnsignedByte(buffer[bufferIndex + 1]); + } + + public int getOffset() { + return NumberUtils.decodeInt(buffer, bufferIndex + 2); + } + + private static class Factory implements Instruction.InstructionFactory { + public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + return new Instruction31t(opcode, buffer, bufferIndex); + } + } +} diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction32x.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction32x.java new file mode 100644 index 00000000..30833ee8 --- /dev/null +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction32x.java @@ -0,0 +1,76 @@ +/* + * [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.Format; + +import org.jf.dexlib.DexFile; +import org.jf.dexlib.Code.Instruction; +import org.jf.dexlib.Code.Opcode; +import org.jf.dexlib.Util.NumberUtils; + +public class Instruction32x extends Instruction +{ + public static final Instruction.InstructionFactory Factory = new Factory(); + + public Instruction32x(Opcode opcode, int regA, int regB) { + super(opcode); + + if (regA >= 1<<16 || + regB >= 1<<16) { + throw new RuntimeException("The register number must be less than v65536"); + } + + buffer[0] = opcode.value; + buffer[2] = (byte)regA; + buffer[3] = (byte)(regA >> 8); + buffer[4] = (byte)regB; + buffer[5] = (byte)(regB >> 8); + } + + private Instruction32x(Opcode opcode, byte[] buffer, int bufferIndex) { + super(opcode, buffer, bufferIndex); + } + + public Format getFormat() { + return Format.Format32x; + } + + public int getRegisterA() { + return NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 2); + } + + public int getRegisterB() { + return NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 4); + } + + private static class Factory implements Instruction.InstructionFactory { + public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + return new Instruction32x(opcode, buffer, bufferIndex); + } + } +} diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35c.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35c.java new file mode 100644 index 00000000..234a713d --- /dev/null +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35c.java @@ -0,0 +1,137 @@ +/* + * [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.Format; + +import org.jf.dexlib.Code.Instruction; +import org.jf.dexlib.Code.InstructionWithReference; +import org.jf.dexlib.Code.Opcode; +import static org.jf.dexlib.Code.Opcode.*; +import org.jf.dexlib.DexFile; +import org.jf.dexlib.Item; +import org.jf.dexlib.MethodIdItem; +import org.jf.dexlib.TypeIdItem; +import org.jf.dexlib.Util.NumberUtils; + +public class Instruction35c extends InstructionWithReference { + public static final Instruction.InstructionFactory Factory = new Factory(); + + public Instruction35c(Opcode opcode, int regCount, byte regD, byte regE, byte regF, byte regG, + byte regA, Item referencedItem) { + super(opcode, referencedItem); + + if (regCount > 5) { + throw new RuntimeException("regCount cannot be greater than 5"); + } + + if (regD >= 1 << 4 || + regE >= 1 << 4 || + regF >= 1 << 4 || + regG >= 1 << 4 || + regA >= 1 << 4) { + throw new RuntimeException("All register args must fit in 4 bits"); + } + + buffer[0] = opcode.value; + buffer[1] = (byte) ((regCount << 4) | regA); + //the item index will be set later, during placement/writing + buffer[4] = (byte) ((regE << 4) | regD); + buffer[5] = (byte) ((regG << 4) | regF); + + checkItem(); + } + + private Instruction35c(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + super(dexFile, opcode, buffer, bufferIndex); + + if (getRegCount() > 5) { + throw new RuntimeException("regCount cannot be greater than 5"); + } + + checkItem(); + } + + public Format getFormat() { + return Format.Format35c; + } + + public byte getRegisterA() { + return NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 1]); + } + + public byte getRegCount() { + return NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 1]); + } + + public byte getRegisterD() { + return NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 4]); + } + + public byte getRegisterE() { + return NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 4]); + } + + public byte getRegisterF() { + return NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 5]); + } + + public byte getRegisterG() { + return NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 5]); + } + + private void checkItem() { + Item item = getReferencedItem(); + + if (opcode == FILLED_NEW_ARRAY) { + //check data for filled-new-array opcode + String type = ((TypeIdItem) item).getTypeDescriptor(); + if (type.charAt(0) != '[') { + throw new RuntimeException("The type must be an array type"); + } + if (type.charAt(1) == 'J' || type.charAt(1) == 'D') { + throw new RuntimeException("The type cannot be an array of longs or doubles"); + } + } else if (opcode.value >= INVOKE_VIRTUAL.value && opcode.value <= INVOKE_INTERFACE.value) { + //check data for invoke-* opcodes + MethodIdItem methodIdItem = (MethodIdItem) item; + int parameterRegisterCount = methodIdItem.getPrototype().getParameterRegisterCount(); + if (opcode != INVOKE_STATIC) { + parameterRegisterCount++; + } + if (parameterRegisterCount != getRegCount()) { + throw new RuntimeException("regCount does not match the number of arguments of the method"); + } + } + } + + private static class Factory implements Instruction.InstructionFactory { + public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + return new Instruction35c(dexFile, opcode, buffer, bufferIndex); + } + } +} diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction3rc.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction3rc.java new file mode 100644 index 00000000..18ed8c05 --- /dev/null +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction3rc.java @@ -0,0 +1,118 @@ +/* + * [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.Format; + +import org.jf.dexlib.Code.Instruction; +import org.jf.dexlib.Code.InstructionWithReference; +import org.jf.dexlib.Code.Opcode; +import static org.jf.dexlib.Code.Opcode.*; +import org.jf.dexlib.DexFile; +import org.jf.dexlib.Item; +import org.jf.dexlib.MethodIdItem; +import org.jf.dexlib.TypeIdItem; +import org.jf.dexlib.Util.NumberUtils; + +public class Instruction3rc extends InstructionWithReference { + public static final Instruction.InstructionFactory Factory = new Factory(); + + public Instruction3rc(Opcode opcode, short regCount, int startReg, Item referencedItem) { + super(opcode, referencedItem); + + if (regCount >= 1 << 8) { + throw new RuntimeException("regCount must be less than 256"); + } + if (regCount < 0) { + throw new RuntimeException("regCount cannot be negative"); + } + + if (startReg >= 1 << 16) { + throw new RuntimeException("The beginning register of the range must be less than 65536"); + } + if (startReg < 0) { + throw new RuntimeException("The beginning register of the range cannot be negative"); + } + + buffer[0] = opcode.value; + buffer[1] = (byte) regCount; + //the item index will be set later, during placement/writing + buffer[4] = (byte) startReg; + buffer[5] = (byte) (startReg >> 8); + + checkItem(); + } + + private Instruction3rc(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + super(dexFile, opcode, buffer, bufferIndex); + + checkItem(); + } + + public Format getFormat() { + return Format.Format3rc; + } + + public short getRegCount() { + return NumberUtils.decodeUnsignedByte(buffer[bufferIndex + 1]); + } + + public int getStartRegister() { + return NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 4); + } + + private void checkItem() { + Item item = getReferencedItem(); + + if (opcode == FILLED_NEW_ARRAY_RANGE) { + //check data for filled-new-array/range opcode + String type = ((TypeIdItem) item).getTypeDescriptor(); + if (type.charAt(0) != '[') { + throw new RuntimeException("The type must be an array type"); + } + if (type.charAt(1) == 'J' || type.charAt(1) == 'D') { + throw new RuntimeException("The type cannot be an array of longs or doubles"); + } + } else if (opcode.value >= INVOKE_VIRTUAL_RANGE.value && opcode.value <= INVOKE_INTERFACE_RANGE.value) { + //check data for invoke-*/range opcodes + MethodIdItem methodIdItem = (MethodIdItem) item; + int parameterRegisterCount = methodIdItem.getPrototype().getParameterRegisterCount(); + if (opcode != INVOKE_STATIC_RANGE) { + parameterRegisterCount++; + } + if (parameterRegisterCount != getRegCount()) { + throw new RuntimeException("regCount does not match the number of arguments of the method"); + } + } + } + + private static class Factory implements Instruction.InstructionFactory { + public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + return new Instruction3rc(dexFile, opcode, buffer, bufferIndex); + } + } +} diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction51l.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction51l.java new file mode 100644 index 00000000..ebd6455a --- /dev/null +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction51l.java @@ -0,0 +1,79 @@ +/* + * [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.Format; + +import org.jf.dexlib.Code.Instruction; +import org.jf.dexlib.Code.Opcode; +import org.jf.dexlib.DexFile; +import org.jf.dexlib.Util.NumberUtils; + +public class Instruction51l extends Instruction { + public static final Instruction.InstructionFactory Factory = new Factory(); + + public Instruction51l(Opcode opcode, short regA, long litB) { + super(opcode); + + if (regA >= 1 << 8) { + throw new RuntimeException("The register number must be less than v256"); + } + + buffer[0] = opcode.value; + buffer[1] = (byte) regA; + buffer[2] = (byte) litB; + buffer[3] = (byte) (litB >> 8); + buffer[4] = (byte) (litB >> 16); + buffer[5] = (byte) (litB >> 24); + buffer[6] = (byte) (litB >> 32); + buffer[7] = (byte) (litB >> 40); + buffer[8] = (byte) (litB >> 48); + buffer[9] = (byte) (litB >> 56); + } + + private Instruction51l(Opcode opcode, byte[] buffer, int bufferIndex) { + super(opcode, buffer, bufferIndex); + } + + public Format getFormat() { + return Format.Format51l; + } + + public short getRegister() { + return NumberUtils.decodeUnsignedByte(buffer[bufferIndex + 1]); + } + + public long getLiteral() { + return NumberUtils.decodeLong(buffer, bufferIndex + 2); + } + + private static class Factory implements Instruction.InstructionFactory { + public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + return new Instruction51l(opcode, buffer, bufferIndex); + } + } +} diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/PackedSwitchDataPseudoInstruction.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/PackedSwitchDataPseudoInstruction.java new file mode 100644 index 00000000..e5ce46b7 --- /dev/null +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/PackedSwitchDataPseudoInstruction.java @@ -0,0 +1,145 @@ +/* + * [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.Format; + +import org.jf.dexlib.Code.Instruction; +import org.jf.dexlib.Code.Opcode; +import org.jf.dexlib.Util.NumberUtils; +import org.jf.dexlib.DexFile; + +import java.util.Iterator; + +public class PackedSwitchDataPseudoInstruction extends Instruction { + public static final Instruction.InstructionFactory Factory = new Factory(); + + @Override + public int getSize() { + return getTargetCount() * 4 + 8; + } + + public PackedSwitchDataPseudoInstruction(int firstKey, int[] targets) { + super(Opcode.NOP, targets.length * 4 + 8); + + /*this.firstKey = firstKey; + this.targets = targets;*/ + + if (targets.length > 0xFFFF) { + throw new RuntimeException("The packed-switch data contains too many elements. " + + "The maximum number of switch elements is 65535"); + } + + buffer[0] = 0x00; + buffer[1] = 0x01; //packed-switch pseudo-opcode + + buffer[2] = (byte) targets.length; + buffer[3] = (byte) (targets.length >> 8); + + buffer[4] = (byte) firstKey; + buffer[5] = (byte) (firstKey >> 8); + buffer[6] = (byte) (firstKey >> 16); + buffer[7] = (byte) (firstKey >> 24); + + int position = 8; + for (int target : targets) { + buffer[position++] = (byte) target; + buffer[position++] = (byte) (target >> 8); + buffer[position++] = (byte) (target >> 16); + buffer[position++] = (byte) (target >> 24); + } + } + + public PackedSwitchDataPseudoInstruction(byte[] buffer, int bufferIndex) { + super(Opcode.NOP, buffer, bufferIndex); + + byte opcodeByte = buffer[bufferIndex++]; + if (opcodeByte != 0x00) { + throw new RuntimeException("Invalid opcode byte for a PackedSwitchData pseudo-instruction"); + } + byte subopcodeByte = buffer[bufferIndex]; + if (subopcodeByte != 0x01) { + throw new RuntimeException("Invalid sub-opcode byte for a PackedSwitchData pseudo-instruction"); + } + } + + public Format getFormat() { + return Format.PackedSwitchData; + } + + public int getTargetCount() { + return NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 2); + } + + public int getFirstKey() { + return NumberUtils.decodeInt(buffer, bufferIndex + 4); + } + + public static class PackedSwitchTarget { + public int value; + public int target; + } + + public Iterator getTargets() { + return new Iterator() { + final int targetCount = getTargetCount(); + int i = 0; + int position = bufferIndex + 8; + int value = getFirstKey(); + + PackedSwitchTarget packedSwitchTarget = new PackedSwitchTarget(); + + public boolean hasNext() { + return i 0xFFFF) { + throw new RuntimeException("The sparse-switch data contains too many elements. " + + "The maximum number of switch elements is 65535"); + } + + buffer[0] = 0x00; + buffer[1] = 0x02; //sparse-switch psuedo-opcode + + buffer[2] = (byte) targets.length; + buffer[3] = (byte) (targets.length >> 8); + + int position = 8; + + if (targets.length > 0) { + int key = keys[0]; + buffer[4] = (byte) key; + buffer[5] = (byte) (key >> 8); + buffer[6] = (byte) (key >> 16); + buffer[7] = (byte) (key >> 24); + + for (int i = 1; i < keys.length; i++) { + key = keys[i]; + if (key <= keys[i - 1]) { + throw new RuntimeException("The targets in a sparse switch block must be sorted in ascending" + + "order, by key"); + } + + buffer[position++] = (byte) key; + buffer[position++] = (byte) (key >> 8); + buffer[position++] = (byte) (key >> 16); + buffer[position++] = (byte) (key >> 24); + } + + for (int target : targets) { + buffer[position++] = (byte) target; + buffer[position++] = (byte) (target >> 8); + buffer[position++] = (byte) (target >> 16); + buffer[position++] = (byte) (target >> 24); + } + } + } + + public SparseSwitchDataPseudoInstruction(byte[] buffer, int bufferIndex) { + super(Opcode.NOP, buffer, bufferIndex); + + byte opcodeByte = buffer[bufferIndex++]; + if (opcodeByte != 0x00) { + throw new RuntimeException("Invalid opcode byte for a SparseSwitchData pseudo-instruction"); + } + byte subopcodeByte = buffer[bufferIndex]; + if (subopcodeByte != 0x02) { + throw new RuntimeException("Invalid sub-opcode byte for a SparseSwitchData pseudo-instruction"); + } + } + + public Format getFormat() { + return Format.SparseSwitchData; + } + + public int getTargetCount() { + return NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 2); + } + + public static class SparseSwitchTarget { + public int value; + public int target; + } + + public Iterator getTargets() { + return new Iterator() { + final int targetCount = getTargetCount(); + int i = 0; + int valuePosition = bufferIndex + 4; + int targetPosition = bufferIndex + 4 + targetCount * 4; + + SparseSwitchTarget sparseSwitchTarget = new SparseSwitchTarget(); + + public boolean hasNext() { + return iProcessInstructionDelegate object containing the methods + * that get called for each instruction that is encountered + */ + public static void IterateInstructions(byte[] insns, ProcessRawInstructionDelegate processRawInstruction) { + int insnsPosition = 0; + + while (insnsPosition < insns.length) { + Opcode opcode = Opcode.getOpcodeByValue(insns[insnsPosition]); + + if (opcode == null) { + throw new RuntimeException("Unknown opcode: " + Hex.u1(insns[insnsPosition])); + } + + if (opcode.referenceType == ReferenceType.none) { + byte secondByte = insns[insnsPosition+1]; + //if this is one of the "special" opcodes + if (opcode == Opcode.NOP && secondByte > 0) { + switch (secondByte) { + case 1: + { + //packed-switch + int size = NumberUtils.decodeUnsignedShort(insns[insnsPosition+2], insns[insnsPosition+3]); + int end = insnsPosition + size * 4 + 8; + processRawInstruction.ProcessPackedSwitchInstruction(insnsPosition, size, end-insnsPosition); + insnsPosition = end; + break; + } + case 2: + { + //sparse-switch + int size = NumberUtils.decodeUnsignedShort(insns[insnsPosition+2], insns[insnsPosition+3]); + int end = insnsPosition + size * 8 + 4; + processRawInstruction.ProcessSparseSwitchInstruction(insnsPosition, size, end-insnsPosition); + insnsPosition = end; + break; + } + case 3: + { + //fill-array-data + int elementWidth = NumberUtils.decodeUnsignedShort(insns[insnsPosition+2], + insns[insnsPosition+3]); + int size = NumberUtils.decodeInt(insns[insnsPosition+4], insns[insnsPosition+5], + insns[insnsPosition+6], insns[insnsPosition+7]); + int end = insnsPosition + (size * elementWidth) + 8; + if (end % 2 == 1) { + end++; + } + processRawInstruction.ProcessFillArrayDataInstruction(insnsPosition, elementWidth, size, + end-insnsPosition); + insnsPosition = end; + break; + } + } + } else { + processRawInstruction.ProcessNormalInstruction(opcode, insnsPosition); + insnsPosition += opcode.format.size; + } + } else { + processRawInstruction.ProcessReferenceInstruction(opcode, insnsPosition); + insnsPosition += opcode.format.size; + } + } + } + + public static void IterateInstructions(DexFile dexFile, byte[] insns, ProcessInstructionDelegate delegate) { + int insnsPosition = 0; + + while (insnsPosition < insns.length) { + Opcode opcode = Opcode.getOpcodeByValue(insns[insnsPosition]); + + Instruction instruction = null; + + if (opcode == null) { + throw new RuntimeException("Unknown opcode: " + Hex.u1(insns[insnsPosition])); + } + + if (opcode == Opcode.NOP) { + byte secondByte = insns[insnsPosition+1]; + switch (secondByte) { + case 0: + { + instruction = new Instruction10x(Opcode.NOP, insns, insnsPosition); + break; + } + case 1: + { + insnsPosition += insnsPosition & 0x01; + instruction = new PackedSwitchDataPseudoInstruction(insns, insnsPosition); + break; + } + case 2: + { + insnsPosition += insnsPosition & 0x01; + instruction = new SparseSwitchDataPseudoInstruction(insns, insnsPosition); + break; + } + case 3: + { + insnsPosition += insnsPosition & 0x01; + instruction = new ArrayDataPseudoInstruction(insns, insnsPosition); + break; + } + } + } else { + instruction = opcode.format.Factory.makeInstruction(dexFile, opcode, insns, insnsPosition); + } + + delegate.ProcessInstruction(insnsPosition, instruction); + insnsPosition += instruction.getSize(); + } + } + + public static interface ProcessRawInstructionDelegate { + /** + * The InstructionIterator 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 LinkedList referencedItems = new LinkedList(); + + InstructionIterator.IterateInstructions(insns, new InstructionIterator.ProcessRawInstructionDelegate() { + public void ProcessNormalInstruction(Opcode opcode, int index) { + } + + public void ProcessReferenceInstruction(Opcode opcode, int index) { + if (opcode == Opcode.CONST_STRING_JUMBO) { + int itemIndex = NumberUtils.decodeInt(insns, index+2); + if (itemIndex < 0) { + throw new RuntimeException("The string index for this const-string/jumbo instruction is too large"); + } + referencedItems.add(dexFile.StringIdsSection.getItemByIndex(itemIndex)); + } else { + int itemIndex = NumberUtils.decodeUnsignedShort(insns, index+2); + if (itemIndex > 0xFFFF) { + throw new RuntimeException("The item index does not fit in 2 bytes"); + } + + switch (opcode.referenceType) { + case string: + referencedItems.add(dexFile.StringIdsSection.getItemByIndex(itemIndex)); + break; + case type: + referencedItems.add(dexFile.TypeIdsSection.getItemByIndex(itemIndex)); + break; + case field: + referencedItems.add(dexFile.FieldIdsSection.getItemByIndex(itemIndex)); + break; + case method: + referencedItems.add(dexFile.MethodIdsSection.getItemByIndex(itemIndex)); + break; + } + } + } + + public void ProcessPackedSwitchInstruction(int index, int targetCount, int instructionLength) { + } + + public void ProcessSparseSwitchInstruction(int index, int targetCount, int instructionLength) { + } + + public void ProcessFillArrayDataInstruction(int index, int elementWidth, int elementCount, int instructionLength) { + } + }); + + Item[] items = new Item[referencedItems.size()]; + return referencedItems.toArray(items); + } +} diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/InstructionWithReference.java b/dexlib/src/main/java/org/jf/dexlib/Code/InstructionWithReference.java new file mode 100644 index 00000000..0f62c8d7 --- /dev/null +++ b/dexlib/src/main/java/org/jf/dexlib/Code/InstructionWithReference.java @@ -0,0 +1,105 @@ +/* + * [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.*; +import org.jf.dexlib.Util.NumberUtils; + +public abstract class InstructionWithReference extends Instruction { + private Item referencedItem; + + protected InstructionWithReference(Opcode opcode, Item referencedItem) { + super(opcode); + this.referencedItem = referencedItem; + checkReferenceType(); + } + + protected InstructionWithReference(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + super(opcode, buffer, bufferIndex); + + int itemIndex = NumberUtils.decodeUnsignedShort(buffer[bufferIndex + 2], + buffer[bufferIndex + 3]); + lookupReferencedItem(dexFile, opcode, itemIndex); + } + + public Item getReferencedItem() { + return referencedItem; + } + + private void lookupReferencedItem(DexFile dexFile, Opcode opcode, int itemIndex) { + switch (opcode.referenceType) { + case field: + referencedItem = dexFile.FieldIdsSection.getItemByIndex(itemIndex); + return; + case method: + referencedItem = dexFile.MethodIdsSection.getItemByIndex(itemIndex); + return; + case type: + referencedItem = dexFile.TypeIdsSection.getItemByIndex(itemIndex); + return; + case string: + referencedItem = dexFile.StringIdsSection.getItemByIndex(itemIndex); + } + } + + + private void checkReferenceType() { + switch (opcode.referenceType) { + case field: + if (!(referencedItem instanceof FieldIdItem)) { + throw new RuntimeException(referencedItem.getClass().getSimpleName() + + " is the wrong item type for opcode " + opcode.name + ". Expecting FieldIdItem."); + } + return; + case method: + if (!(referencedItem instanceof MethodIdItem)) { + throw new RuntimeException(referencedItem.getClass().getSimpleName() + + " is the wrong item type for opcode " + opcode.name + ". Expecting MethodIdItem."); + } + return; + case type: + if (!(referencedItem instanceof TypeIdItem)) { + throw new RuntimeException(referencedItem.getClass().getSimpleName() + + " is the wrong item type for opcode " + opcode.name + ". Expecting TypeIdItem."); + } + return; + case string: + if (!(referencedItem instanceof StringIdItem)) { + throw new RuntimeException(referencedItem.getClass().getSimpleName() + + " is the wrong item type for opcode " + opcode.name + ". Expecting StringIdItem."); + } + return; + default: + if (referencedItem != null) { + throw new RuntimeException(referencedItem.getClass().getSimpleName() + + " is invalid for opcode " + opcode.name + ". This opcode does not reference an item"); + } + } + } +} diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/InstructionWriter.java b/dexlib/src/main/java/org/jf/dexlib/Code/InstructionWriter.java new file mode 100644 index 00000000..d715321b --- /dev/null +++ b/dexlib/src/main/java/org/jf/dexlib/Code/InstructionWriter.java @@ -0,0 +1,79 @@ +/* + * [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.Util.AnnotatedOutput; + +public class InstructionWriter { + public static void writeInstructions(final byte[] insns, final Item[] referencedItems, final AnnotatedOutput out) { + + InstructionIterator.IterateInstructions(insns, new InstructionIterator.ProcessRawInstructionDelegate() { + int itemsPosition = 0; + + public void ProcessNormalInstruction(Opcode opcode, int index) { + out.write(insns, index, opcode.format.size); + } + + public void ProcessReferenceInstruction(Opcode opcode, int index) { + out.write(insns, index, 2); + + if (itemsPosition >= referencedItems.length) { + throw new RuntimeException("There are not enough referenced items"); + } + int itemIndex = referencedItems[itemsPosition++].getIndex(); + if (opcode == Opcode.CONST_STRING_JUMBO) { + out.writeInt(itemIndex); + } else { + if (itemIndex > 0xFFFF) { + throw new RuntimeException("The item index does not fit in 2 bytes"); + } + out.writeShort(itemIndex); + + if (opcode.format.size == 6) { + out.write(insns, index + 4, 2); + } + } + } + + public void ProcessPackedSwitchInstruction(int index, int targetCount, int instructionLength) { + out.write(insns, index, instructionLength); + } + + public void ProcessSparseSwitchInstruction(int index, int targetCount, int instructionLength) { + out.write(insns, index, instructionLength); + } + + public void ProcessFillArrayDataInstruction(int index, int elementWidth, int elementCount, int instructionLength) { + out.write(insns, index, instructionLength); + } + }); + + } +} diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Opcode.java b/dexlib/src/main/java/org/jf/dexlib/Code/Opcode.java new file mode 100644 index 00000000..2964c68e --- /dev/null +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Opcode.java @@ -0,0 +1,290 @@ +/* + * [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.Code.Format.Format; + +import java.util.HashMap; + +public enum Opcode +{ + NOP((byte)0x00, "nop", ReferenceType.none, Format.Format10x), + MOVE((byte)0x01, "move", ReferenceType.none, Format.Format12x), + MOVE_FROM16((byte)0x02, "move/from16", ReferenceType.none, Format.Format22x), + MOVE_16((byte)0x03, "move/16", ReferenceType.none, Format.Format32x), + MOVE_WIDE((byte)0x04, "move-wide", ReferenceType.none, Format.Format12x), + MOVE_WIDE_FROM16((byte)0x05, "move-wide/from16", ReferenceType.none, Format.Format22x), + MOVE_WIDE_16((byte)0x06, "move-wide/16", ReferenceType.none, Format.Format32x), + MOVE_OBJECT((byte)0x07, "move-object", ReferenceType.none, Format.Format12x), + MOVE_OBJECT_FROM16((byte)0x08, "move-object/from16", ReferenceType.none, Format.Format22x), + MOVE_OBJECT_16((byte)0x09, "move-object/16", ReferenceType.none, Format.Format32x), + MOVE_RESULT((byte)0x0a, "move-result", ReferenceType.none, Format.Format11x), + MOVE_RESULT_WIDE((byte)0x0b, "move-result-wide", ReferenceType.none, Format.Format11x), + MOVE_RESULT_OBJECT((byte)0x0c, "move-result-object", ReferenceType.none, Format.Format11x), + MOVE_EXCEPTION((byte)0x0d, "move-exception", ReferenceType.none, Format.Format11x), + RETURN_VOID((byte)0x0e, "return-void", ReferenceType.none, Format.Format10x), + RETURN((byte)0x0f, "return", ReferenceType.none, Format.Format11x), + RETURN_WIDE((byte)0x10, "return-wide", ReferenceType.none, Format.Format11x), + RETURN_OBJECT((byte)0x11, "return-object", ReferenceType.none, Format.Format11x), + CONST_4((byte)0x12, "const/4", ReferenceType.none, Format.Format11n), + CONST_16((byte)0x13, "const/16", ReferenceType.none, Format.Format21s), + CONST((byte)0x14, "const", ReferenceType.none, Format.Format31i), + CONST_HIGH16((byte)0x15, "const/high16", ReferenceType.none, Format.Format21h), + CONST_WIDE_16((byte)0x16, "const-wide/16", ReferenceType.none, Format.Format21s), + CONST_WIDE_32((byte)0x17, "const-wide/32", ReferenceType.none, Format.Format31i), + CONST_WIDE((byte)0x18, "const-wide", ReferenceType.none, Format.Format51l), + CONST_WIDE_HIGH16((byte)0x19, "const-wide/high16", ReferenceType.none, Format.Format21h), + CONST_STRING((byte)0x1a, "const-string", ReferenceType.string, Format.Format21c), + CONST_STRING_JUMBO((byte)0x1b, "const-string/jumbo", ReferenceType.string, Format.Format31c), + CONST_CLASS((byte)0x1c, "const-class", ReferenceType.type, Format.Format21c), + MONITOR_ENTER((byte)0x1d, "monitor-enter", ReferenceType.none, Format.Format11x), + MONITOR_EXIT((byte)0x1e, "monitor-exit", ReferenceType.none, Format.Format11x), + CHECK_CAST((byte)0x1f, "check-cast", ReferenceType.type, Format.Format21c), + INSTANCE_OF((byte)0x20, "instance-of", ReferenceType.type, Format.Format22c), + ARRAY_LENGTH((byte)0x21, "array-length", ReferenceType.none, Format.Format12x), + NEW_INSTANCE((byte)0x22, "new-instance", ReferenceType.type, Format.Format21c), + NEW_ARRAY((byte)0x23, "new-array", ReferenceType.type, Format.Format22c), + FILLED_NEW_ARRAY((byte)0x24, "filled-new-array", ReferenceType.type, Format.Format35c), + FILLED_NEW_ARRAY_RANGE((byte)0x25, "filled-new-array/range", ReferenceType.type, Format.Format3rc), + FILL_ARRAY_DATA((byte)0x26, "fill-array-data", ReferenceType.none, Format.Format31t), + THROW((byte)0x27, "throw", ReferenceType.none, Format.Format11x), + GOTO((byte)0x28, "goto", ReferenceType.none, Format.Format10t), + GOTO_16((byte)0x29, "goto/16", ReferenceType.none, Format.Format20t), + GOTO_32((byte)0x2a, "goto/32", ReferenceType.none, Format.Format30t), + PACKED_SWITCH((byte)0x2b, "packed-switch", ReferenceType.none, Format.Format31t), + SPARSE_SWITCH((byte)0x2c, "sparse-switch", ReferenceType.none, Format.Format31t), + CMPL_FLOAT((byte)0x2d, "cmpl-float", ReferenceType.none, Format.Format23x), + CMPG_FLOAT((byte)0x2e, "cmpg-float", ReferenceType.none, Format.Format23x), + CMPL_DOUBLE((byte)0x2f, "cmpl-double", ReferenceType.none, Format.Format23x), + CMPG_DOUBLE((byte)0x30, "cmpg-double", ReferenceType.none, Format.Format23x), + CMP_LONG((byte)0x31, "cmp-long", ReferenceType.none, Format.Format23x), + IF_EQ((byte)0x32, "if-eq", ReferenceType.none, Format.Format22t), + IF_NE((byte)0x33, "if-ne", ReferenceType.none, Format.Format22t), + IF_LT((byte)0x34, "if-lt", ReferenceType.none, Format.Format22t), + IF_GE((byte)0x35, "if-ge", ReferenceType.none, Format.Format22t), + IF_GT((byte)0x36, "if-gt", ReferenceType.none, Format.Format22t), + IF_LE((byte)0x37, "if-le", ReferenceType.none, Format.Format22t), + IF_EQZ((byte)0x38, "if-eqz", ReferenceType.none, Format.Format21t), + IF_NEZ((byte)0x39, "if-nez", ReferenceType.none, Format.Format21t), + IF_LTZ((byte)0x3a, "if-ltz", ReferenceType.none, Format.Format21t), + IF_GEZ((byte)0x3b, "if-gez", ReferenceType.none, Format.Format21t), + IF_GTZ((byte)0x3c, "if-gtz", ReferenceType.none, Format.Format21t), + IF_LEZ((byte)0x3d, "if-lez", ReferenceType.none, Format.Format21t), + AGET((byte)0x44, "aget", ReferenceType.none, Format.Format23x), + AGET_WIDE((byte)0x45, "aget-wide", ReferenceType.none, Format.Format23x), + AGET_OBJECT((byte)0x46, "aget-object", ReferenceType.none, Format.Format23x), + AGET_BOOLEAN((byte)0x47, "aget-boolean", ReferenceType.none, Format.Format23x), + AGET_BYTE((byte)0x48, "aget-byte", ReferenceType.none, Format.Format23x), + AGET_CHAR((byte)0x49, "aget-char", ReferenceType.none, Format.Format23x), + AGET_SHORT((byte)0x4a, "aget-short", ReferenceType.none, Format.Format23x), + APUT((byte)0x4b, "aput", ReferenceType.none, Format.Format23x), + APUT_WIDE((byte)0x4c, "aput-wide", ReferenceType.none, Format.Format23x), + APUT_OBJECT((byte)0x4d, "aput-object", ReferenceType.none, Format.Format23x), + APUT_BOOLEAN((byte)0x4e, "aput-boolean", ReferenceType.none, Format.Format23x), + APUT_BYTE((byte)0x4f, "aput-byte", ReferenceType.none, Format.Format23x), + APUT_CHAR((byte)0x50, "aput-char", ReferenceType.none, Format.Format23x), + APUT_SHORT((byte)0x51, "aput-short", ReferenceType.none, Format.Format23x), + IGET((byte)0x52, "iget", ReferenceType.field, Format.Format22c), + IGET_WIDE((byte)0x53, "iget-wide", ReferenceType.field, Format.Format22c), + IGET_OBJECT((byte)0x54, "iget-object", ReferenceType.field, Format.Format22c), + IGET_BOOLEAN((byte)0x55, "iget-boolean", ReferenceType.field, Format.Format22c), + IGET_BYTE((byte)0x56, "iget-byte", ReferenceType.field, Format.Format22c), + IGET_CHAR((byte)0x57, "iget-char", ReferenceType.field, Format.Format22c), + IGET_SHORT((byte)0x58, "iget-short", ReferenceType.field, Format.Format22c), + IPUT((byte)0x59, "iput", ReferenceType.field, Format.Format22c), + IPUT_WIDE((byte)0x5a, "iput-wide", ReferenceType.field, Format.Format22c), + IPUT_OBJECT((byte)0x5b, "iput-object", ReferenceType.field, Format.Format22c), + IPUT_BOOLEAN((byte)0x5c, "iput-boolean", ReferenceType.field, Format.Format22c), + IPUT_BYTE((byte)0x5d, "iput-byte", ReferenceType.field, Format.Format22c), + IPUT_CHAR((byte)0x5e, "iput-char", ReferenceType.field, Format.Format22c), + IPUT_SHORT((byte)0x5f, "iput-short", ReferenceType.field, Format.Format22c), + SGET((byte)0x60, "sget", ReferenceType.field, Format.Format21c), + SGET_WIDE((byte)0x61, "sget-wide", ReferenceType.field, Format.Format21c), + SGET_OBJECT((byte)0x62, "sget-object", ReferenceType.field, Format.Format21c), + SGET_BOOLEAN((byte)0x63, "sget-boolean", ReferenceType.field, Format.Format21c), + SGET_BYTE((byte)0x64, "sget-byte", ReferenceType.field, Format.Format21c), + SGET_CHAR((byte)0x65, "sget-char", ReferenceType.field, Format.Format21c), + SGET_SHORT((byte)0x66, "sget-short", ReferenceType.field, Format.Format21c), + SPUT((byte)0x67, "sput", ReferenceType.field, Format.Format21c), + SPUT_WIDE((byte)0x68, "sput-wide", ReferenceType.field, Format.Format21c), + SPUT_OBJECT((byte)0x69, "sput-object", ReferenceType.field, Format.Format21c), + SPUT_BOOLEAN((byte)0x6a, "sput-boolean", ReferenceType.field, Format.Format21c), + SPUT_BYTE((byte)0x6b, "sput-byte", ReferenceType.field, Format.Format21c), + SPUT_CHAR((byte)0x6c, "sput-char", ReferenceType.field, Format.Format21c), + SPUT_SHORT((byte)0x6d, "sput-short", ReferenceType.field, Format.Format21c), + INVOKE_VIRTUAL((byte)0x6e, "invoke-virtual", ReferenceType.method, Format.Format35c), + INVOKE_SUPER((byte)0x6f, "invoke-super", ReferenceType.method, Format.Format35c), + INVOKE_DIRECT((byte)0x70, "invoke-direct", ReferenceType.method, Format.Format35c), + INVOKE_STATIC((byte)0x71, "invoke-static", ReferenceType.method, Format.Format35c), + INVOKE_INTERFACE((byte)0x72, "invoke-interface", ReferenceType.method, Format.Format35c), + INVOKE_VIRTUAL_RANGE((byte)0x74, "invoke-virtual/range", ReferenceType.method, Format.Format3rc), + INVOKE_SUPER_RANGE((byte)0x75, "invoke-super/range", ReferenceType.method, Format.Format3rc), + INVOKE_DIRECT_RANGE((byte)0x76, "invoke-direct/range", ReferenceType.method, Format.Format3rc), + INVOKE_STATIC_RANGE((byte)0x77, "invoke-static/range", ReferenceType.method, Format.Format3rc), + INVOKE_INTERFACE_RANGE((byte)0x78, "invoke-interface/range", ReferenceType.method, Format.Format3rc), + NEG_INT((byte)0x7b, "neg-int", ReferenceType.none, Format.Format12x), + NOT_INT((byte)0x7c, "not-int", ReferenceType.none, Format.Format12x), + NEG_LONG((byte)0x7d, "neg-long", ReferenceType.none, Format.Format12x), + NOT_LONG((byte)0x7e, "not-long", ReferenceType.none, Format.Format12x), + NEG_FLOAT((byte)0x7f, "neg-float", ReferenceType.none, Format.Format12x), + NEG_DOUBLE((byte)0x80, "neg-double", ReferenceType.none, Format.Format12x), + INT_TO_LONG((byte)0x81, "int-to-long", ReferenceType.none, Format.Format12x), + INT_TO_FLOAT((byte)0x82, "int-to-float", ReferenceType.none, Format.Format12x), + INT_TO_DOUBLE((byte)0x83, "int-to-double", ReferenceType.none, Format.Format12x), + LONG_TO_INT((byte)0x84, "long-to-int", ReferenceType.none, Format.Format12x), + LONG_TO_FLOAT((byte)0x85, "long-to-float", ReferenceType.none, Format.Format12x), + LONG_TO_DOUBLE((byte)0x86, "long-to-double", ReferenceType.none, Format.Format12x), + FLOAT_TO_INT((byte)0x87, "float-to-int", ReferenceType.none, Format.Format12x), + FLOAT_TO_LONG((byte)0x88, "float-to-long", ReferenceType.none, Format.Format12x), + FLOAT_TO_DOUBLE((byte)0x89, "float-to-double", ReferenceType.none, Format.Format12x), + DOUBLE_TO_INT((byte)0x8a, "double-to-int", ReferenceType.none, Format.Format12x), + DOUBLE_TO_LONG((byte)0x8b, "double-to-long", ReferenceType.none, Format.Format12x), + DOUBLE_TO_FLOAT((byte)0x8c, "double-to-float", ReferenceType.none, Format.Format12x), + INT_TO_BYTE((byte)0x8d, "int-to-byte", ReferenceType.none, Format.Format12x), + INT_TO_CHAR((byte)0x8e, "int-to-char", ReferenceType.none, Format.Format12x), + INT_TO_SHORT((byte)0x8f, "int-to-short", ReferenceType.none, Format.Format12x), + ADD_INT((byte)0x90, "add-int", ReferenceType.none, Format.Format23x), + SUB_INT((byte)0x91, "sub-int", ReferenceType.none, Format.Format23x), + MUL_INT((byte)0x92, "mul-int", ReferenceType.none, Format.Format23x), + DIV_INT((byte)0x93, "div-int", ReferenceType.none, Format.Format23x), + REM_INT((byte)0x94, "rem-int", ReferenceType.none, Format.Format23x), + AND_INT((byte)0x95, "and-int", ReferenceType.none, Format.Format23x), + OR_INT((byte)0x96, "or-int", ReferenceType.none, Format.Format23x), + XOR_INT((byte)0x97, "xor-int", ReferenceType.none, Format.Format23x), + SHL_INT((byte)0x98, "shl-int", ReferenceType.none, Format.Format23x), + SHR_INT((byte)0x99, "shr-int", ReferenceType.none, Format.Format23x), + USHR_INT((byte)0x9a, "ushr-int", ReferenceType.none, Format.Format23x), + ADD_LONG((byte)0x9b, "add-long", ReferenceType.none, Format.Format23x), + SUB_LONG((byte)0x9c, "sub-long", ReferenceType.none, Format.Format23x), + MUL_LONG((byte)0x9d, "mul-long", ReferenceType.none, Format.Format23x), + DIV_LONG((byte)0x9e, "div-long", ReferenceType.none, Format.Format23x), + REM_LONG((byte)0x9f, "rem-long", ReferenceType.none, Format.Format23x), + AND_LONG((byte)0xa0, "and-long", ReferenceType.none, Format.Format23x), + OR_LONG((byte)0xa1, "or-long", ReferenceType.none, Format.Format23x), + XOR_LONG((byte)0xa2, "xor-long", ReferenceType.none, Format.Format23x), + SHL_LONG((byte)0xa3, "shl-long", ReferenceType.none, Format.Format23x), + SHR_LONG((byte)0xa4, "shr-long", ReferenceType.none, Format.Format23x), + USHR_LONG((byte)0xa5, "ushr-long", ReferenceType.none, Format.Format23x), + ADD_FLOAT((byte)0xa6, "add-float", ReferenceType.none, Format.Format23x), + SUB_FLOAT((byte)0xa7, "sub-float", ReferenceType.none, Format.Format23x), + MUL_FLOAT((byte)0xa8, "mul-float", ReferenceType.none, Format.Format23x), + DIV_FLOAT((byte)0xa9, "div-float", ReferenceType.none, Format.Format23x), + REM_FLOAT((byte)0xaa, "rem-float", ReferenceType.none, Format.Format23x), + ADD_DOUBLE((byte)0xab, "add-double", ReferenceType.none, Format.Format23x), + SUB_DOUBLE((byte)0xac, "sub-double", ReferenceType.none, Format.Format23x), + MUL_DOUBLE((byte)0xad, "mul-double", ReferenceType.none, Format.Format23x), + DIV_DOUBLE((byte)0xae, "div-double", ReferenceType.none, Format.Format23x), + REM_DOUBLE((byte)0xaf, "rem-double", ReferenceType.none, Format.Format23x), + ADD_INT_2ADDR((byte)0xb0, "add-int/2addr", ReferenceType.none, Format.Format12x), + SUB_INT_2ADDR((byte)0xb1, "sub-int/2addr", ReferenceType.none, Format.Format12x), + MUL_INT_2ADDR((byte)0xb2, "mul-int/2addr", ReferenceType.none, Format.Format12x), + DIV_INT_2ADDR((byte)0xb3, "div-int/2addr", ReferenceType.none, Format.Format12x), + REM_INT_2ADDR((byte)0xb4, "rem-int/2addr", ReferenceType.none, Format.Format12x), + AND_INT_2ADDR((byte)0xb5, "and-int/2addr", ReferenceType.none, Format.Format12x), + OR_INT_2ADDR((byte)0xb6, "or-int/2addr", ReferenceType.none, Format.Format12x), + XOR_INT_2ADDR((byte)0xb7, "xor-int/2addr", ReferenceType.none, Format.Format12x), + SHL_INT_2ADDR((byte)0xb8, "shl-int/2addr", ReferenceType.none, Format.Format12x), + SHR_INT_2ADDR((byte)0xb9, "shr-int/2addr", ReferenceType.none, Format.Format12x), + USHR_INT_2ADDR((byte)0xba, "ushr-int/2addr", ReferenceType.none, Format.Format12x), + ADD_LONG_2ADDR((byte)0xbb, "add-long/2addr", ReferenceType.none, Format.Format12x), + SUB_LONG_2ADDR((byte)0xbc, "sub-long/2addr", ReferenceType.none, Format.Format12x), + MUL_LONG_2ADDR((byte)0xbd, "mul-long/2addr", ReferenceType.none, Format.Format12x), + DIV_LONG_2ADDR((byte)0xbe, "div-long/2addr", ReferenceType.none, Format.Format12x), + REM_LONG_2ADDR((byte)0xbf, "rem-long/2addr", ReferenceType.none, Format.Format12x), + AND_LONG_2ADDR((byte)0xc0, "and-long/2addr", ReferenceType.none, Format.Format12x), + OR_LONG_2ADDR((byte)0xc1, "or-long/2addr", ReferenceType.none, Format.Format12x), + XOR_LONG_2ADDR((byte)0xc2, "xor-long/2addr", ReferenceType.none, Format.Format12x), + SHL_LONG_2ADDR((byte)0xc3, "shl-long/2addr", ReferenceType.none, Format.Format12x), + SHR_LONG_2ADDR((byte)0xc4, "shr-long/2addr", ReferenceType.none, Format.Format12x), + USHR_LONG_2ADDR((byte)0xc5, "ushr-long/2addr", ReferenceType.none, Format.Format12x), + ADD_FLOAT_2ADDR((byte)0xc6, "add-float/2addr", ReferenceType.none, Format.Format12x), + SUB_FLOAT_2ADDR((byte)0xc7, "sub-float/2addr", ReferenceType.none, Format.Format12x), + MUL_FLOAT_2ADDR((byte)0xc8, "mul-float/2addr", ReferenceType.none, Format.Format12x), + DIV_FLOAT_2ADDR((byte)0xc9, "div-float/2addr", ReferenceType.none, Format.Format12x), + REM_FLOAT_2ADDR((byte)0xca, "rem-float/2addr", ReferenceType.none, Format.Format12x), + ADD_DOUBLE_2ADDR((byte)0xcb, "add-double/2addr", ReferenceType.none, Format.Format12x), + SUB_DOUBLE_2ADDR((byte)0xcc, "sub-double/2addr", ReferenceType.none, Format.Format12x), + MUL_DOUBLE_2ADDR((byte)0xcd, "mul-double/2addr", ReferenceType.none, Format.Format12x), + DIV_DOUBLE_2ADDR((byte)0xce, "div-double/2addr", ReferenceType.none, Format.Format12x), + REM_DOUBLE_2ADDR((byte)0xcf, "rem-double/2addr", ReferenceType.none, Format.Format12x), + ADD_INT_LIT16((byte)0xd0, "add-int/lit16", ReferenceType.none, Format.Format22s), + RSUB_INT((byte)0xd1, "rsub-int", ReferenceType.none, Format.Format22s), + MUL_INT_LIT16((byte)0xd2, "mul-int/lit16", ReferenceType.none, Format.Format22s), + DIV_INT_LIT16((byte)0xd3, "div-int/lit16", ReferenceType.none, Format.Format22s), + REM_INT_LIT16((byte)0xd4, "rem-int/lit16", ReferenceType.none, Format.Format22s), + AND_INT_LIT16((byte)0xd5, "and-int/lit16", ReferenceType.none, Format.Format22s), + OR_INT_LIT16((byte)0xd6, "or-int/lit16", ReferenceType.none, Format.Format22s), + XOR_INT_LIT16((byte)0xd7, "xor-int/lit16", ReferenceType.none, Format.Format22s), + ADD_INT_LIT8((byte)0xd8, "add-int/lit8", ReferenceType.none, Format.Format22b), + RSUB_INT_LIT8((byte)0xd9, "rsub-int/lit8", ReferenceType.none, Format.Format22b), + MUL_INT_LIT8((byte)0xda, "mul-int/lit8", ReferenceType.none, Format.Format22b), + DIV_INT_LIT8((byte)0xdb, "div-int/lit8", ReferenceType.none, Format.Format22b), + REM_INT_LIT8((byte)0xdc, "rem-int/lit8", ReferenceType.none, Format.Format22b), + AND_INT_LIT8((byte)0xdd, "and-int/lit8", ReferenceType.none, Format.Format22b), + OR_INT_LIT8((byte)0xde, "or-int/lit8", ReferenceType.none, Format.Format22b), + XOR_INT_LIT8((byte)0xdf, "xor-int/lit8", ReferenceType.none, Format.Format22b), + SHL_INT_LIT8((byte)0xe0, "shl-int/lit8", ReferenceType.none, Format.Format22b), + SHR_INT_LIT8((byte)0xe1, "shr-int/lit8", ReferenceType.none, Format.Format22b), + USHR_INT_LIT8((byte)0xe2, "ushr-int/lit8", ReferenceType.none, Format.Format22b); + + + + private static Opcode[] opcodesByValue; + private static HashMap opcodesByName; + + static { + opcodesByValue = new Opcode[256]; + opcodesByName = new HashMap(); + + for (Opcode opcode: Opcode.values()) { + opcodesByValue[opcode.value & 0xFF] = opcode; + opcodesByName.put(opcode.name.hashCode(), opcode); + } + } + + public static Opcode getOpcodeByName(String opcodeName) { + return opcodesByName.get(opcodeName.toLowerCase().hashCode()); + } + + public static Opcode getOpcodeByValue(byte opcodeValue) { + return opcodesByValue[opcodeValue & 0xFF]; + } + + public final byte value; + public final String name; + public final ReferenceType referenceType; + public final Format format; + + Opcode(byte opcodeValue, String opcodeName, ReferenceType referenceType, Format format) { + this.value = opcodeValue; + this.name = opcodeName; + this.referenceType = referenceType; + this.format = format; + } +} diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/ReferenceType.java b/dexlib/src/main/java/org/jf/dexlib/Code/ReferenceType.java new file mode 100644 index 00000000..8590dae3 --- /dev/null +++ b/dexlib/src/main/java/org/jf/dexlib/Code/ReferenceType.java @@ -0,0 +1,56 @@ +/* + * [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.*; + +public enum ReferenceType +{ + string, + type, + field, + method, + none; + + public boolean checkItem(Item item) { + switch (this) { + case string: + return item instanceof StringIdItem; + case type: + return item instanceof TypeIdItem; + case field: + return item instanceof FieldIdItem; + case method: + return item instanceof MethodIdItem; + case none: + return item == null; + } + return false; + } +} \ No newline at end of file diff --git a/dexlib/src/main/java/org/jf/dexlib/CodeItem.java b/dexlib/src/main/java/org/jf/dexlib/CodeItem.java new file mode 100644 index 00000000..3417b701 --- /dev/null +++ b/dexlib/src/main/java/org/jf/dexlib/CodeItem.java @@ -0,0 +1,562 @@ +/* + * [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.Code.InstructionReader; +import org.jf.dexlib.Code.InstructionIterator; +import org.jf.dexlib.Code.Opcode; +import org.jf.dexlib.Code.InstructionWriter; +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.Input; +import org.jf.dexlib.Util.SparseArray; +import org.jf.dexlib.Util.Leb128Utils; + +public class CodeItem extends Item { + private int registerCount; + private int inWords; + private int outWords; + private DebugInfoItem debugInfo; + private byte[] encodedInstructions; + private Item[] referencedItems; + private TryItem[] tries; + private EncodedCatchHandler[] encodedCatchHandlers; + + private MethodIdItem parent; + + /** + * Creates a new uninitialized CodeItem + * @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(); + SparseArray handlerMap = new SparseArray(handlerCount); + encodedCatchHandlers = new EncodedCatchHandler[handlerCount]; + for (int i=0; i 0) { + if (encodedInstructions.length % 2 == 1) { + offset++; + } + + offset += tries.length * 8; + int encodedCatchHandlerBaseOffset = offset; + for (EncodedCatchHandler encodedCatchHandler: encodedCatchHandlers) { + offset += encodedCatchHandler.place(offset, encodedCatchHandlerBaseOffset); + } + } + return offset; + } + + /** {@inheritDoc} */ + protected void writeItem(final AnnotatedOutput out) { + if (out.annotates()) { + out.annotate(2, "registers_size"); + out.annotate(2, "ins_size"); + out.annotate(2, "outs_size"); + out.annotate(2, "tries_size"); + out.annotate(4, "debug_info_off"); + out.annotate(4, "insns_size"); + InstructionIterator.IterateInstructions(encodedInstructions, + new InstructionIterator.ProcessRawInstructionDelegate() { + + public void ProcessNormalInstruction(Opcode opcode, int index) { + out.annotate(opcode.format.size, opcode.name + " instruction"); + } + + public void ProcessReferenceInstruction(Opcode opcode, int index) { + out.annotate(opcode.format.size, opcode.name + " instruction"); + } + + public void ProcessPackedSwitchInstruction(int index, int targetCount, int instructionLength) { + out.annotate(instructionLength, "packed_switch instruction"); + } + + public void ProcessSparseSwitchInstruction(int index, int targetCount, int instructionLength) { + out.annotate(instructionLength, "sparse_switch instruction"); + } + + public void ProcessFillArrayDataInstruction(int index, int elementWidth, int elementCount, int instructionLength) { + out.annotate(instructionLength, "fill_array_data instruction"); + } + }); + if (tries != null && (tries.length % 2 == 1)) { + out.annotate(2, "padding"); + } + } + + out.writeShort(registerCount); + out.writeShort(inWords); + out.writeShort(outWords); + if (tries == null) { + out.writeShort(0); + } else { + out.writeShort(tries.length); + } + if (debugInfo == null) { + out.writeInt(0); + } else { + out.writeInt(debugInfo.getIndex()); + } + out.writeInt(encodedInstructions.length / 2); + InstructionWriter.writeInstructions(encodedInstructions, referencedItems, out); + + if (tries != null && tries.length > 0) { + if ((tries.length % 2) == 1) { + out.writeShort(0); + } + + for (TryItem tryItem: tries) { + tryItem.writeTo(out); + } + + out.writeUnsignedLeb128(encodedCatchHandlers.length); + + for (EncodedCatchHandler encodedCatchHandler: encodedCatchHandlers) { + encodedCatchHandler.writeTo(out); + } + } + } + + /** {@inheritDoc} */ + public ItemType getItemType() { + return ItemType.TYPE_CODE_ITEM; + } + + /** {@inheritDoc} */ + public String getConciseIdentity() { + //TODO: should mention the method name here + return "code_item @0x" + Integer.toHexString(getOffset()); + } + + /** {@inheritDoc} */ + public int compareTo(CodeItem other) { + if (parent == null) { + if (other.parent == null) { + return 0; + } + return -1; + } + if (other.parent == null) { + return 1; + } + return parent.compareTo(other.parent); + } + + /** + * @return the register count + */ + public int getRegisterCount() { + return registerCount; + } + + /** + * @return a byte array containing the encoded instructions + */ + public byte[] getEncodedInstructions() { + return encodedInstructions; + } + + /** + * @return an array of the TryItem 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, SparseArray encodedCatchHandlers) { + startAddress = in.readInt(); + instructionCount = in.readShort(); + + encodedCatchHandler = encodedCatchHandlers.get(in.readShort()); + if (encodedCatchHandler == null) { + throw new RuntimeException("Could not find the EncodedCatchHandler referenced by this TryItem"); + } + } + + /** + * Writes the TryItem 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 from the beginning of the + * encoded_catch_handler_list structure + */ + private int getOffsetInList() { + return offset-baseOffset; + } + + /** + * Places the EncodedCatchHandler, 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 Item { + private int lineStart; + private StringIdItem[] parameterNames; + private byte[] encodedDebugInfo; + private Item[] referencedItems; + + private CodeItem parent = null; + + /** + * Creates a new uninitialized DebugInfoInfo + * @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()]; + IndexedSection stringIdSection = dexFile.StringIdsSection; + for (int i=0; i referencedItemsList = new ArrayList(50); + DebugInstructionIterator.IterateInstructions(in, + new DebugInstructionIterator.ProcessRawDebugInstructionDelegate() { + @Override + public void ProcessStartLocal(int startOffset, int length, int registerNum, int nameIndex, + int typeIndex) { + if (nameIndex != -1) { + referencedItemsList.add(dexFile.StringIdsSection.getItemByIndex(nameIndex)); + } + if (typeIndex != -1) { + referencedItemsList.add(dexFile.TypeIdsSection.getItemByIndex(typeIndex)); + } + } + + @Override + public void ProcessStartLocalExtended(int startOffset, int length, int registerNume, int nameIndex, + int typeIndex, int signatureIndex) { + if (nameIndex != -1) { + referencedItemsList.add(dexFile.StringIdsSection.getItemByIndex(nameIndex)); + } + if (typeIndex != -1) { + referencedItemsList.add(dexFile.TypeIdsSection.getItemByIndex(typeIndex)); + } + if (signatureIndex != -1) { + referencedItemsList.add(dexFile.StringIdsSection.getItemByIndex(signatureIndex)); + } + } + + @Override + public void ProcessSetFile(int startOffset, int length, int nameIndex) { + if (nameIndex != -1) { + referencedItemsList.add(dexFile.StringIdsSection.getItemByIndex(nameIndex)); + } + } + }); + + referencedItems = new Item[referencedItemsList.size()]; + referencedItemsList.toArray(referencedItems); + + int length = in.getCursor() - start; + in.setCursor(start); + encodedDebugInfo = in.readBytes(length); + } + + + + /** {@inheritDoc} */ + protected int placeItem(int offset) { + offset += Leb128Utils.unsignedLeb128Size(lineStart); + offset += Leb128Utils.unsignedLeb128Size(parameterNames.length); + for (StringIdItem parameterName: parameterNames) { + int indexp1; + if (parameterName == null) { + indexp1 = 0; + } else { + indexp1 = parameterName.getIndex() + 1; + } + offset += Leb128Utils.unsignedLeb128Size(indexp1); + } + + //make a subclass so we can keep track of and access the computed length + class ProcessDebugInstructionDelegateWithLength extends + DebugInstructionIterator.ProcessRawDebugInstructionDelegate { + public int length = 0; + } + ProcessDebugInstructionDelegateWithLength pdidwl; + + //final referencedItems = this.referencedItems; + + DebugInstructionIterator.IterateInstructions(new ByteArrayInput(encodedDebugInfo), + pdidwl = new ProcessDebugInstructionDelegateWithLength() { + private int referencedItemsPosition = 0; + + @Override + public void ProcessStaticOpcode(int startOffset, int length) { + this.length+=length; + } + + @Override + public void ProcessStartLocal(int startOffset, int length, int registerNum, int nameIndex, + int typeIndex) { + this.length+=Leb128Utils.unsignedLeb128Size(registerNum); + if (nameIndex != 0) { + this.length+= + Leb128Utils.unsignedLeb128Size(referencedItems[referencedItemsPosition++].getIndex()+1); + } else { + this.length++; + } + if (typeIndex != 0) { + this.length+= + Leb128Utils.unsignedLeb128Size(referencedItems[referencedItemsPosition++].getIndex()+1); + } else { + this.length++; + } + + } + + @Override + public void ProcessStartLocalExtended(int startOffset, int length, int registerNum, int nameIndex, + int typeIndex, int signatureIndex) { + this.length+=Leb128Utils.unsignedLeb128Size(registerNum); + if (nameIndex != 0) { + this.length+= + Leb128Utils.unsignedLeb128Size(referencedItems[referencedItemsPosition++].getIndex()+1); + } else { + this.length++; + } + if (typeIndex != 0) { + this.length+= + Leb128Utils.unsignedLeb128Size(referencedItems[referencedItemsPosition++].getIndex()+1); + } else { + this.length++; + } + if (signatureIndex != 0) { + this.length+= + Leb128Utils.unsignedLeb128Size(referencedItems[referencedItemsPosition++].getIndex()+1); + } else { + this.length++; + } + } + + @Override + public void ProcessSetFile(int startOffset, int length, int nameIndex) { + if (nameIndex != 0) { + this.length+= + Leb128Utils.unsignedLeb128Size(referencedItems[referencedItemsPosition++].getIndex()+1); + } else { + this.length++; + } + } + }); + return offset + pdidwl.length; + } + + /** {@inheritDoc} */ + protected void writeItem(final AnnotatedOutput out) { + out.writeUnsignedLeb128(lineStart); + out.writeUnsignedLeb128(parameterNames.length); + for (StringIdItem parameterName: parameterNames) { + int indexp1; + if (parameterName == null) { + indexp1 = 0; + } else { + indexp1 = parameterName.getIndex() + 1; + } + out.writeUnsignedLeb128(indexp1); + } + + DebugInstructionIterator.IterateInstructions(new ByteArrayInput(encodedDebugInfo), + new DebugInstructionIterator.ProcessRawDebugInstructionDelegate() { + private int referencedItemsPosition = 0; + + @Override + public void ProcessStaticOpcode(int startOffset, int length) { + out.write(encodedDebugInfo, startOffset, length); + } + + @Override + public void ProcessStartLocal(int startOffset, int length, int registerNum, int nameIndex, + int typeIndex) { + out.writeUnsignedLeb128(registerNum); + if (nameIndex != -1) { + out.writeUnsignedLeb128(referencedItems[referencedItemsPosition++].getIndex() + 1); + } else { + out.writeByte(0); + } + if (typeIndex != -1) { + out.writeUnsignedLeb128(referencedItems[referencedItemsPosition++].getIndex() + 1); + } else { + out.writeByte(0); + } + } + + @Override + public void ProcessStartLocalExtended(int startOffset, int length, int registerNum, int nameIndex, + int typeIndex, int signatureIndex) { + out.writeUnsignedLeb128(registerNum); + if (nameIndex != -1) { + out.writeUnsignedLeb128(referencedItems[referencedItemsPosition++].getIndex() + 1); + } else { + out.writeByte(0); + } + if (typeIndex != -1) { + out.writeUnsignedLeb128(referencedItems[referencedItemsPosition++].getIndex() + 1); + } else { + out.writeByte(0); + } + if (signatureIndex != -1) { + out.writeUnsignedLeb128(referencedItems[referencedItemsPosition++].getIndex() + 1); + } else { + out.writeByte(0); + } + } + + @Override + public void ProcessSetFile(int startOffset, int length, int nameIndex) { + if (nameIndex != -1) { + out.writeUnsignedLeb128(referencedItems[referencedItemsPosition++].getIndex() + 1); + } else { + out.writeByte(0); + } + } + }); + } + + /** {@inheritDoc} */ + public ItemType getItemType() { + return ItemType.TYPE_DEBUG_INFO_ITEM; + } + + /** {@inheritDoc} */ + public String getConciseIdentity() { + return "debug_info_item @0x" + Integer.toHexString(getOffset()); + } + + /** {@inheritDoc} */ + public int compareTo(DebugInfoItem other) { + if (parent == null) { + if (other.parent == null) { + return 0; + } + return -1; + } + if (other.parent == null) { + return 1; + } + return parent.compareTo(other.parent); + } + + /** + * Set the CodeItem 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

+ * + *

These are the main use cases that drove the design of this library

+ * + *
    + *
  1. 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)

  2. + * + *
  3. 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 + *

      + *
    • the order of exception handlers in the EncodedCatchHandlerList for a method
    • + *
    • the ordering of some of the debug info in the {@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

  4. + * + *
  5. 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.

  6. + * + * + *
  7. 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.

  8. + * + * + *

    Other use cases

    + * + *

    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

    + * + *
      + *
    • deleting classes/methods/etc. from a dex file
    • + *
    • merging 2 dex files
    • + *
    • splitting a dex file
    • + *
    • moving classes from 1 dex file to another
    • + *
    • removing the debug information from a dex file
    • + *
    • obfustication of a dex file
    • + *
    + */ +public class DexFile +{ + /** + * A mapping from ItemType to the section that contains items of the given type + */ + private final Section[] sectionsByType; + + /** + * Ordered lists of the indexed and offsetted sections. The order of these lists specifies the order + * that the sections will be written in + */ + private final IndexedSection[] indexedSections; + private final OffsettedSection[] offsettedSections; + + /** + * dalvik had a bug where it wrote the registers for certain types of debug info in a signed leb + * format, instead of an unsigned leb format. There are no negative registers of course, but + * certain positive values have a different encoding depending on whether they are encoded as + * an unsigned leb128 or a signed leb128. Specifically, the signed leb128 is 1 byte longer in some cases. + * + * This determine whether we should keep any signed registers as signed, or force all register to + * unsigned. By default we don't keep track of whether they were signed or not, and write them back + * out as unsigned. This option only has an effect when reading an existing dex file. It has no + * effect when a dex file is created from scratch + * + * The 2 main use-cases in play are + * 1. Annotate an existing dex file - In this case, preserveSignedRegisters should be false, so that we keep + * track of any signed registers and write them back out as signed Leb128 values. + * + * 2. Canonicalize an existing dex file - In this case, fixRegisters should be true, so that all + * registers in the debug info are written as unsigned Leb128 values regardless of how they were + * originally encoded + */ + private final boolean preserveSignedRegisters; + + /** + * When true, this prevents any sorting of the items during placement of the dex file. This + * should *only* be set to true when this dex file was read in from an existing (valid) dex file, + * and no modifications were made (i.e. no items added or deleted). Otherwise it is likely that + * an invalid dex file will be generated. + * + * This is useful for the first use case (annotating an existing dex file). This ensures the items + * retain the same order as in the original dex file. + */ + private boolean inplace = false; + + /** + * When true, this imposes an full ordering on all the items, to force them into a (possibly + * arbitrary) canonical order. When false, only the items that the dex format specifies + * an order for are sorted. The rest of the items are not ordered. + * + * This is useful for the second use case (canonicalizing an existing dex file) or possibly for + * the third use case (creating a dex file from scratch), if there is a need to write the new + * dex file in a canonical form. + */ + private boolean sortAllItems = false; + + + /** + * this is used to access the dex file from within inner classes, when they declare fields or + * variable that hide fields on this object + */ + private final DexFile dexFile = this; + + + private int dataOffset; + private int dataSize; + private int fileSize; + + + /** + * A private constructor containing common code to initialize the section maps and lists + * @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() + */ + 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 The specific item subclass - inferred from the passed item + * @return the Section containing items of the same type as the given item + */ + public Section getSectionForItem(T item) { + return (Section)sectionsByType[item.getItemType().SectionIndex]; + } + + /** + * Get the 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 Comparator
    () { + public int compare(Section a, Section b) { + return a.getOffset() - b.getOffset(); + } + }); + + return sections; + } + + /** + * This method should be called before writing a dex file. It sorts the sections + * as needed or as indicated by getSortAllItems() 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 IndexedSection StringIdsSection = + new IndexedSection(this, ItemType.TYPE_STRING_ID_ITEM); + + /** + * The IndexedSection containing TypeIdItem items + */ + public final IndexedSection TypeIdsSection = + new IndexedSection(this, ItemType.TYPE_TYPE_ID_ITEM); + + /** + * The IndexedSection containing ProtoIdItem items + */ + public final IndexedSection ProtoIdsSection = + new IndexedSection(this, ItemType.TYPE_PROTO_ID_ITEM); + + /** + * The IndexedSection containing FieldIdItem items + */ + public final IndexedSection FieldIdsSection = + new IndexedSection(this, ItemType.TYPE_FIELD_ID_ITEM); + + /** + * The IndexedSection containing MethodIdItem items + */ + public final IndexedSection MethodIdsSection = + new IndexedSection(this, ItemType.TYPE_METHOD_ID_ITEM); + + /** + * The IndexedSection containing ClassDefItem items + */ + public final IndexedSection ClassDefsSection = + new IndexedSection(this, ItemType.TYPE_CLASS_DEF_ITEM) { + + public int placeAt(int offset) { + if (dexFile.getInplace()) { + return super.placeAt(offset); + } + + int ret = ClassDefItem.placeClassDefItems(this, offset); + + this.offset = items.get(0).getOffset(); + return ret; + } + }; + + /** + * The OffsettedSection containing TypeListItem items + */ + public final OffsettedSection TypeListsSection = + new OffsettedSection(this, ItemType.TYPE_TYPE_LIST); + + /** + * The OffsettedSection containing AnnotationSetRefList items + */ + public final OffsettedSection AnnotationSetRefListsSection = + new OffsettedSection(this, ItemType.TYPE_ANNOTATION_SET_REF_LIST); + + /** + * The OffsettedSection containing AnnotationSetItem items + */ + public final OffsettedSection AnnotationSetsSection = + new OffsettedSection(this, ItemType.TYPE_ANNOTATION_SET_ITEM); + + /** + * The OffsettedSection containing ClassDataItem items + */ + public final OffsettedSection ClassDataSection = + new OffsettedSection(this, ItemType.TYPE_CLASS_DATA_ITEM); + + /** + * The OffsettedSection containing CodeItem items + */ + public final OffsettedSection CodeItemsSection = + new OffsettedSection(this, ItemType.TYPE_CODE_ITEM); + + /** + * The OffsettedSection containing StringDataItem items + */ + public final OffsettedSection StringDataSection = + new OffsettedSection(this, ItemType.TYPE_STRING_DATA_ITEM); + + /** + * The OffsettedSection containing DebugInfoItem items + */ + public final OffsettedSection DebugInfoItemsSection = + new OffsettedSection(this, ItemType.TYPE_DEBUG_INFO_ITEM); + + /** + * The OffsettedSection containing AnnotationItem items + */ + public final OffsettedSection AnnotationsSection = + new OffsettedSection(this, ItemType.TYPE_ANNOTATION_ITEM); + + /** + * The OffsettedSection containing EncodedArrayItem items + */ + public final OffsettedSection EncodedArraysSection = + new OffsettedSection(this, ItemType.TYPE_ENCODED_ARRAY_ITEM); + + /** + * The OffsettedSection containing AnnotationDirectoryItem items + */ + public final OffsettedSection AnnotationDirectoriesSection = + new OffsettedSection(this, ItemType.TYPE_ANNOTATIONS_DIRECTORY_ITEM); + + + /** + * Calculates the signature for the dex file in the given byte array, + * and then writes the signature to the appropriate location in the header + * containing in the array + * + * @param bytes non-null; the bytes of the file + */ + public static void calcSignature(byte[] bytes) { + MessageDigest md; + + try { + md = MessageDigest.getInstance("SHA-1"); + } catch (NoSuchAlgorithmException ex) { + throw new RuntimeException(ex); + } + + md.update(bytes, 32, bytes.length - 32); + + try { + int amt = md.digest(bytes, 12, 20); + if (amt != 20) { + throw new RuntimeException("unexpected digest write: " + amt + + " bytes"); + } + } catch (DigestException ex) { + throw new RuntimeException(ex); + } + } + + /** + * Calculates the checksum for the .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 Item { + private int hashCode = 0; + + private ArrayEncodedSubValue encodedArray; + + /** + * Creates a new uninitialized EncodedArrayItem + * @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; iAnnotationEncodedValue 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 AnnotationEncodedSubValue(TypeIdItem annotationType, StringIdItem[] names, EncodedValue[] values) { + this.annotationType = annotationType; + if (names.length != values.length) { + throw new RuntimeException("The names and values parameters must be the same length"); + } + this.names = names; + this.values = values; + } + + /** {@inheritDoc} */ + public void writeValue(AnnotatedOutput out) { + out.writeUnsignedLeb128(annotationType.getIndex()); + out.writeUnsignedLeb128(names.length); + + for (int i=0; iAnnotationEncodedValue 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 + */ + 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; iArrayEncodedSubValue with the given values + * @param values The array values + */ + public ArrayEncodedSubValue(EncodedValue[] values) { + this.values = values; + } + + /** {@inheritDoc} */ + public void writeValue(AnnotatedOutput out) { + out.writeByte(ValueType.VALUE_ARRAY.value); + out.writeUnsignedLeb128(values.length); + for (EncodedValue encodedValue: values) { + encodedValue.writeValue(out); + } + } + + /** {@inheritDoc} */ + public int placeValue(int offset) { + offset = offset + 1 + Leb128Utils.unsignedLeb128Size(values.length); + for (EncodedValue encodedValue: values) { + offset = encodedValue.placeValue(offset); + } + + return offset; + } + + /** {@inheritDoc} */ + protected int compareValue(EncodedValue o) { + ArrayEncodedSubValue other = (ArrayEncodedSubValue)o; + + int comp = values.length - other.values.length; + if (comp != 0) { + return comp; + } + + for (int i=0; iArrayEncodedValue 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 + */ + 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 Comparable { + /** + * Writes this EncodedValue 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 , which + * is guaranteed to be of the same type as this 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 SparseArray valueTypeIntegerMap; + + static { + /** build the valueTypeIntegerMap object */ + valueTypeIntegerMap = new SparseArray(16); + + for (ValueType valueType : ValueType.values()) { + valueTypeIntegerMap.put(valueType.value, valueType); + } + } + + /** + * The byte value for this ValueType + */ + public final byte value; + + private ValueType(byte value) { + this.value = value; + } + + /** + * Converts a byte value to the corresponding ValueType enum value, + * or null if the value isn't a valid ValueType value + * + * @param valueType the byte value to convert to a ValueType + * @return the ValueType enum value corresponding to valueType, or null + * if not a valid ValueType value + */ + public static ValueType fromByte(byte valueType) { + return valueTypeIntegerMap.get(valueType); + } +} \ No newline at end of file diff --git a/dexlib/src/main/java/org/jf/dexlib/FieldIdItem.java b/dexlib/src/main/java/org/jf/dexlib/FieldIdItem.java new file mode 100644 index 00000000..548ec994 --- /dev/null +++ b/dexlib/src/main/java/org/jf/dexlib/FieldIdItem.java @@ -0,0 +1,196 @@ +/* + * [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 FieldIdItem extends Item { + private int hashCode = 0; + + private TypeIdItem classType; + private TypeIdItem fieldType; + private StringIdItem fieldName; + + /** + * Creates a new uninitialized FieldIdItem + * @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 Item { + /** + * non-null; the file format magic number, represented as the + * low-order bytes of a string + */ + private static final String MAGIC = "dex\n035" + '\0'; + + /** size of this section, in bytes */ + private static final int HEADER_SIZE = 0x70; + + /** the endianness constants */ + private static final int LITTLE_ENDIAN = 0x12345678; + private static final int BIG_ENDIAN = 0x78562312; + + /** + * Create a new uninitialized HeaderItem + * @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 IndexedSection extends Section { + + /** + * Create a new indexed section + * @param dexFile The DexFile 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 Item implements Comparable { + /** + * The offset of this item in the dex file, or -1 if not known + */ + private int offset = -1; + + /** + * The index of this item in the containing section, or -1 if not known + */ + private int index = -1; + + /** + * The DexFile that this item is associatedr with + */ + protected final DexFile dexFile; + + /** + * The constructor that is used when reading in a DexFile + * @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 TreeMap itemTypeIntegerMap; + + /** builds the itemTypeIntegerMap object */ + static { + itemTypeIntegerMap = new TreeMap(); + + for (ItemType itemType: ItemType.values()) { + itemTypeIntegerMap.put(itemType.MapValue, itemType); + } + } + + + + /** + * value when represented in a MapItem + */ + public final int MapValue; + + /** + * name of the type + */ + public final String TypeName; + + /** + * index for this item's section + */ + public final int SectionIndex; + + /** + * the alignment for this item type + */ + public final int ItemAlignment; + /** + * Constructs an instance. + * + * @param mapValue value when represented in a MapItem + * @param sectionIndex index for this item's section + * @param itemAlignment the byte alignment required by this item + * @param typeName non-null; name of the type + */ + private ItemType(int mapValue, int sectionIndex, int itemAlignment, String typeName) { + this.MapValue = mapValue; + this.SectionIndex = sectionIndex; + this.ItemAlignment = itemAlignment; + this.TypeName = typeName; + } + + /** + * Converts an int value to the corresponding ItemType enum value, + * or null if the value isn't a valid ItemType value + * + * @param itemType the int value to convert to an ItemType + * @return the ItemType enum value corresponding to itemType, or null + * if not a valid ItemType value + */ + public static ItemType fromInt(int itemType) { + return itemTypeIntegerMap.get(itemType); + } + + /** + * Returns true if this is an indexed item, or false if its an offsetted item + * @return true if this is an indexed item, or false if its an offsetted item + */ + public boolean isIndexedItem() { + return MapValue <= 0x1000; + } +} \ No newline at end of file diff --git a/dexlib/src/main/java/org/jf/dexlib/MapItem.java b/dexlib/src/main/java/org/jf/dexlib/MapItem.java new file mode 100644 index 00000000..7265f24c --- /dev/null +++ b/dexlib/src/main/java/org/jf/dexlib/MapItem.java @@ -0,0 +1,126 @@ +/* + * [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 org.jf.dexlib.Util.Hex; + +import junit.framework.Assert; + +/** + * This item represents a map_list item from the dex specification. It contains a + * SectionInfo instance for every section in the DexFile, with the number of items + * in and offset of that section. + */ +public class MapItem extends Item { + /** + * This item is read in immediately after the HeaderItem, and the section info contained + * by this item is added to the ReadContext object, which is used when reading in the other + * sections in the dex file. + * + * This item should be placed last. It depends on the fact that the other sections + * in the file have been placed. + */ + + /** + * Create a new uninitialized MapItem + * @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; i 0); + + writeSectionInfo(out, ItemType.TYPE_HEADER_ITEM, 1, 0); + + for (Section section: dexFile.getOrderedSections()) { + writeSectionInfo(out, section.ItemType, section.getItems().size(), section.getOffset()); + } + + writeSectionInfo(out, ItemType.TYPE_MAP_LIST, 1, dexFile.MapItem.getOffset()); + } + + private void writeSectionInfo(AnnotatedOutput out, ItemType itemType, int sectionSize, int sectionOffset) { + if (out.annotates()) { + out.annotate(2, "ItemType: " + itemType); + out.annotate(2, "unused"); + out.annotate(4, "Section Size: " + sectionSize); + out.annotate(4, "Section Offset: " + Hex.u4(sectionOffset)); + } + + out.writeShort(itemType.MapValue); + out.writeShort(0); + out.writeInt(sectionSize); + out.writeInt(sectionOffset); + } + + /** {@inheritDoc} */ + public ItemType getItemType() { + return ItemType.TYPE_MAP_LIST; + } + + /** {@inheritDoc} */ + public int compareTo(MapItem o) { + return 0; + } + + /** {@inheritDoc} */ + public String getConciseIdentity() { + return "map_item"; + } +} diff --git a/dexlib/src/main/java/org/jf/dexlib/MethodIdItem.java b/dexlib/src/main/java/org/jf/dexlib/MethodIdItem.java new file mode 100644 index 00000000..3da44067 --- /dev/null +++ b/dexlib/src/main/java/org/jf/dexlib/MethodIdItem.java @@ -0,0 +1,198 @@ +/* + * [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 MethodIdItem extends Item { + private int hashCode = 0; + + private TypeIdItem classType; + private ProtoIdItem methodPrototype; + private StringIdItem methodName; + + /** + * Creates a new uninitialized MethodIdItem + * @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 OffsettedSection extends Section { + public OffsettedSection(DexFile dexFile, ItemType itemType) { + super(dexFile, itemType); + } + + public void readItems(Input in, ReadContext readContext) { + SparseArray precreatedItems = (SparseArray)readContext.getItemsByType(ItemType); + + assert precreatedItems.size() <= items.size(): "Trying to read " + items.size() + " items, but this section " + + "already contains " + precreatedItems.size() + " items."; + + int precreatedIndex = 0; + int nextPrecreatedOffset = Integer.MAX_VALUE; + + if (precreatedItems.size() > 0) { + nextPrecreatedOffset = precreatedItems.keyAt(0); + } + + for (int i = 0; i < items.size(); i++) { + assert items.get(i) == null; + + T item = null; + in.alignTo(ItemType.ItemAlignment); + int currentOffset = in.getCursor(); + + if (currentOffset == nextPrecreatedOffset) { + item = precreatedItems.valueAt(precreatedIndex++); + if (precreatedIndex < precreatedItems.size()) { + nextPrecreatedOffset = precreatedItems.keyAt(precreatedIndex); + } else { + nextPrecreatedOffset = Integer.MAX_VALUE; + } + } else if (currentOffset > nextPrecreatedOffset) { + //we passed by the next precreated item, something is wrong + throw new RuntimeException("The pre-created item at offset 0x" + Hex.u4(nextPrecreatedOffset) + + " was not read"); + } else { + item = (T)ItemFactory.makeItem(ItemType, DexFile); + } + + items.set(i, item); + item.readFrom(in, i, readContext); + } + + readContext.setItemsForSection(ItemType, items); + } +} diff --git a/dexlib/src/main/java/org/jf/dexlib/ProtoIdItem.java b/dexlib/src/main/java/org/jf/dexlib/ProtoIdItem.java new file mode 100644 index 00000000..76ad8eac --- /dev/null +++ b/dexlib/src/main/java/org/jf/dexlib/ProtoIdItem.java @@ -0,0 +1,203 @@ +/* + * [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 ProtoIdItem extends Item { + private int hashCode = 0; + + private StringIdItem shortyDescriptor; + private TypeIdItem returnType; + private TypeListItem parameters; + + /** + * Creates a new uninitialized ProtoIdItem + * @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 SparseArray typeListItems = new SparseArray(0); + private SparseArray annotationSetRefLists = new SparseArray(0); + private SparseArray annotationSetItems = new SparseArray(0); + private SparseArray classDataItems = new SparseArray(0); + private SparseArray codeItems = new SparseArray(0); + private SparseArray stringDataItems = new SparseArray(0); + private SparseArray debugInfoItems = new SparseArray(0); + private SparseArray annotationItems = new SparseArray(0); + private SparseArray encodedArrayItems = new SparseArray(); + private SparseArray annotationDirectoryItems = new SparseArray(); + + private SparseArray[] itemsByType = new SparseArray[] { + null, //string_id_item + null, //type_id_item + null, //proto_id_item + null, //field_id_item + null, //method_id_item + null, //class_def_item + typeListItems, + annotationSetRefLists, + annotationSetItems, + classDataItems, + codeItems, + stringDataItems, + debugInfoItems, + annotationItems, + encodedArrayItems, + annotationDirectoryItems, + null, //map_list + null //header_item + }; + + + /** + * The section sizes that are passed in while reading HeaderItem/MapItem, via the + * addSection method. + */ + private int[] sectionSizes = new int[18]; + + /** + * The section offsets that are passed in while reading MapItem/HeaderItem, via the + * addSection method. + */ + private int[] sectionOffsets = new int[18]; + + /** + * Creates a new ReadContext instance. + * @param dexFile The dex file that is being read in + */ + public ReadContext(DexFile dexFile) { + this.dexFile = dexFile; + + for (int i=0; i<18; i++) { + sectionSizes[i] = -1; + sectionOffsets[i] = -1; + } + } + + /** + * Returns a SparseArray containing the items of the given type + * that have been pre-created while reading in other sections. + * + * If the given ItemType isn't an offsetted item, this method will + * return null + * @param itemType The type of item to get + * @return a SparseArray containing the items of the given type + * that have been pre-created while reading in other sections, or + * null if the ItemType isn't an offsetted item + */ + public SparseArray getItemsByType(ItemType itemType) { + return itemsByType[itemType.SectionIndex]; + } + + /** + * Gets or creates an offsetted item of the specified type for the + * given offset. Multiple calls to this method with the same itemType + * and offset will return the same item. + * + * It should not be assumed that the item that is returned will be + * initialized. It is only guaranteed that the item will be read in + * and initiliazed after the entire dex file has been read in. + * + * Note that it *is* guaranteed that this exact item will be added to + * its corresponding section and read in. In other words, when the + * corresponding section is being read in, it will use any items for + * that have been "pre-created" by this method, and only create + * new items for offsets that haven't been pre-created yet. + * + * @param itemType The type of item to get + * @param offset The offset of the StringDataItem + * @return a StringDataItem for the given offset + */ + public Item getOffsettedItemByOffset(ItemType itemType, int offset) { + assert !itemType.isIndexedItem(); + + if (offset == 0) { + return null; + } + + SparseArray sa = itemsByType[itemType.SectionIndex]; + Item item = sa.get(offset); + if (item == null) { + item = ItemFactory.makeItem(itemType, dexFile); + sa.put(offset, item); + } + return item; + } + + /** + * Adds the size and offset information for the given offset + * @param itemType the item type of the section + * @param sectionSize the size of the section + * @param sectionOffset the offset of the section + */ + public void addSection(final ItemType itemType, int sectionSize, int sectionOffset) { + if (!itemType.isIndexedItem()) { + itemsByType[itemType.SectionIndex].ensureCapacity(sectionSize); + } + int storedSectionSize = sectionSizes[itemType.SectionIndex]; + if (storedSectionSize == -1) { + sectionSizes[itemType.SectionIndex] = sectionSize; + } else { + if (storedSectionSize != sectionSize) { + throw new RuntimeException("The section size in the header and map for item type " + + itemType + " do not match"); + } + } + + int storedSectionOffset = sectionOffsets[itemType.SectionIndex]; + if (storedSectionOffset == -1) { + sectionOffsets[itemType.SectionIndex] = sectionOffset; + } else { + if (storedSectionOffset != sectionOffset) { + throw new RuntimeException("The section offset in the header and map for item type " + + itemType + " do not match"); + } + } + } + + + /** + * Sets the items for the specified section. This should be called by an offsetted section + * after it is finished reading in all its items. + * @param itemType the item type of the section. This must be an offsetted item type + * @param items the full list of items in the section, ordered by offset + */ + public void setItemsForSection(ItemType itemType, List items) { + assert !itemType.isIndexedItem(); + + SparseArray sa = itemsByType[itemType.SectionIndex]; + + sa.clear(); + sa.ensureCapacity(items.size()); + for (Item item: items) { + sa.append(item.getOffset(), item); + } + } + + /** + * @param itemType the item type of the section + * @return the size of the given section as it was read in from the map item + */ + public int getSectionSize(ItemType itemType) { + return sectionSizes[itemType.SectionIndex]; + } + + /** + * @param itemType the item type of the section + * @return the offset of the given section as it was read in from the map item + */ + public int getSectionOffset(ItemType itemType) { + return sectionOffsets[itemType.SectionIndex]; + } +} diff --git a/dexlib/src/main/java/org/jf/dexlib/Section.java b/dexlib/src/main/java/org/jf/dexlib/Section.java new file mode 100644 index 00000000..d84c6945 --- /dev/null +++ b/dexlib/src/main/java/org/jf/dexlib/Section.java @@ -0,0 +1,210 @@ +/* + * [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; + +import java.util.*; + +import junit.framework.Assert; + +public abstract class Section { + /** + * A list of the items that this section contains. + * If the section has been placed, this list should be in the order that the items + * will written to the dex file + */ + protected final ArrayList items; + + /** + * A HashMap of the items in this section. This is used when interning items, to determine + * if this section already has an item equivalent to the one that is being interned. + * Both the key and the value should be the same object + */ + protected HashMap uniqueItems = null; + + /** + * The offset of this section within the DexFile + */ + 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 ArrayList(); + this.ItemType = itemType; + } + + /** + * Finalize the location of all items, and place them starting at the given offset + * @param offset The offset where this section should be placed + * @return the offset of the byte immediate after the last item in this section + */ + protected int placeAt(int offset) { + if (items.size() > 0) { + offset = AlignmentUtils.alignOffset(offset, ItemType.ItemAlignment); + this.offset = offset; + + for (int i=0; i < items.size(); i++) { + T item = items.get(i); + Assert.assertTrue("This section contains a null item", item != null); + offset = AlignmentUtils.alignOffset(offset, ItemType.ItemAlignment); + offset = item.placeAt(offset, i); + } + } else { + this.offset = -1; + } + + return offset; + } + + /** + * Write the items to the given AnnotatedOutput + * @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 List getItems() { + return Collections.unmodifiableList(items); + } + + /** + * This method checks if an item that is equivalent to the given item has already been added. If found, + * it returns that item. If not found, it adds the given item to this section and returns it. + * @param item the item to intern + * @return An item from this section that is equivalent to the given item. It may or may not be the same + * as the item passed to this method. + */ + protected T intern(T item) { + T internedItem = getInternedItem(item); + if (internedItem == null) { + uniqueItems.put(item, item); + return item; + } + return internedItem; + } + + /** + * Returns the interned item that is equivalent to the given item, or null + * @param item the item to check + * @return the interned item that is equivalent to the given item, or null + */ + protected T getInternedItem(T item) { + if (uniqueItems == null) { + buildInternedItemMap(); + } + return uniqueItems.get(item); + } + + /** + * Builds the interned item map from the items that are in this section + */ + private void buildInternedItemMap() { + uniqueItems = new HashMap(); + for (T item: items) { + Assert.assertTrue("item shouldn't be null here", item != null); + uniqueItems.put(item, item); + } + } + + /** + * Sorts the items in the section + */ + protected void sortSection() { + Collections.sort(items); + } +} \ No newline at end of file diff --git a/dexlib/src/main/java/org/jf/dexlib/StringDataItem.java b/dexlib/src/main/java/org/jf/dexlib/StringDataItem.java new file mode 100644 index 00000000..d0861d15 --- /dev/null +++ b/dexlib/src/main/java/org/jf/dexlib/StringDataItem.java @@ -0,0 +1,149 @@ +/* + * [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 StringDataItem extends Item { + private int hashCode = 0; + + private String stringValue; + + /** + * Creates a new uninitialized StringDataItem + * @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 Item { + private StringDataItem stringDataItem; + + /** + * Creates a new uninitialized StringIdItem + * @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 Item { + private StringIdItem typeDescriptor; + + /** + * Creates a new uninitialized TypeIdItem + * @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 Item { + private int hashCode = 0; + + private TypeIdItem[] typeList; + + /** + * Creates a new uninitialized TypeListItem + * @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, List typeList) { + super(dexFile); + + this.typeList = new TypeIdItem[typeList.size()]; + typeList.toArray(this.typeList); + } + + /** + * Returns a TypeListItem 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, List typeList) { + TypeListItem typeListItem = new TypeListItem(dexFile, typeList); + return dexFile.TypeListsSection.intern(typeListItem); + } + + /** {@inheritDoc} */ + protected void readItem(Input in, ReadContext readContext) { + int size = in.readInt(); + typeList = new TypeIdItem[size]; + for (int i=0; i otherSize) { + return 1; + } else { + return 0; + } + } + + /** + * @return the number of registers required for this TypeListItem + */ + 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; i accessFlagsByName; + + static { + accessFlagsByName = new HashMap(); + for (AccessFlags accessFlag: AccessFlags.values()) { + accessFlagsByName.put(accessFlag.accessFlagName, accessFlag); + } + } + + private AccessFlags(int value, String accessFlagName, boolean validForClass, boolean validForMethod, + boolean validForField) { + this.value = value; + this.accessFlagName = accessFlagName; + this.validForClass = validForClass; + this.validForMethod = validForMethod; + this.validForField = validForField; + } + + public static List getAccessFlagsForClass(int accessFlagValue) { + ArrayList accessFlags = new ArrayList(); + + for (AccessFlags accessFlag: AccessFlags.values()) { + if (accessFlag.validForClass && (accessFlagValue & accessFlag.value) != 0) { + accessFlags.add(accessFlag); + } + } + return accessFlags; + } + + public static List getAccessFlagsForMethod(int accessFlagValue) { + ArrayList accessFlags = new ArrayList(); + + for (AccessFlags accessFlag: AccessFlags.values()) { + if (accessFlag.validForMethod && (accessFlagValue & accessFlag.value) != 0) { + accessFlags.add(accessFlag); + } + } + return accessFlags; + } + + public static List getAccessFlagsForField(int accessFlagValue) { + ArrayList accessFlags = new ArrayList(); + + for (AccessFlags accessFlag: AccessFlags.values()) { + if (accessFlag.validForField && (accessFlagValue & accessFlag.value) != 0) { + accessFlags.add(accessFlag); + } + } + return accessFlags; + } + + public static AccessFlags getAccessFlag(String accessFlag) { + return accessFlagsByName.get(accessFlag); + } + + public int getValue() { + return value; + } + + public String toString() { + return accessFlagName; + } +} diff --git a/dexlib/src/main/java/org/jf/dexlib/Util/AlignmentUtils.java b/dexlib/src/main/java/org/jf/dexlib/Util/AlignmentUtils.java new file mode 100644 index 00000000..c5b54dd0 --- /dev/null +++ b/dexlib/src/main/java/org/jf/dexlib/Util/AlignmentUtils.java @@ -0,0 +1,39 @@ +/* + * [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 junit.framework.Assert; + +public abstract class AlignmentUtils { + public static int alignOffset(int offset, int alignment) { + int mask = alignment - 1; + Assert.assertFalse("bogus alignment", (alignment < 0) || ((mask & alignment) != 0)); + return (offset + mask) & ~mask; + } +} diff --git a/dexlib/src/main/java/org/jf/dexlib/Util/AnnotatedOutput.java b/dexlib/src/main/java/org/jf/dexlib/Util/AnnotatedOutput.java new file mode 100644 index 00000000..a34e2098 --- /dev/null +++ b/dexlib/src/main/java/org/jf/dexlib/Util/AnnotatedOutput.java @@ -0,0 +1,97 @@ +/* + * [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 org.jf.dexlib.Util.Output; + +/** + * Interface for a binary output destination that may be augmented + * with textual annotations. + */ +public interface AnnotatedOutput + extends Output { + /** + * Get whether this instance will actually keep annotations. + * + * @return true 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(){ + public int compare(element a, element b) { + return a.first.compareTo(b.first); + } + }); + + for (int i=0; ibyte[], which provides read-only access and + * can "reveal" a partial slice of the underlying array. + * + * Note: Multibyte accessors all use big-endian order. + */ +public final class ByteArray { + /** non-null; underlying array */ + private final byte[] bytes; + + /** >= 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 indexth 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 indexth 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 indexth 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(); + } +}