|  | // Copyright (c) 2017, 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.MAX_VDEX_VERSION; | 
|  | import static com.android.tools.r8.dex.Constants.MIN_VDEX_VERSION; | 
|  | import static com.android.tools.r8.dex.Constants.VDEX_FILE_MAGIC_PREFIX; | 
|  | import static com.android.tools.r8.dex.Constants.VDEX_FILE_VERSION_LENGTH; | 
|  | import static com.android.tools.r8.dex.Constants.VDEX_NUMBER_OF_DEX_FILES_OFFSET; | 
|  |  | 
|  | import com.android.tools.r8.errors.CompilationError; | 
|  | import com.android.tools.r8.origin.Origin; | 
|  | import com.google.common.io.ByteStreams; | 
|  | import java.io.IOException; | 
|  | import java.io.InputStream; | 
|  | import java.nio.ByteOrder; | 
|  |  | 
|  | /** | 
|  | * See runtime/vdex_file.h and runtime/vdex_file.cc in the Art code for the vdex file format. | 
|  | */ | 
|  | public class VDexReader extends BinaryReader { | 
|  |  | 
|  | private final int version; | 
|  |  | 
|  | public VDexReader(Origin origin, InputStream stream) throws IOException { | 
|  | super(origin, ByteStreams.toByteArray(stream)); | 
|  | version = parseMagic(buffer); | 
|  | if (!supportedVersion(version)) { | 
|  | throw new CompilationError("Unsupported vdex file version " + version, origin); | 
|  | } | 
|  | } | 
|  |  | 
|  | private static boolean supportedVersion(int versionNumber) { | 
|  | return MIN_VDEX_VERSION <= versionNumber && versionNumber <= MAX_VDEX_VERSION; | 
|  | } | 
|  |  | 
|  | // Parse the magic header and determine the dex file version. | 
|  | private int parseMagic(CompatByteBuffer buffer) { | 
|  | int index = 0; | 
|  | for (byte prefixByte : VDEX_FILE_MAGIC_PREFIX) { | 
|  | if (buffer.get(index++) != prefixByte) { | 
|  | throw new CompilationError("VDex file has invalid header", origin); | 
|  | } | 
|  | } | 
|  | byte[] version = new byte[VDEX_FILE_VERSION_LENGTH]; | 
|  | for (int i = 0; i < VDEX_FILE_VERSION_LENGTH; i++) { | 
|  | if (!buffer.hasRemaining()) { | 
|  | throw new CompilationError("Truncated VDex file - unable to read version", origin); | 
|  | } | 
|  | version[i] = buffer.get(index++); | 
|  | } | 
|  | if (version[VDEX_FILE_VERSION_LENGTH - 1] != '\0') { | 
|  | throw new CompilationError("VDex file has invalid version number", origin); | 
|  | } | 
|  | int versionNumber = 0; | 
|  | for (int i = 0; i < VDEX_FILE_VERSION_LENGTH - 1;i++) { | 
|  | if (0x30 <= version[i] && version[i] <= 0x39) { | 
|  | versionNumber = versionNumber * 10 + version[i] - 0x30; | 
|  | } else { | 
|  | throw new CompilationError("VDex file has invalid version number", origin); | 
|  | } | 
|  | } | 
|  | return versionNumber; | 
|  | } | 
|  |  | 
|  | public static int firstDexOffset(int numberOfDexFiles) { | 
|  | return Constants.VDEX_CHECKSUM_SECTION_OFFSET | 
|  | + numberOfDexFiles * Constants.VDEX_DEX_CHECKSUM_SIZE; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | void setByteOrder() { | 
|  | // Make sure we set the right endian for reading. | 
|  | buffer.order(ByteOrder.LITTLE_ENDIAN); | 
|  | int dexFiles = buffer.getInt(VDEX_NUMBER_OF_DEX_FILES_OFFSET); | 
|  | // Reading a strange number of dex files indicate reading with the wrong endian. | 
|  | if (dexFiles < 0 || dexFiles > 1000) { | 
|  | buffer.order(ByteOrder.BIG_ENDIAN); | 
|  | dexFiles = buffer.getInt(VDEX_NUMBER_OF_DEX_FILES_OFFSET); | 
|  | assert dexFiles < 0 || dexFiles > 1000; | 
|  | } | 
|  |  | 
|  | // Make sure we did set the right endian for reading. | 
|  | int endian = buffer.getInt(firstDexOffset(dexFiles) + Constants.ENDIAN_TAG_OFFSET); | 
|  | if (endian != Constants.ENDIAN_CONSTANT) { | 
|  | throw new CompilationError("Unable to determine endianess for reading vdex file."); | 
|  | } | 
|  | } | 
|  |  | 
|  | int getVDexVersion() { | 
|  | return version; | 
|  | } | 
|  | } |