Add a new dex version and api level
- Refactor around dex version to have an enumeration similar
to AndroidApiLevel
Change-Id: I9c2d09dd7cf0d51055959041e10c9cdb627e93e7
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
index 6169178..cc75fd0 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
@@ -3,8 +3,6 @@
// 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.ANDROID_N_DEX_VERSION;
-import static com.android.tools.r8.dex.Constants.ANDROID_O_DEX_VERSION;
import static com.android.tools.r8.graph.ClassKind.CLASSPATH;
import static com.android.tools.r8.graph.ClassKind.LIBRARY;
import static com.android.tools.r8.graph.ClassKind.PROGRAM;
@@ -26,6 +24,7 @@
import com.android.tools.r8.naming.ProguardMapReader;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DexVersion;
import com.android.tools.r8.utils.ClassProvider;
import com.android.tools.r8.utils.ClasspathClassCollection;
import com.android.tools.r8.utils.DescriptorUtils;
@@ -95,38 +94,18 @@
}
private int verifyOrComputeMinApiLevel(int computedMinApiLevel, DexFile file) {
- int version = file.getDexVersion();
+ DexVersion version = DexVersion.getDexVersion(file.getDexVersion());
if (options.minApiLevel == AndroidApiLevel.getDefault().getLevel()) {
- computedMinApiLevel = Math.max(computedMinApiLevel, dexVersionToMinSdk(version));
- } else if (!minApiMatchesDexVersion(version)) {
- throw new CompilationError("Dex file with version '" + version +
+ computedMinApiLevel = Math
+ .max(computedMinApiLevel, AndroidApiLevel.getMinAndroidApiLevel(version).getLevel());
+ } else if (!version
+ .matchesApiLevel(AndroidApiLevel.getAndroidApiLevel(options.minApiLevel))) {
+ throw new CompilationError("Dex file with version '" + version.getIntValue() +
"' cannot be used with min sdk level '" + options.minApiLevel + "'.");
}
return computedMinApiLevel;
}
- private boolean minApiMatchesDexVersion(int version) {
- switch (version) {
- case ANDROID_O_DEX_VERSION:
- return options.minApiLevel >= AndroidApiLevel.O.getLevel();
- case ANDROID_N_DEX_VERSION:
- return options.minApiLevel >= AndroidApiLevel.N.getLevel();
- default:
- return true;
- }
- }
-
- private int dexVersionToMinSdk(int version) {
- switch (version) {
- case ANDROID_O_DEX_VERSION:
- return AndroidApiLevel.O.getLevel();
- case ANDROID_N_DEX_VERSION:
- return AndroidApiLevel.N.getLevel();
- default:
- return AndroidApiLevel.getDefault().getLevel();
- }
- }
-
private void readProguardMap(DexApplication.Builder builder, ExecutorService executorService,
List<Future<?>> futures) {
// Read the Proguard mapping file in parallel with DexCode and DexProgramClass items.
diff --git a/src/main/java/com/android/tools/r8/dex/Constants.java b/src/main/java/com/android/tools/r8/dex/Constants.java
index 58ff5d7..3a06f1d 100644
--- a/src/main/java/com/android/tools/r8/dex/Constants.java
+++ b/src/main/java/com/android/tools/r8/dex/Constants.java
@@ -11,16 +11,6 @@
public static final byte[] VDEX_FILE_MAGIC_PREFIX = {'v', 'd', 'e', 'x'};
public static final byte[] VDEX_FILE_VERSION = {'0', '1', '0', '\0'};
- /** dex file version number for Android O (API level 26) */
- public static final int ANDROID_O_DEX_VERSION = 38;
- public static final byte[] ANDROID_O_DEX_VERSION_BYTES = {'0', '3', '8'};
- /** dex file version number for Android N (API level 24) */
- public static final int ANDROID_N_DEX_VERSION = 37;
- public static final byte[] ANDROID_N_DEX_VERSION_BYTES = {'0', '3', '7'};
- /** dex file version number for all releases prior to Android N */
- public static final int ANDROID_PRE_N_DEX_VERSION = 35;
- public static final byte[] ANDROID_PRE_N_DEX_VERSION_BYTES = {'0', '3', '5'};
-
/** vdex file version number for Android O (API level 26) */
public static final int ANDROID_O_VDEX_VERSION = 10;
diff --git a/src/main/java/com/android/tools/r8/dex/DexFile.java b/src/main/java/com/android/tools/r8/dex/DexFile.java
index 2865300..d2db9d6 100644
--- a/src/main/java/com/android/tools/r8/dex/DexFile.java
+++ b/src/main/java/com/android/tools/r8/dex/DexFile.java
@@ -6,6 +6,7 @@
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.DexVersion;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
@@ -54,14 +55,17 @@
byte versionByte = buffer.get(index++);
int version;
switch (versionByte) {
+ case '9':
+ version = DexVersion.V39.getIntValue();
+ break;
case '8':
- version = Constants.ANDROID_O_DEX_VERSION;
+ version = DexVersion.V38.getIntValue();
break;
case '7':
- version = Constants.ANDROID_N_DEX_VERSION;
+ version = DexVersion.V37.getIntValue();
break;
case '5':
- version = Constants.ANDROID_PRE_N_DEX_VERSION;
+ version = DexVersion.V35.getIntValue();
break;
default:
throw new CompilationError("Dex file has invalid version number: " + name);
diff --git a/src/main/java/com/android/tools/r8/dex/FileWriter.java b/src/main/java/com/android/tools/r8/dex/FileWriter.java
index afa7105..70fd655 100644
--- a/src/main/java/com/android/tools/r8/dex/FileWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/FileWriter.java
@@ -48,6 +48,7 @@
import com.android.tools.r8.naming.MemberNaming.Signature;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.DexVersion;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.LebUtils;
import com.android.tools.r8.utils.ThrowingConsumer;
@@ -733,20 +734,13 @@
dest.forward(size * Constants.TYPE_MAP_LIST_ITEM_SIZE);
}
- private static byte[] convertApiLevelToDexVersion(int apiLevel) {
- if (apiLevel >= AndroidApiLevel.O.getLevel()) {
- return Constants.ANDROID_O_DEX_VERSION_BYTES;
- }
- if (apiLevel >= AndroidApiLevel.N.getLevel()) {
- return Constants.ANDROID_N_DEX_VERSION_BYTES;
- }
- return Constants.ANDROID_PRE_N_DEX_VERSION_BYTES;
- }
-
private void writeHeader(Layout layout) {
dest.moveTo(0);
dest.putBytes(Constants.DEX_FILE_MAGIC_PREFIX);
- dest.putBytes(convertApiLevelToDexVersion(options.minApiLevel));
+ dest.putBytes(
+ DexVersion
+ .getDexVersion(AndroidApiLevel.getAndroidApiLevel(options.minApiLevel))
+ .getBytes());
dest.putByte(Constants.DEX_FILE_MAGIC_SUFFIX);
// Leave out checksum and signature for now.
dest.moveTo(Constants.FILE_SIZE_OFFSET);
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java b/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java
index 04ab202..b1bdfde 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java
@@ -3,10 +3,13 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.utils;
+import com.android.tools.r8.errors.Unreachable;
+
/**
* Android API level description
*/
public enum AndroidApiLevel {
+ P(27),
O(26),
N_MR1(25),
N(24),
@@ -51,4 +54,84 @@
public static AndroidApiLevel getDefault() {
return AndroidApiLevel.B;
}
+
+ public DexVersion getDexVersion() {
+ return DexVersion.getDexVersion(this);
+ }
+
+ public static AndroidApiLevel getMinAndroidApiLevel(DexVersion dexVersion) {
+ switch (dexVersion) {
+ case V35:
+ return AndroidApiLevel.B;
+ case V37:
+ return AndroidApiLevel.N;
+ case V38:
+ return AndroidApiLevel.O;
+ case V39:
+ return AndroidApiLevel.P;
+ default:
+ throw new Unreachable();
+ }
+ }
+
+ public static AndroidApiLevel getAndroidApiLevel(int apiLevel) {
+ switch (apiLevel) {
+ case 1:
+ return B;
+ case 2:
+ return B_1_1;
+ case 3:
+ return C;
+ case 4:
+ return D;
+ case 5:
+ return E;
+ case 6:
+ return E_0_1;
+ case 7:
+ return E_MR1;
+ case 8:
+ return F;
+ case 9:
+ return G;
+ case 10:
+ return G_MR1;
+ case 11:
+ return H;
+ case 12:
+ return H_MR1;
+ case 13:
+ return H_MR2;
+ case 14:
+ return I;
+ case 15:
+ return I_MR1;
+ case 16:
+ return J;
+ case 17:
+ return J_MR1;
+ case 18:
+ return J_MR2;
+ case 19:
+ return K;
+ case 20:
+ return K_WATCH;
+ case 21:
+ return L;
+ case 22:
+ return L_MR1;
+ case 23:
+ return M;
+ case 24:
+ return N;
+ case 25:
+ return N_MR1;
+ case 26:
+ return O;
+ case 27:
+ return P;
+ default:
+ throw new Unreachable();
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/utils/DexVersion.java b/src/main/java/com/android/tools/r8/utils/DexVersion.java
new file mode 100644
index 0000000..e45a0dc
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/DexVersion.java
@@ -0,0 +1,101 @@
+// 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.utils;
+
+import com.android.tools.r8.errors.Unreachable;
+
+/**
+ * Android dex version
+ */
+public enum DexVersion {
+ V35(35, new byte[]{'0', '3', '5'}),
+ V37(37, new byte[]{'0', '3', '7'}),
+ V38(38, new byte[]{'0', '3', '8'}),
+ V39(39, new byte[]{'0', '3', '9'});
+
+ private final int dexVersion;
+
+ private final byte[] dexVersionBytes;
+
+ DexVersion(int dexVersion, byte[] dexVersionBytes) {
+ this.dexVersion = dexVersion;
+ this.dexVersionBytes = dexVersionBytes;
+ }
+
+ public int getIntValue() {
+ return dexVersion;
+ }
+
+ public byte[] getBytes() {
+ return dexVersionBytes;
+ }
+
+ public boolean matchesApiLevel(AndroidApiLevel androidApiLevel) {
+ switch (this) {
+ case V35:
+ return true;
+ case V37:
+ return androidApiLevel.getLevel() >= AndroidApiLevel.N.getLevel();
+ case V38:
+ return androidApiLevel.getLevel() >= AndroidApiLevel.O.getLevel();
+ case V39:
+ return androidApiLevel.getLevel() >= AndroidApiLevel.P.getLevel();
+ default:
+ throw new Unreachable();
+ }
+ }
+
+ public static DexVersion getDexVersion(AndroidApiLevel androidApiLevel) {
+ switch (androidApiLevel) {
+ case P:
+ return DexVersion.V39;
+ case O:
+ return DexVersion.V38;
+ case N_MR1:
+ case N:
+ return DexVersion.V37;
+ case B:
+ case B_1_1:
+ case C:
+ case D:
+ case E:
+ case E_0_1:
+ case E_MR1:
+ case F:
+ case G:
+ case G_MR1:
+ case H:
+ case H_MR1:
+ case H_MR2:
+ case I:
+ case I_MR1:
+ case J:
+ case J_MR1:
+ case J_MR2:
+ case K:
+ case K_WATCH:
+ case L:
+ case L_MR1:
+ case M:
+ return DexVersion.V35;
+ default :
+ throw new Unreachable();
+ }
+ }
+
+ public static DexVersion getDexVersion(int intValue) {
+ switch (intValue) {
+ case 35:
+ return V35;
+ case 37:
+ return V37;
+ case 38:
+ return V38;
+ case 39:
+ return V39;
+ default:
+ throw new Unreachable();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/dex/EncodedFloatingValueTest.java b/src/test/java/com/android/tools/r8/dex/EncodedFloatingValueTest.java
index 3a4ea04..2bcce43 100644
--- a/src/test/java/com/android/tools/r8/dex/EncodedFloatingValueTest.java
+++ b/src/test/java/com/android/tools/r8/dex/EncodedFloatingValueTest.java
@@ -7,6 +7,8 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.DexVersion;
import com.android.tools.r8.utils.EncodedValueUtils;
import java.util.Arrays;
import java.util.Collection;
@@ -45,7 +47,7 @@
private DexFile createDexFileWithContent(byte[] bytes) {
DexOutputBuffer buffer = new DexOutputBuffer();
buffer.putBytes(Constants.DEX_FILE_MAGIC_PREFIX);
- buffer.putBytes(Constants.ANDROID_PRE_N_DEX_VERSION_BYTES);
+ buffer.putBytes(AndroidApiLevel.B.getDexVersion().getBytes());
buffer.putByte(Constants.DEX_FILE_MAGIC_SUFFIX);
buffer.putBytes(bytes);
DexFile dexFile = new DexFile(buffer.asArray());
diff --git a/src/test/java/com/android/tools/r8/dex/Leb128Test.java b/src/test/java/com/android/tools/r8/dex/Leb128Test.java
index 2b6ab9e..4cfefa1 100644
--- a/src/test/java/com/android/tools/r8/dex/Leb128Test.java
+++ b/src/test/java/com/android/tools/r8/dex/Leb128Test.java
@@ -5,6 +5,8 @@
import static com.android.tools.r8.dex.Constants.DEX_MAGIC_SIZE;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.DexVersion;
import com.android.tools.r8.utils.LebUtils;
import java.util.Arrays;
import java.util.Collection;
@@ -48,7 +50,7 @@
private DexFile createDexFileWithContent(byte[] bytes) {
DexOutputBuffer buffer = new DexOutputBuffer();
buffer.putBytes(Constants.DEX_FILE_MAGIC_PREFIX);
- buffer.putBytes(Constants.ANDROID_PRE_N_DEX_VERSION_BYTES);
+ buffer.putBytes(AndroidApiLevel.B.getDexVersion().getBytes());
buffer.putByte(Constants.DEX_FILE_MAGIC_SUFFIX);
buffer.putBytes(bytes);
DexFile dexFile = new DexFile(buffer.asArray());