blob: 344134649e9b03914650dd69c40acb8daa73d91d [file] [log] [blame]
// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.dex;
import static com.android.tools.r8.dex.Constants.DEX_FILE_MAGIC_PREFIX;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.utils.LebUtils;
import com.google.common.io.ByteStreams;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
public class DexFile {
final String name;
private final ByteBuffer buffer;
private final int version;
DexFile(String name) throws IOException {
this.name = name;
Path path = Paths.get(name);
buffer = ByteBuffer.wrap(Files.readAllBytes(path));
version = parseMagic(buffer);
}
public DexFile(InputStream input) throws IOException {
// TODO(zerny): Remove dependencies on file names.
name = "input-stream.dex";
buffer = ByteBuffer.wrap(ByteStreams.toByteArray(input));
version = parseMagic(buffer);
}
/**
* Returns a File that contains the bytes provided as argument. Used for testing.
*
* @param bytes contents of the file
*/
DexFile(byte[] bytes) {
this.name = "mockfile.dex";
buffer = ByteBuffer.wrap(bytes);
version = parseMagic(buffer);
}
// Parse the magic header and determine the dex file version.
private int parseMagic(ByteBuffer buffer) {
int index = 0;
for (byte prefixByte : DEX_FILE_MAGIC_PREFIX) {
if (buffer.get(index++) != prefixByte) {
throw new CompilationError("Dex file has invalid header: " + name);
}
}
if (buffer.get(index++) != '0' || buffer.get(index++) != '3') {
throw new CompilationError("Dex file has invalid version number: " + name);
}
byte versionByte = buffer.get(index++);
int version;
switch (versionByte) {
case '8':
version = Constants.ANDROID_O_DEX_VERSION;
break;
case '7':
version = Constants.ANDROID_N_DEX_VERSION;
break;
case '5':
version = Constants.ANDROID_PRE_N_DEX_VERSION;
break;
default:
throw new CompilationError("Dex file has invalid version number: " + name);
}
if (buffer.get(index++) != '\0') {
throw new CompilationError("Dex file has invalid header: " + name);
}
return version;
}
int getDexVersion() {
return version;
}
byte[] getByteArray(int size) {
byte[] result = new byte[size];
buffer.get(result);
return result;
}
int getUleb128() {
return LebUtils.parseUleb128(this);
}
int getSleb128() {
return LebUtils.parseSleb128(this);
}
int getUleb128p1() {
return getUleb128() - 1;
}
int getUint() {
int result = buffer.getInt();
assert result >= 0; // Ensure the java int didn't overflow.
return result;
}
int getUshort() {
int result = buffer.getShort() & 0xffff;
assert result >= 0; // Ensure we have a non-negative number.
return result;
}
short getShort() {
return buffer.getShort();
}
int getUint(int offset) {
int result = buffer.getInt(offset);
assert result >= 0; // Ensure the java int didn't overflow.
return result;
}
public int getInt() {
return buffer.getInt();
}
void setByteOrder() {
// Make sure we set the right endian for reading.
buffer.order(ByteOrder.LITTLE_ENDIAN);
int endian = buffer.getInt(Constants.ENDIAN_TAG_OFFSET);
if (endian == Constants.REVERSE_ENDIAN_CONSTANT) {
buffer.order(ByteOrder.BIG_ENDIAN);
} else {
assert endian == Constants.ENDIAN_CONSTANT;
}
}
int position() {
return buffer.position();
}
void position(int position) {
buffer.position(position);
}
void align(int alignment) {
assert (alignment & (alignment - 1)) == 0; // Check alignment is power of 2.
int p = buffer.position();
p += (alignment - (p % alignment)) & (alignment - 1);
buffer.position(p);
}
public byte get() {
return buffer.get();
}
int getUbyte() {
int result = buffer.get() & 0xff;
assert result >= 0; // Ensure we have a non-negative result.
return result;
}
int end() {
return buffer.capacity();
}
}