com.android.dex.Dex Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of builder Show documentation
Show all versions of builder Show documentation
Library to build Android applications.
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.dex;
import com.android.dex.Code.CatchHandler;
import com.android.dex.Code.Try;
import com.android.dex.util.ByteInput;
import com.android.dex.util.ByteOutput;
import com.android.dex.util.FileUtils;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UTFDataFormatException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.AbstractList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.RandomAccess;
import java.util.zip.Adler32;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
/**
* The bytes of a dex file in memory for reading and writing. All int offsets
* are unsigned.
*/
public final class Dex {
private static final int CHECKSUM_OFFSET = 8;
private static final int CHECKSUM_SIZE = 4;
private static final int SIGNATURE_OFFSET = CHECKSUM_OFFSET + CHECKSUM_SIZE;
private static final int SIGNATURE_SIZE = 20;
// Provided as a convenience to avoid a memory allocation to benefit Dalvik.
// Note: libcore.util.EmptyArray cannot be accessed when this code isn't run on Dalvik.
static final short[] EMPTY_SHORT_ARRAY = new short[0];
private ByteBuffer data;
private final TableOfContents tableOfContents = new TableOfContents();
private int nextSectionStart = 0;
private final StringTable strings = new StringTable();
private final TypeIndexToDescriptorIndexTable typeIds = new TypeIndexToDescriptorIndexTable();
private final TypeIndexToDescriptorTable typeNames = new TypeIndexToDescriptorTable();
private final ProtoIdTable protoIds = new ProtoIdTable();
private final FieldIdTable fieldIds = new FieldIdTable();
private final MethodIdTable methodIds = new MethodIdTable();
/**
* Creates a new dex that reads from {@code data}. It is an error to modify
* {@code data} after using it to create a dex buffer.
*/
public Dex(byte[] data) throws IOException {
this(ByteBuffer.wrap(data));
}
private Dex(ByteBuffer data) throws IOException {
this.data = data;
this.data.order(ByteOrder.LITTLE_ENDIAN);
this.tableOfContents.readFrom(this);
}
/**
* Creates a new empty dex of the specified size.
*/
public Dex(int byteCount) throws IOException {
this.data = ByteBuffer.wrap(new byte[byteCount]);
this.data.order(ByteOrder.LITTLE_ENDIAN);
}
/**
* Creates a new dex buffer of the dex in {@code in}, and closes {@code in}.
*/
public Dex(InputStream in) throws IOException {
loadFrom(in);
}
/**
* Creates a new dex buffer from the dex file {@code file}.
*/
public Dex(File file) throws IOException {
if (FileUtils.hasArchiveSuffix(file.getName())) {
ZipFile zipFile = new ZipFile(file);
ZipEntry entry = zipFile.getEntry(DexFormat.DEX_IN_JAR_NAME);
if (entry != null) {
loadFrom(zipFile.getInputStream(entry));
zipFile.close();
} else {
throw new DexException("Expected " + DexFormat.DEX_IN_JAR_NAME + " in " + file);
}
} else if (file.getName().endsWith(".dex")) {
loadFrom(new FileInputStream(file));
} else {
throw new DexException("unknown output extension: " + file);
}
}
/**
* Creates a new dex from the contents of {@code bytes}. This API supports
* both {@code .dex} and {@code .odex} input. Calling this constructor
* transfers ownership of {@code bytes} to the returned Dex: it is an error
* to access the buffer after calling this method.
*/
public static Dex create(ByteBuffer data) throws IOException {
data.order(ByteOrder.LITTLE_ENDIAN);
// if it's an .odex file, set position and limit to the .dex section
if (data.get(0) == 'd'
&& data.get(1) == 'e'
&& data.get(2) == 'y'
&& data.get(3) == '\n') {
data.position(8);
int offset = data.getInt();
int length = data.getInt();
data.position(offset);
data.limit(offset + length);
data = data.slice();
}
return new Dex(data);
}
private void loadFrom(InputStream in) throws IOException {
ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
byte[] buffer = new byte[8192];
int count;
while ((count = in.read(buffer)) != -1) {
bytesOut.write(buffer, 0, count);
}
in.close();
this.data = ByteBuffer.wrap(bytesOut.toByteArray());
this.data.order(ByteOrder.LITTLE_ENDIAN);
this.tableOfContents.readFrom(this);
}
private static void checkBounds(int index, int length) {
if (index < 0 || index >= length) {
throw new IndexOutOfBoundsException("index:" + index + ", length=" + length);
}
}
public void writeTo(OutputStream out) throws IOException {
byte[] buffer = new byte[8192];
ByteBuffer data = this.data.duplicate(); // positioned ByteBuffers aren't thread safe
data.clear();
while (data.hasRemaining()) {
int count = Math.min(buffer.length, data.remaining());
data.get(buffer, 0, count);
out.write(buffer, 0, count);
}
}
public void writeTo(File dexOut) throws IOException {
OutputStream out = new FileOutputStream(dexOut);
writeTo(out);
out.close();
}
public TableOfContents getTableOfContents() {
return tableOfContents;
}
public Section open(int position) {
if (position < 0 || position >= data.capacity()) {
throw new IllegalArgumentException("position=" + position
+ " length=" + data.capacity());
}
ByteBuffer sectionData = data.duplicate();
sectionData.order(ByteOrder.LITTLE_ENDIAN); // necessary?
sectionData.position(position);
sectionData.limit(data.capacity());
return new Section("section", sectionData);
}
public Section appendSection(int maxByteCount, String name) {
if ((maxByteCount & 3) != 0) {
throw new IllegalStateException("Not four byte aligned!");
}
int limit = nextSectionStart + maxByteCount;
ByteBuffer sectionData = data.duplicate();
sectionData.order(ByteOrder.LITTLE_ENDIAN); // necessary?
sectionData.position(nextSectionStart);
sectionData.limit(limit);
Section result = new Section(name, sectionData);
nextSectionStart = limit;
return result;
}
public int getLength() {
return data.capacity();
}
public int getNextSectionStart() {
return nextSectionStart;
}
/**
* Returns a copy of the the bytes of this dex.
*/
public byte[] getBytes() {
ByteBuffer data = this.data.duplicate(); // positioned ByteBuffers aren't thread safe
byte[] result = new byte[data.capacity()];
data.position(0);
data.get(result);
return result;
}
public List strings() {
return strings;
}
public List typeIds() {
return typeIds;
}
public List typeNames() {
return typeNames;
}
public List protoIds() {
return protoIds;
}
public List fieldIds() {
return fieldIds;
}
public List methodIds() {
return methodIds;
}
public Iterable classDefs() {
return new ClassDefIterable();
}
public TypeList readTypeList(int offset) {
if (offset == 0) {
return TypeList.EMPTY;
}
return open(offset).readTypeList();
}
public ClassData readClassData(ClassDef classDef) {
int offset = classDef.getClassDataOffset();
if (offset == 0) {
throw new IllegalArgumentException("offset == 0");
}
return open(offset).readClassData();
}
public Code readCode(ClassData.Method method) {
int offset = method.getCodeOffset();
if (offset == 0) {
throw new IllegalArgumentException("offset == 0");
}
return open(offset).readCode();
}
/**
* Returns the signature of all but the first 32 bytes of this dex. The
* first 32 bytes of dex files are not specified to be included in the
* signature.
*/
public byte[] computeSignature() throws IOException {
MessageDigest digest;
try {
digest = MessageDigest.getInstance("SHA-1");
} catch (NoSuchAlgorithmException e) {
throw new AssertionError();
}
byte[] buffer = new byte[8192];
ByteBuffer data = this.data.duplicate(); // positioned ByteBuffers aren't thread safe
data.limit(data.capacity());
data.position(SIGNATURE_OFFSET + SIGNATURE_SIZE);
while (data.hasRemaining()) {
int count = Math.min(buffer.length, data.remaining());
data.get(buffer, 0, count);
digest.update(buffer, 0, count);
}
return digest.digest();
}
/**
* Returns the checksum of all but the first 12 bytes of {@code dex}.
*/
public int computeChecksum() throws IOException {
Adler32 adler32 = new Adler32();
byte[] buffer = new byte[8192];
ByteBuffer data = this.data.duplicate(); // positioned ByteBuffers aren't thread safe
data.limit(data.capacity());
data.position(CHECKSUM_OFFSET + CHECKSUM_SIZE);
while (data.hasRemaining()) {
int count = Math.min(buffer.length, data.remaining());
data.get(buffer, 0, count);
adler32.update(buffer, 0, count);
}
return (int) adler32.getValue();
}
/**
* Generates the signature and checksum of the dex file {@code out} and
* writes them to the file.
*/
public void writeHashes() throws IOException {
open(SIGNATURE_OFFSET).write(computeSignature());
open(CHECKSUM_OFFSET).writeInt(computeChecksum());
}
/**
* Look up a field id name index from a field index. Cheaper than:
* {@code fieldIds().get(fieldDexIndex).getNameIndex();}
*/
public int nameIndexFromFieldIndex(int fieldIndex) {
checkBounds(fieldIndex, tableOfContents.fieldIds.size);
int position = tableOfContents.fieldIds.off + (SizeOf.MEMBER_ID_ITEM * fieldIndex);
position += SizeOf.USHORT; // declaringClassIndex
position += SizeOf.USHORT; // typeIndex
return data.getInt(position); // nameIndex
}
public int findStringIndex(String s) {
return Collections.binarySearch(strings, s);
}
public int findTypeIndex(String descriptor) {
return Collections.binarySearch(typeNames, descriptor);
}
public int findFieldIndex(FieldId fieldId) {
return Collections.binarySearch(fieldIds, fieldId);
}
public int findMethodIndex(MethodId methodId) {
return Collections.binarySearch(methodIds, methodId);
}
public int findClassDefIndexFromTypeIndex(int typeIndex) {
checkBounds(typeIndex, tableOfContents.typeIds.size);
if (!tableOfContents.classDefs.exists()) {
return -1;
}
for (int i = 0; i < tableOfContents.classDefs.size; i++) {
if (typeIndexFromClassDefIndex(i) == typeIndex) {
return i;
}
}
return -1;
}
/**
* Look up a field id type index from a field index. Cheaper than:
* {@code fieldIds().get(fieldDexIndex).getTypeIndex();}
*/
public int typeIndexFromFieldIndex(int fieldIndex) {
checkBounds(fieldIndex, tableOfContents.fieldIds.size);
int position = tableOfContents.fieldIds.off + (SizeOf.MEMBER_ID_ITEM * fieldIndex);
position += SizeOf.USHORT; // declaringClassIndex
return data.getShort(position) & 0xFFFF; // typeIndex
}
/**
* Look up a method id declaring class index from a method index. Cheaper than:
* {@code methodIds().get(methodIndex).getDeclaringClassIndex();}
*/
public int declaringClassIndexFromMethodIndex(int methodIndex) {
checkBounds(methodIndex, tableOfContents.methodIds.size);
int position = tableOfContents.methodIds.off + (SizeOf.MEMBER_ID_ITEM * methodIndex);
return data.getShort(position) & 0xFFFF; // declaringClassIndex
}
/**
* Look up a method id name index from a method index. Cheaper than:
* {@code methodIds().get(methodIndex).getNameIndex();}
*/
public int nameIndexFromMethodIndex(int methodIndex) {
checkBounds(methodIndex, tableOfContents.methodIds.size);
int position = tableOfContents.methodIds.off + (SizeOf.MEMBER_ID_ITEM * methodIndex);
position += SizeOf.USHORT; // declaringClassIndex
position += SizeOf.USHORT; // protoIndex
return data.getInt(position); // nameIndex
}
/**
* Look up a parameter type ids from a method index. Cheaper than:
* {@code readTypeList(protoIds.get(methodIds().get(methodDexIndex).getProtoIndex()).getParametersOffset()).getTypes();}
*/
public short[] parameterTypeIndicesFromMethodIndex(int methodIndex) {
checkBounds(methodIndex, tableOfContents.methodIds.size);
int position = tableOfContents.methodIds.off + (SizeOf.MEMBER_ID_ITEM * methodIndex);
position += SizeOf.USHORT; // declaringClassIndex
int protoIndex = data.getShort(position) & 0xFFFF;
checkBounds(protoIndex, tableOfContents.protoIds.size);
position = tableOfContents.protoIds.off + (SizeOf.PROTO_ID_ITEM * protoIndex);
position += SizeOf.UINT; // shortyIndex
position += SizeOf.UINT; // returnTypeIndex
int parametersOffset = data.getInt(position);
if (parametersOffset == 0) {
return EMPTY_SHORT_ARRAY;
}
position = parametersOffset;
int size = data.getInt(position);
if (size <= 0) {
throw new AssertionError("Unexpected parameter type list size: " + size);
}
position += SizeOf.UINT;
short[] types = new short[size];
for (int i = 0; i < size; i++) {
types[i] = data.getShort(position);
position += SizeOf.USHORT;
}
return types;
}
/**
* Look up a method id return type index from a method index. Cheaper than:
* {@code protoIds().get(methodIds().get(methodDexIndex).getProtoIndex()).getReturnTypeIndex();}
*/
public int returnTypeIndexFromMethodIndex(int methodIndex) {
checkBounds(methodIndex, tableOfContents.methodIds.size);
int position = tableOfContents.methodIds.off + (SizeOf.MEMBER_ID_ITEM * methodIndex);
position += SizeOf.USHORT; // declaringClassIndex
int protoIndex = data.getShort(position) & 0xFFFF;
checkBounds(protoIndex, tableOfContents.protoIds.size);
position = tableOfContents.protoIds.off + (SizeOf.PROTO_ID_ITEM * protoIndex);
position += SizeOf.UINT; // shortyIndex
return data.getInt(position); // returnTypeIndex
}
/**
* Look up a descriptor index from a type index. Cheaper than:
* {@code open(tableOfContents.typeIds.off + (index * SizeOf.TYPE_ID_ITEM)).readInt();}
*/
public int descriptorIndexFromTypeIndex(int typeIndex) {
checkBounds(typeIndex, tableOfContents.typeIds.size);
int position = tableOfContents.typeIds.off + (SizeOf.TYPE_ID_ITEM * typeIndex);
return data.getInt(position);
}
/**
* Look up a type index index from a class def index.
*/
public int typeIndexFromClassDefIndex(int classDefIndex) {
checkBounds(classDefIndex, tableOfContents.classDefs.size);
int position = tableOfContents.classDefs.off + (SizeOf.CLASS_DEF_ITEM * classDefIndex);
return data.getInt(position);
}
/**
* Look up an annotation directory offset from a class def index.
*/
public int annotationDirectoryOffsetFromClassDefIndex(int classDefIndex) {
checkBounds(classDefIndex, tableOfContents.classDefs.size);
int position = tableOfContents.classDefs.off + (SizeOf.CLASS_DEF_ITEM * classDefIndex);
position += SizeOf.UINT; // type
position += SizeOf.UINT; // accessFlags
position += SizeOf.UINT; // superType
position += SizeOf.UINT; // interfacesOffset
position += SizeOf.UINT; // sourceFileIndex
return data.getInt(position);
}
/**
* Look up interface types indices from a return type index from a method index. Cheaper than:
* {@code ...getClassDef(classDefIndex).getInterfaces();}
*/
public short[] interfaceTypeIndicesFromClassDefIndex(int classDefIndex) {
checkBounds(classDefIndex, tableOfContents.classDefs.size);
int position = tableOfContents.classDefs.off + (SizeOf.CLASS_DEF_ITEM * classDefIndex);
position += SizeOf.UINT; // type
position += SizeOf.UINT; // accessFlags
position += SizeOf.UINT; // superType
int interfacesOffset = data.getInt(position);
if (interfacesOffset == 0) {
return EMPTY_SHORT_ARRAY;
}
position = interfacesOffset;
int size = data.getInt(position);
if (size <= 0) {
throw new AssertionError("Unexpected interfaces list size: " + size);
}
position += SizeOf.UINT;
short[] types = new short[size];
for (int i = 0; i < size; i++) {
types[i] = data.getShort(position);
position += SizeOf.USHORT;
}
return types;
}
public final class Section implements ByteInput, ByteOutput {
private final String name;
private final ByteBuffer data;
private final int initialPosition;
private Section(String name, ByteBuffer data) {
this.name = name;
this.data = data;
this.initialPosition = data.position();
}
public int getPosition() {
return data.position();
}
public int readInt() {
return data.getInt();
}
public short readShort() {
return data.getShort();
}
public int readUnsignedShort() {
return readShort() & 0xffff;
}
public byte readByte() {
return data.get();
}
public byte[] readByteArray(int length) {
byte[] result = new byte[length];
data.get(result);
return result;
}
public short[] readShortArray(int length) {
if (length == 0) {
return EMPTY_SHORT_ARRAY;
}
short[] result = new short[length];
for (int i = 0; i < length; i++) {
result[i] = readShort();
}
return result;
}
public int readUleb128() {
return Leb128.readUnsignedLeb128(this);
}
public int readUleb128p1() {
return Leb128.readUnsignedLeb128(this) - 1;
}
public int readSleb128() {
return Leb128.readSignedLeb128(this);
}
public void writeUleb128p1(int i) {
writeUleb128(i + 1);
}
public TypeList readTypeList() {
int size = readInt();
short[] types = readShortArray(size);
alignToFourBytes();
return new TypeList(Dex.this, types);
}
public String readString() {
int offset = readInt();
int savedPosition = data.position();
int savedLimit = data.limit();
data.position(offset);
data.limit(data.capacity());
try {
int expectedLength = readUleb128();
String result = Mutf8.decode(this, new char[expectedLength]);
if (result.length() != expectedLength) {
throw new DexException("Declared length " + expectedLength
+ " doesn't match decoded length of " + result.length());
}
return result;
} catch (UTFDataFormatException e) {
throw new DexException(e);
} finally {
data.position(savedPosition);
data.limit(savedLimit);
}
}
public FieldId readFieldId() {
int declaringClassIndex = readUnsignedShort();
int typeIndex = readUnsignedShort();
int nameIndex = readInt();
return new FieldId(Dex.this, declaringClassIndex, typeIndex, nameIndex);
}
public MethodId readMethodId() {
int declaringClassIndex = readUnsignedShort();
int protoIndex = readUnsignedShort();
int nameIndex = readInt();
return new MethodId(Dex.this, declaringClassIndex, protoIndex, nameIndex);
}
public ProtoId readProtoId() {
int shortyIndex = readInt();
int returnTypeIndex = readInt();
int parametersOffset = readInt();
return new ProtoId(Dex.this, shortyIndex, returnTypeIndex, parametersOffset);
}
public ClassDef readClassDef() {
int offset = getPosition();
int type = readInt();
int accessFlags = readInt();
int supertype = readInt();
int interfacesOffset = readInt();
int sourceFileIndex = readInt();
int annotationsOffset = readInt();
int classDataOffset = readInt();
int staticValuesOffset = readInt();
return new ClassDef(Dex.this, offset, type, accessFlags, supertype,
interfacesOffset, sourceFileIndex, annotationsOffset, classDataOffset,
staticValuesOffset);
}
private Code readCode() {
int registersSize = readUnsignedShort();
int insSize = readUnsignedShort();
int outsSize = readUnsignedShort();
int triesSize = readUnsignedShort();
int debugInfoOffset = readInt();
int instructionsSize = readInt();
short[] instructions = readShortArray(instructionsSize);
Try[] tries;
CatchHandler[] catchHandlers;
if (triesSize > 0) {
if (instructions.length % 2 == 1) {
readShort(); // padding
}
/*
* We can't read the tries until we've read the catch handlers.
* Unfortunately they're in the opposite order in the dex file
* so we need to read them out-of-order.
*/
Section triesSection = open(data.position());
skip(triesSize * SizeOf.TRY_ITEM);
catchHandlers = readCatchHandlers();
tries = triesSection.readTries(triesSize, catchHandlers);
} else {
tries = new Try[0];
catchHandlers = new CatchHandler[0];
}
return new Code(registersSize, insSize, outsSize, debugInfoOffset, instructions,
tries, catchHandlers);
}
private CatchHandler[] readCatchHandlers() {
int baseOffset = data.position();
int catchHandlersSize = readUleb128();
CatchHandler[] result = new CatchHandler[catchHandlersSize];
for (int i = 0; i < catchHandlersSize; i++) {
int offset = data.position() - baseOffset;
result[i] = readCatchHandler(offset);
}
return result;
}
private Try[] readTries(int triesSize, CatchHandler[] catchHandlers) {
Try[] result = new Try[triesSize];
for (int i = 0; i < triesSize; i++) {
int startAddress = readInt();
int instructionCount = readUnsignedShort();
int handlerOffset = readUnsignedShort();
int catchHandlerIndex = findCatchHandlerIndex(catchHandlers, handlerOffset);
result[i] = new Try(startAddress, instructionCount, catchHandlerIndex);
}
return result;
}
private int findCatchHandlerIndex(CatchHandler[] catchHandlers, int offset) {
for (int i = 0; i < catchHandlers.length; i++) {
CatchHandler catchHandler = catchHandlers[i];
if (catchHandler.getOffset() == offset) {
return i;
}
}
throw new IllegalArgumentException();
}
private CatchHandler readCatchHandler(int offset) {
int size = readSleb128();
int handlersCount = Math.abs(size);
int[] typeIndexes = new int[handlersCount];
int[] addresses = new int[handlersCount];
for (int i = 0; i < handlersCount; i++) {
typeIndexes[i] = readUleb128();
addresses[i] = readUleb128();
}
int catchAllAddress = size <= 0 ? readUleb128() : -1;
return new CatchHandler(typeIndexes, addresses, catchAllAddress, offset);
}
private ClassData readClassData() {
int staticFieldsSize = readUleb128();
int instanceFieldsSize = readUleb128();
int directMethodsSize = readUleb128();
int virtualMethodsSize = readUleb128();
ClassData.Field[] staticFields = readFields(staticFieldsSize);
ClassData.Field[] instanceFields = readFields(instanceFieldsSize);
ClassData.Method[] directMethods = readMethods(directMethodsSize);
ClassData.Method[] virtualMethods = readMethods(virtualMethodsSize);
return new ClassData(staticFields, instanceFields, directMethods, virtualMethods);
}
private ClassData.Field[] readFields(int count) {
ClassData.Field[] result = new ClassData.Field[count];
int fieldIndex = 0;
for (int i = 0; i < count; i++) {
fieldIndex += readUleb128(); // field index diff
int accessFlags = readUleb128();
result[i] = new ClassData.Field(fieldIndex, accessFlags);
}
return result;
}
private ClassData.Method[] readMethods(int count) {
ClassData.Method[] result = new ClassData.Method[count];
int methodIndex = 0;
for (int i = 0; i < count; i++) {
methodIndex += readUleb128(); // method index diff
int accessFlags = readUleb128();
int codeOff = readUleb128();
result[i] = new ClassData.Method(methodIndex, accessFlags, codeOff);
}
return result;
}
/**
* Returns a byte array containing the bytes from {@code start} to this
* section's current position.
*/
private byte[] getBytesFrom(int start) {
int end = data.position();
byte[] result = new byte[end - start];
data.position(start);
data.get(result);
return result;
}
public Annotation readAnnotation() {
byte visibility = readByte();
int start = data.position();
new EncodedValueReader(this, EncodedValueReader.ENCODED_ANNOTATION).skipValue();
return new Annotation(Dex.this, visibility, new EncodedValue(getBytesFrom(start)));
}
public EncodedValue readEncodedArray() {
int start = data.position();
new EncodedValueReader(this, EncodedValueReader.ENCODED_ARRAY).skipValue();
return new EncodedValue(getBytesFrom(start));
}
public void skip(int count) {
if (count < 0) {
throw new IllegalArgumentException();
}
data.position(data.position() + count);
}
/**
* Skips bytes until the position is aligned to a multiple of 4.
*/
public void alignToFourBytes() {
data.position((data.position() + 3) & ~3);
}
/**
* Writes 0x00 until the position is aligned to a multiple of 4.
*/
public void alignToFourBytesWithZeroFill() {
while ((data.position() & 3) != 0) {
data.put((byte) 0);
}
}
public void assertFourByteAligned() {
if ((data.position() & 3) != 0) {
throw new IllegalStateException("Not four byte aligned!");
}
}
public void write(byte[] bytes) {
this.data.put(bytes);
}
public void writeByte(int b) {
data.put((byte) b);
}
public void writeShort(short i) {
data.putShort(i);
}
public void writeUnsignedShort(int i) {
short s = (short) i;
if (i != (s & 0xffff)) {
throw new IllegalArgumentException("Expected an unsigned short: " + i);
}
writeShort(s);
}
public void write(short[] shorts) {
for (short s : shorts) {
writeShort(s);
}
}
public void writeInt(int i) {
data.putInt(i);
}
public void writeUleb128(int i) {
try {
Leb128.writeUnsignedLeb128(this, i);
} catch (ArrayIndexOutOfBoundsException e) {
throw new DexException("Section limit " + data.limit() + " exceeded by " + name);
}
}
public void writeSleb128(int i) {
try {
Leb128.writeSignedLeb128(this, i);
} catch (ArrayIndexOutOfBoundsException e) {
throw new DexException("Section limit " + data.limit() + " exceeded by " + name);
}
}
public void writeStringData(String value) {
try {
int length = value.length();
writeUleb128(length);
write(Mutf8.encode(value));
writeByte(0);
} catch (UTFDataFormatException e) {
throw new AssertionError();
}
}
public void writeTypeList(TypeList typeList) {
short[] types = typeList.getTypes();
writeInt(types.length);
for (short type : types) {
writeShort(type);
}
alignToFourBytesWithZeroFill();
}
/**
* Returns the number of bytes remaining in this section.
*/
public int remaining() {
return data.remaining();
}
/**
* Returns the number of bytes used by this section.
*/
public int used() {
return data.position() - initialPosition;
}
}
private final class StringTable extends AbstractList implements RandomAccess {
@Override public String get(int index) {
checkBounds(index, tableOfContents.stringIds.size);
return open(tableOfContents.stringIds.off + (index * SizeOf.STRING_ID_ITEM))
.readString();
}
@Override public int size() {
return tableOfContents.stringIds.size;
}
}
private final class TypeIndexToDescriptorIndexTable extends AbstractList
implements RandomAccess {
@Override public Integer get(int index) {
return descriptorIndexFromTypeIndex(index);
}
@Override public int size() {
return tableOfContents.typeIds.size;
}
}
private final class TypeIndexToDescriptorTable extends AbstractList
implements RandomAccess {
@Override public String get(int index) {
return strings.get(descriptorIndexFromTypeIndex(index));
}
@Override public int size() {
return tableOfContents.typeIds.size;
}
}
private final class ProtoIdTable extends AbstractList implements RandomAccess {
@Override public ProtoId get(int index) {
checkBounds(index, tableOfContents.protoIds.size);
return open(tableOfContents.protoIds.off + (SizeOf.PROTO_ID_ITEM * index))
.readProtoId();
}
@Override public int size() {
return tableOfContents.protoIds.size;
}
}
private final class FieldIdTable extends AbstractList implements RandomAccess {
@Override public FieldId get(int index) {
checkBounds(index, tableOfContents.fieldIds.size);
return open(tableOfContents.fieldIds.off + (SizeOf.MEMBER_ID_ITEM * index))
.readFieldId();
}
@Override public int size() {
return tableOfContents.fieldIds.size;
}
}
private final class MethodIdTable extends AbstractList implements RandomAccess {
@Override public MethodId get(int index) {
checkBounds(index, tableOfContents.methodIds.size);
return open(tableOfContents.methodIds.off + (SizeOf.MEMBER_ID_ITEM * index))
.readMethodId();
}
@Override public int size() {
return tableOfContents.methodIds.size;
}
}
private final class ClassDefIterator implements Iterator {
private final Dex.Section in = open(tableOfContents.classDefs.off);
private int count = 0;
@Override
public boolean hasNext() {
return count < tableOfContents.classDefs.size;
}
@Override
public ClassDef next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
count++;
return in.readClassDef();
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
private final class ClassDefIterable implements Iterable {
public Iterator iterator() {
return !tableOfContents.classDefs.exists()
? Collections.emptySet().iterator()
: new ClassDefIterator();
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy