Fields Summary |
---|
private final MixedItemSection | wordDatanon-null; word data section |
private final MixedItemSection | typeListsnon-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 | mapnon-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 | stringDatanon-null; string data section |
private final StringIdsSection | stringIdsnon-null; string identifiers section |
private final TypeIdsSection | typeIdsnon-null; type identifiers section |
private final ProtoIdsSection | protoIdsnon-null; prototype identifiers section |
private final FieldIdsSection | fieldIdsnon-null; field identifiers section |
private final MethodIdsSection | methodIdsnon-null; method identifiers section |
private final ClassDefsSection | classDefsnon-null; class definitions section |
private final MixedItemSection | classDatanon-null; class data section |
private final MixedItemSection | byteDatanon-null; byte data section |
private final HeaderSection | headernon-null; file header |
private final Section[] | sectionsnon-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 void | add(ClassDefItem clazz)Adds a class to this instance. It is illegal to attempt to add more
than one class with the same name.
classDefs.add(clazz);
|
private static void | calcChecksum(byte[] bytes)Calculates the checksum for the .dex file in the
given array, and modify the array to contain it.
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 void | calcSignature(byte[] bytes)Calculates the signature for the .dex file in the
given array, and modify the array to contain it.
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);
}
|
IndexedItem | findItemOrNull(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.
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;
}
|
MixedItemSection | getByteData()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 byteData;
|
MixedItemSection | getClassData()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 classData;
|
ClassDefsSection | getClassDefs()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 classDefs;
|
public ClassDefItem | getClassOrNull(java.lang.String name)Gets the class definition with the given name, if any.
try {
Type type = Type.internClassName(name);
return (ClassDefItem) classDefs.get(new CstType(type));
} catch (IllegalArgumentException ex) {
// Translate exception, per contract.
return null;
}
|
FieldIdsSection | getFieldIds()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 fieldIds;
|
int | getFileSize()Gets the total file size, if known.
This is package-scope in order to allow
the {@link HeaderSection} to set itself up properly.
if (fileSize < 0) {
throw new RuntimeException("file size not yet known");
}
return fileSize;
|
Section | getFirstDataSection()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 wordData;
|
Section | getLastDataSection()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 map;
|
MixedItemSection | getMap()Gets the map section.
This is package-scope in order to allow the header section
to query it.
return map;
|
MethodIdsSection | getMethodIds()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 methodIds;
|
ProtoIdsSection | getProtoIds()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 protoIds;
|
public Statistics | getStatistics()Generates and returns statistics for all the items in the file.
Statistics stats = new Statistics();
for (Section s : sections) {
stats.addAll(s);
}
return stats;
|
MixedItemSection | getStringData()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 stringData;
|
StringIdsSection | getStringIds()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 stringIds;
|
TypeIdsSection | getTypeIds()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 typeIds;
|
MixedItemSection | getTypeLists()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 typeLists;
|
MixedItemSection | getWordData()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 wordData;
|
void | internIfAppropriate(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.
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 void | setDumpWidth(int dumpWidth)Sets the maximum width of the human-oriented dump of the instance.
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.
boolean annotate = (humanOut != null);
ByteArrayAnnotatedOutput result = toDex0(annotate, verbose);
if (annotate) {
result.writeAnnotationsTo(humanOut);
}
return result.getArray();
|
private com.android.dx.util.ByteArrayAnnotatedOutput | toDex0(boolean annotate, boolean verbose)Returns the contents of this instance as a .dex file,
in a {@link ByteArrayAnnotatedOutput} 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 void | writeTo(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.
boolean annotate = (humanOut != null);
ByteArrayAnnotatedOutput result = toDex0(annotate, verbose);
if (out != null) {
out.write(result.getArray());
}
if (annotate) {
result.writeAnnotationsTo(humanOut);
}
|