FileDocCategorySizeDatePackage
DexFile.javaAPI DocAndroid 1.5 API20023Wed May 06 22:41:02 BST 2009com.android.dx.dex.file

DexFile

public final class DexFile extends Object
Representation of an entire .dex (Dalvik EXecutable) file, which itself consists of a set of Dalvik classes.

Fields Summary
private final MixedItemSection
wordData
non-null; word data section
private final MixedItemSection
typeLists
non-null; type lists section. This is word data, but separating it from {@link #wordData} helps break what would otherwise be a circular dependency between the that and {@link #protoIds}.
private final MixedItemSection
map
non-null; map section. The map needs to be in a section by itself for the self-reference mechanics to work in a reasonably straightforward way. See {@link MapItem#addMap} for more detail.
private final MixedItemSection
stringData
non-null; string data section
private final StringIdsSection
stringIds
non-null; string identifiers section
private final TypeIdsSection
typeIds
non-null; type identifiers section
private final ProtoIdsSection
protoIds
non-null; prototype identifiers section
private final FieldIdsSection
fieldIds
non-null; field identifiers section
private final MethodIdsSection
methodIds
non-null; method identifiers section
private final ClassDefsSection
classDefs
non-null; class definitions section
private final MixedItemSection
classData
non-null; class data section
private final MixedItemSection
byteData
non-null; byte data section
private final HeaderSection
header
non-null; file header
private final Section[]
sections
non-null; array of sections in the order they will appear in the final output file
private int
fileSize
>= -1; total file size or -1 if unknown
private int
dumpWidth
>= 40; maximum width of the file dump
Constructors Summary
public DexFile()
Constructs an instance. It is initially empty.

        header = new HeaderSection(this);
        typeLists = new MixedItemSection(null, this, 4, SortType.NONE);
        wordData = new MixedItemSection("word_data", this, 4, SortType.TYPE);
        stringData =
            new MixedItemSection("string_data", this, 1, SortType.INSTANCE);
        classData = new MixedItemSection(null, this, 1, SortType.NONE);
        byteData = new MixedItemSection("byte_data", this, 1, SortType.TYPE);
        stringIds = new StringIdsSection(this);
        typeIds = new TypeIdsSection(this);
        protoIds = new ProtoIdsSection(this);
        fieldIds = new FieldIdsSection(this);
        methodIds = new MethodIdsSection(this);
        classDefs = new ClassDefsSection(this);
        map = new MixedItemSection("map", this, 4, SortType.NONE);

        /* 
         * This is the list of sections in the order they appear in
         * the final output.
         */
        sections = new Section[] {
            header, stringIds, typeIds, protoIds, fieldIds, methodIds,
            classDefs, wordData, typeLists, stringData, byteData, 
            classData, map };
        
        fileSize = -1;
        dumpWidth = 79;
    
Methods Summary
public voidadd(ClassDefItem clazz)
Adds a class to this instance. It is illegal to attempt to add more than one class with the same name.

param
clazz non-null; the class to add

        classDefs.add(clazz);
    
private static voidcalcChecksum(byte[] bytes)
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

        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);
    
private static voidcalcSignature(byte[] bytes)
Calculates the signature for the .dex file in the given array, and modify the array to contain it.

param
bytes non-null; the bytes of the file

        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);
        }
    
IndexedItemfindItemOrNull(com.android.dx.rop.cst.Constant cst)
Gets the {@link IndexedItem} corresponding to the given constant, if it is a constant that has such a correspondence, or return null if it isn't such a constant. This will throw an exception if the given constant should have been found but wasn't.

param
cst non-null; the constant to look up
return
null-ok; its corresponding item, if it has a corresponding item, or null if it's not that sort of constant

        IndexedItem item;

        if (cst instanceof CstString) {
            return stringIds.get(cst);
        } else if (cst instanceof CstType) {
            return typeIds.get(cst);
        } else if (cst instanceof CstBaseMethodRef) {
            return methodIds.get(cst);
        } else if (cst instanceof CstFieldRef) {
            return fieldIds.get(cst);
        } else {
            return null;
        }
    
MixedItemSectiongetByteData()
Gets the byte data section.

This is package-scope in order to allow the various {@link Item} instances to add items to the instance.

return
non-null; the byte data section

        return byteData;
    
MixedItemSectiongetClassData()
Gets the class data section.

This is package-scope in order to allow the various {@link Item} instances to add items to the instance.

