|  | // 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.ProgramResource; | 
|  | import com.android.tools.r8.ResourceException; | 
|  | import com.android.tools.r8.errors.CompilationError; | 
|  | import com.android.tools.r8.origin.Origin; | 
|  | import com.android.tools.r8.utils.DexVersion; | 
|  | import com.android.tools.r8.utils.StringUtils; | 
|  | import java.io.IOException; | 
|  | import java.nio.BufferUnderflowException; | 
|  | import java.nio.ByteOrder; | 
|  | import java.util.Optional; | 
|  |  | 
|  | /** | 
|  | * {@link BinaryReader} for Dex content. | 
|  | */ | 
|  | public class DexReader extends BinaryReader { | 
|  |  | 
|  | private final DexVersion version; | 
|  |  | 
|  | public DexReader(ProgramResource resource) throws ResourceException, IOException { | 
|  | super(resource); | 
|  | version = parseMagic(buffer); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Returns a File that contains the bytes provided as argument. Used for testing. | 
|  | * | 
|  | * @param bytes contents of the file | 
|  | */ | 
|  | DexReader(Origin origin, byte[] bytes) { | 
|  | super(origin, bytes); | 
|  | version = parseMagic(buffer); | 
|  | } | 
|  |  | 
|  | // Parse the magic header and determine the dex file version. | 
|  | private DexVersion parseMagic(CompatByteBuffer buffer) { | 
|  | try { | 
|  | buffer.get(); | 
|  | buffer.rewind(); | 
|  | } catch (BufferUnderflowException e) { | 
|  | throw new CompilationError("Dex file is empty", origin); | 
|  | } | 
|  | int index = 0; | 
|  | for (byte prefixByte : DEX_FILE_MAGIC_PREFIX) { | 
|  | byte actualByte = buffer.get(index++); | 
|  | if (actualByte != prefixByte) { | 
|  | StringBuilder stringBuilder = new StringBuilder(); | 
|  | stringBuilder.append( | 
|  | "Dex file has invalid header, expected " | 
|  | + prefixByte | 
|  | + " got " | 
|  | + actualByte | 
|  | + ". Next bytes are "); | 
|  | for (int i = 0; i < 10; i++) { | 
|  | if (buffer.hasRemaining()) { | 
|  | stringBuilder.append(StringUtils.hexString(buffer.get(), 2)); | 
|  | stringBuilder.append(","); | 
|  | } | 
|  | } | 
|  | throw new CompilationError(stringBuilder.toString(), origin); | 
|  | } | 
|  | } | 
|  |  | 
|  | char versionByte0 = (char) buffer.get(index++); | 
|  | char versionByte1 = (char) buffer.get(index++); | 
|  | char versionByte2 = (char) buffer.get(index++); | 
|  | Optional<DexVersion> maybeVersion = | 
|  | DexVersion.getDexVersion(versionByte0, versionByte1, versionByte2); | 
|  | if (!maybeVersion.isPresent()) { | 
|  | throw new CompilationError( | 
|  | "Unsupported DEX file version: " | 
|  | + versionByte0 | 
|  | + versionByte1 | 
|  | + versionByte2, | 
|  | origin); | 
|  | } | 
|  | if (buffer.get(index++) != '\0') { | 
|  | throw new CompilationError("Dex file has invalid header", origin); | 
|  | } | 
|  | return maybeVersion.get(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | 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 { | 
|  | if (endian != Constants.ENDIAN_CONSTANT) { | 
|  | throw new CompilationError("Unable to determine endianess for reading dex file."); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | DexVersion getDexVersion() { | 
|  | return version; | 
|  | } | 
|  | } |