Revert "Revert "Increment dex version for N api level use""

This reverts commit 272faf45cc60eafc8b91a150e5d7455460cde5b6.

Bug: 27894910
 
Change-Id: Ic7e3acfed0a3f5757ea3bb5d34726564f2a1e385
diff --git a/dexlib/src/main/java/org/jf/dexlib/HeaderItem.java b/dexlib/src/main/java/org/jf/dexlib/HeaderItem.java
index e3f9a0f..249bd68 100644
--- a/dexlib/src/main/java/org/jf/dexlib/HeaderItem.java
+++ b/dexlib/src/main/java/org/jf/dexlib/HeaderItem.java
@@ -40,7 +40,8 @@
      */
     public static final byte[][] MAGIC_VALUES = new byte[][] {
             new byte[] {0x64, 0x65, 0x78, 0x0A, 0x30, 0x33, 0x35, 0x00}, //"dex\n035" + '\0';
-            new byte[] {0x64, 0x65, 0x78, 0x0A, 0x30, 0x33, 0x36, 0x00}}; //"dex\n036" + '\0';
+            new byte[] {0x64, 0x65, 0x78, 0x0A, 0x30, 0x33, 0x36, 0x00}, //"dex\n036" + '\0';
+            new byte[] {0x64, 0x65, 0x78, 0x0A, 0x30, 0x33, 0x37, 0x00}}; //"dex\n037" + '\0';
 
 
     /** size of this section, in bytes */
diff --git a/dx/src/com/android/jack/dx/dex/DexFormat.java b/dx/src/com/android/jack/dx/dex/DexFormat.java
index 1c87cda..2648066 100644
--- a/dx/src/com/android/jack/dx/dex/DexFormat.java
+++ b/dx/src/com/android/jack/dx/dex/DexFormat.java
@@ -27,7 +27,7 @@
    * API level to target in order to produce the most modern file
    * format
    */
-  public static final int API_CURRENT = 14;
+  public static final int API_CURRENT = 24;
 
   /** API level to target in order to suppress extended opcode usage */
   public static final int API_NO_EXTENDED_OPCODES = 13;
@@ -44,8 +44,14 @@
   /** common suffix for all dex file "magic numbers" */
   public static final String MAGIC_SUFFIX = "\0";
 
-  /** dex file version number for the current format variant */
-  public static final String VERSION_CURRENT = "036";
+  /**
+   * Dex file version number for dalvik.
+   * <p>
+   * Note: Dex version 36 was loadable in some versions of Dalvik but was never fully supported or
+   * completed and is not considered a valid dex file format.
+   * </p>
+   */
+  public static final String VERSION_CURRENT = "037";
 
   /** dex file version number for API level 13 and earlier */
   public static final String VERSION_FOR_API_13 = "035";
@@ -88,7 +94,7 @@
     if (version.equals(VERSION_CURRENT)) {
       return API_CURRENT;
     } else if (version.equals(VERSION_FOR_API_13)) {
-      return 13;
+      return API_NO_EXTENDED_OPCODES;
     }
 
     return -1;
@@ -108,4 +114,9 @@
 
     return MAGIC_PREFIX + version + MAGIC_SUFFIX;
   }
+
+  public static boolean isSupportedDexMagic(byte[] magic) {
+    int api = magicToApi(magic);
+    return api == API_NO_EXTENDED_OPCODES || api == API_CURRENT;
+  }
 }
diff --git a/dx/src/com/android/jack/dx/dex/TableOfContents.java b/dx/src/com/android/jack/dx/dex/TableOfContents.java
index 0585c3d8..6eef864 100644
--- a/dx/src/com/android/jack/dx/dex/TableOfContents.java
+++ b/dx/src/com/android/jack/dx/dex/TableOfContents.java
@@ -68,6 +68,7 @@
       encodedArrays,
       annotationsDirectories};
 
+  public int apiLevel;
   public int checksum;
   public byte[] signature;
   public int fileSize;