return
non-null; the class data section

        return classData;
    
ClassDefsSectiongetClassDefs()
Gets the class definitions section.

This is package-scope in order to allow the various {@link Item} instances to add items to the instance.

return
non-null; the class definitions section

        return classDefs;
    
public ClassDefItemgetClassOrNull(java.lang.String name)
Gets the class definition with the given name, if any.

param
name non-null; the class name to look for
return
null-ok; the class with the given name, or null if there is no such class

        try {
            Type type = Type.internClassName(name);
            return (ClassDefItem) classDefs.get(new CstType(type));
        } catch (IllegalArgumentException ex) {
            // Translate exception, per contract.
            return null;
        }
    
FieldIdsSectiongetFieldIds()
Gets the field identifiers section.

This is package-scope in order to allow the various {@link Item} instances to add items to the instance.

return
non-null; the field identifiers section

        return fieldIds;
    
intgetFileSize()
Gets the total file size, if known.

This is package-scope in order to allow the {@link HeaderSection} to set itself up properly.

return
>= 0; the total file size
throws
RuntimeException thrown if the file size is not yet known

        if (fileSize < 0) {
            throw new RuntimeException("file size not yet known");
        }

        return fileSize;
    
SectiongetFirstDataSection()
Gets the first section of the file that is to be considered part of the data section.

This is package-scope in order to allow the header section to query it.

return
non-null; the section

        return wordData;
    
SectiongetLastDataSection()
Gets the last section of the file that is to be considered part of the data section.

This is package-scope in order to allow the header section to query it.

return
non-null; the section

        return map;
    
MixedItemSectiongetMap()
Gets the map section.

This is package-scope in order to allow the header section to query it.

return
non-null; the map section

        return map;
    
MethodIdsSectiongetMethodIds()
Gets the method identifiers section.

This is package-scope in order to allow the various {@link Item} instances to add items to the instance.

return
non-null; the method identifiers section

        return methodIds;
    
ProtoIdsSectiongetProtoIds()
Gets the prototype identifiers section.

This is package-scope in order to allow the various {@link Item} instances to add items to the instance.

return
non-null; the prototype identifiers section

        return protoIds;
    
public StatisticsgetStatistics()
Generates and returns statistics for all the items in the file.

return
non-null; the statistics

        Statistics stats = new Statistics();

        for (Section s : sections) {
            stats.addAll(s);
        }

        return stats;
    
MixedItemSectiongetStringData()
Gets the string data section.

This is package-scope in order to allow the various {@link Item} instances to add items to the instance.

return
non-null; the string data section

        return stringData;
    
StringIdsSectiongetStringIds()
Gets the string identifiers section.

This is package-scope in order to allow the various {@link Item} instances to add items to the instance.

return
non-null; the string identifiers section

        return stringIds;
    
TypeIdsSectiongetTypeIds()
Gets the type identifiers section.

This is package-scope in order to allow the various {@link Item} instances to add items to the instance.

return
non-null; the class identifiers section

        return typeIds;
    
MixedItemSectiongetTypeLists()
Gets the type lists section.

This is package-scope in order to allow the various {@link Item} instances to add items to the instance.

return
non-null; the word data section

        return typeLists;
    
MixedItemSectiongetWordData()
Gets the word data section.

This is package-scope in order to allow the various {@link Item} instances to add items to the instance.

return
non-null; the word data section

        return wordData;
    
voidinternIfAppropriate(com.android.dx.rop.cst.Constant cst)
Interns the given constant in the appropriate section of this instance, or do nothing if the given constant isn't the sort that should be interned.

param
cst non-null; constant to possibly intern

        if (cst instanceof CstString) {
            stringIds.intern((CstString) cst);
        } else if (cst instanceof CstUtf8) {
            stringIds.intern((CstUtf8) cst);
        } else if (cst instanceof CstType) {
            typeIds.intern((CstType) cst);
        } else if (cst instanceof CstBaseMethodRef) {
            methodIds.intern((CstBaseMethodRef) cst);
        } else if (cst instanceof CstFieldRef) {
            fieldIds.intern((CstFieldRef) cst);
        } else if (cst instanceof CstEnumRef) {
            fieldIds.intern(((CstEnumRef) cst).getFieldRef());
        } else if (cst == null) {
            throw new NullPointerException("cst == null");
        }
    
public voidsetDumpWidth(int dumpWidth)
Sets the maximum width of the human-oriented dump of the instance.

param
dumpWidth >= 40; the width

        if (dumpWidth < 40) {
            throw new IllegalArgumentException("dumpWidth < 40");
        }

        this.dumpWidth = dumpWidth;
    
public byte[]toDex(java.io.Writer humanOut, boolean verbose)
Returns the contents of this instance as a .dex file, in byte[] form.

param
humanOut null-ok; where to write human-oriented output to
param
verbose whether to be verbose when writing human-oriented output
return
non-null; a .dex file for this instance

        boolean annotate = (humanOut != null);
        ByteArrayAnnotatedOutput result = toDex0(annotate, verbose);

        if (annotate) {
            result.writeAnnotationsTo(humanOut);
        }

        return result.getArray();
    
private com.android.dx.util.ByteArrayAnnotatedOutputtoDex0(boolean annotate, boolean verbose)
Returns the contents of this instance as a .dex file, in a {@link ByteArrayAnnotatedOutput} instance.

param
annotate whether or not to keep annotations
param
verbose if annotating, whether to be verbose
return
non-null; a .dex file for this instance

        /*
         * The following is ordered so that the prepare() calls which
         * add items happen before the calls to the sections that get
         * added to.
         */

        classDefs.prepare();
        classData.prepare();
        wordData.prepare();
        byteData.prepare();
        methodIds.prepare();
        fieldIds.prepare();
        protoIds.prepare();
        typeLists.prepare();
        typeIds.prepare();
        stringIds.prepare();
        stringData.prepare();
        header.prepare();

        // Place the sections within the file.

        int count = sections.length;
        int offset = 0;

        for (int i = 0; i < count; i++) {
            Section one = sections[i];
            int placedAt = one.setFileOffset(offset);
            if (placedAt < offset) {
                throw new RuntimeException("bogus placement for section " + i);
            }

            try {
                if (one == map) {
                    /*
                     * Inform the map of all the sections, and add it
                     * to the file. This can only be done after all
                     * the other items have been sorted and placed.
                     */
                    MapItem.addMap(sections, map);
                    map.prepare();
                }

                if (one instanceof MixedItemSection) {
                    /*
                     * Place the items of a MixedItemSection that just
                     * got placed.
                     */
                    ((MixedItemSection) one).placeItems();
                }
            
                offset = placedAt + one.writeSize();
            } catch (RuntimeException ex) {
                throw ExceptionWithContext.withContext(ex,
                        "...while writing section " + i);
            }
        }

        // Write out all the sections.

        fileSize = offset;
        byte[] barr = new byte[fileSize];
        ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput(barr);

        if (annotate) {
            out.enableAnnotations(dumpWidth, verbose);
        }

        for (int i = 0; i < count; i++) {
            try {
                Section one = sections[i];
                int zeroCount = one.getFileOffset() - out.getCursor();
                if (zeroCount < 0) {
                    throw new ExceptionWithContext("excess write of " +
                            (-zeroCount));
                }
                out.writeZeroes(one.getFileOffset() - out.getCursor());
                one.writeTo(out);
            } catch (RuntimeException ex) {
                ExceptionWithContext ec;
                if (ex instanceof ExceptionWithContext) {
                    ec = (ExceptionWithContext) ex;
                } else {
                    ec = new ExceptionWithContext(ex);
                }
                ec.addContext("...while writing section " + i);
                throw ec;
            }
        }

        if (out.getCursor() != fileSize) {
            throw new RuntimeException("foreshortened write");
        }

        // Perform final bookkeeping.
        
        calcSignature(barr);
        calcChecksum(barr);

        if (annotate) {
            wordData.writeIndexAnnotation(out, ItemType.TYPE_CODE_ITEM,
                    "\nmethod code index:\n\n");
            getStatistics().writeAnnotation(out);
            out.finishAnnotating();
        }

        return out;
    
public voidwriteTo(java.io.OutputStream out, java.io.Writer humanOut, boolean verbose)
Writes the contents of this instance as either a binary or a human-readable form, or both.

param
out null-ok; where to write to
param
humanOut null-ok; where to write human-oriented output to
param
verbose whether to be verbose when writing human-oriented output

        boolean annotate = (humanOut != null);
        ByteArrayAnnotatedOutput result = toDex0(annotate, verbose);

        if (out != null) {
            out.write(result.getArray());
        }

        if (annotate) {
            result.writeAnnotationsTo(humanOut);
        }