@@ -88,12 +89,12 @@
 
   private void readHeader(DexBuffer.Section headerIn) {
     byte[] magic = headerIn.readByteArray(8);
-    int apiTarget = DexFormat.magicToApi(magic);
 
-    if (apiTarget != DexFormat.API_NO_EXTENDED_OPCODES) {
+    if (!DexFormat.isSupportedDexMagic(magic)) {
       throw new DexException("Unexpected magic: " + Arrays.toString(magic));
     }
 
+    apiLevel = DexFormat.magicToApi(magic);
     checksum = headerIn.readInt();
     signature = headerIn.readByteArray(20);
     fileSize = headerIn.readInt();
@@ -178,8 +179,8 @@
     throw new IllegalArgumentException("No such map item: " + type);
   }
 
-  public void writeHeader(DexBuffer.Section out) throws IOException {
-    out.write(DexFormat.apiToMagic(DexFormat.API_NO_EXTENDED_OPCODES).getBytes("UTF-8"));
+  public void writeHeader(DexBuffer.Section out, int api) throws IOException {
+    out.write(DexFormat.apiToMagic(api).getBytes("UTF-8"));
     out.writeInt(checksum);
     out.write(signature);
     out.writeInt(fileSize);
diff --git a/jack-tests/tests/com/android/jack/dx/DxTests.java b/jack-tests/tests/com/android/jack/dx/DxTests.java
index dfe4e71..75792d7 100644
--- a/jack-tests/tests/com/android/jack/dx/DxTests.java
+++ b/jack-tests/tests/com/android/jack/dx/DxTests.java
@@ -16,7 +16,9 @@
 
 package com.android.jack.dx;
 
+import com.android.jack.Options;
 import com.android.jack.TestTools;
+import com.android.jack.dx.dex.DexFormat;
 import com.android.jack.test.category.RuntimeRegressionTest;
 import com.android.jack.test.helper.FileChecker;
 import com.android.jack.test.helper.RuntimeTestHelper;
@@ -24,6 +26,11 @@
 import com.android.jack.test.runtime.RuntimeTestInfo;
 import com.android.jack.test.toolchain.AbstractTestTools;
 import com.android.jack.test.toolchain.IToolchain;
+import com.android.jack.test.toolchain.JackApiToolchainBase;
+import com.android.jack.test.toolchain.JackApiV01;
+import com.android.jack.test.toolchain.JackApiV02;
+import com.android.jack.test.toolchain.JackBasedToolchain;
+import com.android.jack.test.toolchain.JillBasedToolchain;
 
 import junit.framework.Assert;
 
@@ -40,8 +47,12 @@
 import org.junit.experimental.categories.Category;
 
 import java.io.File;
+import java.io.FileInputStream;
+import java.util.ArrayList;
+import java.util.List;
 
 import javax.annotation.Nonnull;
+import javax.xml.bind.attachment.AttachmentMarshaller;
 
 public class DxTests extends RuntimeTest {
 
@@ -126,6 +137,56 @@
     Assert.assertTrue(hasOpcode(ci, Opcode.DIV_INT_2ADDR));
   }
 
+  @Test
+  public void testDexVersionIsIncremented() throws Exception {
+
+    String dex37Magic = new String(new byte[] {0x64, 0x65, 0x78, 0x0A, 0x30, 0x33, 0x37, 0x00});
+    String dex35Magic = new String(new byte[] {0x64, 0x65, 0x78, 0x0A, 0x30, 0x33, 0x35, 0x00});
+
+    List<Class<? extends IToolchain>> excludedToolchains =
+        new ArrayList<Class<? extends IToolchain>>();
+    excludedToolchains.add(JillBasedToolchain.class);
+
+    JackApiToolchainBase toolchain =
+        AbstractTestTools.getCandidateToolchain(JackApiV02.class, excludedToolchains);
+
+    File dexOutDir = AbstractTestTools.createTempDir();
+    File outFile = new File(dexOutDir, "classes.dex");
+
+    toolchain.addToClasspath(toolchain.getDefaultBootClasspath());
+    toolchain.addProperty(Options.ANDROID_MIN_API_LEVEL.getName(), "23");
+    toolchain.srcToExe(dexOutDir, /* zipFile = */ false,
+        AbstractTestTools.getTestRootDir("com.android.jack.dx.version"));
+
+    byte[] magic = new byte[8];
+
+    FileInputStream fis = new FileInputStream(outFile);
+    fis.read(magic);
+    fis.close();
+
+    Assert.assertEquals(dex35Magic, new String(magic));
+
+    toolchain =
+        AbstractTestTools.getCandidateToolchain(JackApiV02.class, excludedToolchains);
+
+    dexOutDir = AbstractTestTools.createTempDir();
+    outFile = new File(dexOutDir, "classes.dex");
+
+    toolchain.addToClasspath(toolchain.getDefaultBootClasspath());
+    toolchain.addProperty(Options.ANDROID_MIN_API_LEVEL.getName(), "24");
+    toolchain.srcToExe(dexOutDir, /* zipFile = */ false,
+        AbstractTestTools.getTestRootDir("com.android.jack.dx.version"));
+
+    magic = new byte[8];
+
+    fis = new FileInputStream(outFile);
+    fis.read(magic);
+    fis.close();
+
+    Assert.assertEquals(dex37Magic, new String(magic));
+
+  }
+
   private boolean hasOpcode(@Nonnull CodeItem codeItem, @Nonnull Opcode opcode) {
     for (Instruction inst : codeItem.getInstructions()) {
       if (inst.opcode == opcode) {
diff --git a/jack-tests/tests/com/android/jack/dx/version/C.java b/jack-tests/tests/com/android/jack/dx/version/C.java
new file mode 100644
index 0000000..e09b991
--- /dev/null
+++ b/jack-tests/tests/com/android/jack/dx/version/C.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2016 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.jack.dx.version;
+
+public class C {
+
+  public void m() {
+
+  }
+
+}
+
diff --git a/jack/src/com/android/jack/backend/dex/DexInLibraryWriter.java b/jack/src/com/android/jack/backend/dex/DexInLibraryWriter.java
index 9526d61..e2416ab 100644
--- a/jack/src/com/android/jack/backend/dex/DexInLibraryWriter.java
+++ b/jack/src/com/android/jack/backend/dex/DexInLibraryWriter.java
@@ -63,6 +63,8 @@
 
   private final boolean forceJumbo = ThreadConfig.get(CodeItemBuilder.FORCE_JUMBO).booleanValue();
 
+  private final int apiLevel = ThreadConfig.get(Options.ANDROID_MIN_API_LEVEL).intValue();
+
   private final boolean usePrebuilts =
       ThreadConfig.get(Options.USE_PREBUILT_FROM_LIBRARY).booleanValue();
 
@@ -106,6 +108,7 @@
 
     DexOptions options = new DexOptions();
     options.forceJumbo = forceJumbo;
+    options.targetApiLevel = apiLevel;
     DexFile typeDex = new DexFile(options);
     typeDex.add(cdiMarker.getClassDefItem());
     OutputStream outStream = null;
diff --git a/jack/src/com/android/jack/backend/dex/DexWritingTool.java b/jack/src/com/android/jack/backend/dex/DexWritingTool.java
index a1b6f2c..c5d5c62 100644
--- a/jack/src/com/android/jack/backend/dex/DexWritingTool.java
+++ b/jack/src/com/android/jack/backend/dex/DexWritingTool.java
@@ -74,6 +74,8 @@
 
   private final boolean forceJumbo = ThreadConfig.get(CodeItemBuilder.FORCE_JUMBO).booleanValue();
 
+  private final int apiLevel = ThreadConfig.get(Options.ANDROID_MIN_API_LEVEL).intValue();
+
   private final boolean usePrebuilts =
       ThreadConfig.get(Options.USE_PREBUILT_FROM_LIBRARY).booleanValue();
 
@@ -81,6 +83,7 @@
   protected DexFile createDexFile() {
     DexOptions options = new DexOptions();
     options.forceJumbo = forceJumbo;
+    options.targetApiLevel = apiLevel;
     return new DexFile(options);
   }
 
diff --git a/jack/src/com/android/jack/backend/dex/rop/CodeItemBuilder.java b/jack/src/com/android/jack/backend/dex/rop/CodeItemBuilder.java
index 44c7bb2..79f2d26 100644
--- a/jack/src/com/android/jack/backend/dex/rop/CodeItemBuilder.java
+++ b/jack/src/com/android/jack/backend/dex/rop/CodeItemBuilder.java
@@ -174,6 +174,7 @@
       ThreadConfig.get(Options.EMIT_LOCAL_DEBUG_INFO).booleanValue();
   private final boolean runDxOptimizations = ThreadConfig.get(DEX_OPTIMIZE).booleanValue();
   private final boolean forceJumbo = ThreadConfig.get(FORCE_JUMBO).booleanValue();
+  private final int apiLevel = ThreadConfig.get(Options.ANDROID_MIN_API_LEVEL).intValue();
   private final boolean emitLineNumberTable =
       ThreadConfig.get(Options.EMIT_LINE_NUMBER_DEBUG_INFO).booleanValue();
 
@@ -486,6 +487,7 @@
   private DalvCode createCode(@Nonnull JMethod method, @Nonnull RopMethod ropMethod) {
     DexOptions options = new DexOptions();
     options.forceJumbo = forceJumbo;
+    options.targetApiLevel = apiLevel;
     int paramSize = getParameterWordCount(method);
     int positionListKind;
     LocalVariableInfo lvInfo;