Merge "Fix method index support in Jayce V2" into ub-jack
diff --git a/build.xml b/build.xml
index 1389787..06045f8 100644
--- a/build.xml
+++ b/build.xml
@@ -958,6 +958,11 @@
       <arg line="${jack.static.libs.list}" />
       <arg value="${jack-tests.dist.dir}/${jack-tests.execname}" />
     </java>
+    <jar update="true" destfile="${jack-tests.dist.dir}/${jack-tests.execname}">
+      <metainf dir="${jack.dir}/rsc/META-INF" includes="services/**" />
+      <metainf dir="${jill.dir}/rsc/META-INF" includes="services/**" />
+    </jar>
+
   </target>
 
 
@@ -2298,6 +2303,10 @@
     <istrue value="${tests.console-output}"/>
   </condition>
 
+  <condition property="jack.runtime.version" value="-Druntime.version=${runtime.version}" else="">
+    <isset property="runtime.version"/>
+  </condition>
+
   <filelist id="jack.junit.tests.classpath" dir="/">
     <file name="${jack-tests.dist.dir}/${jack-tests.execname}" />
     <file name="${ddm-lib.dist.dir}/${ddm-lib.libname}" />
@@ -2323,6 +2332,7 @@
       classname="com.android.jack.test.junit.JackJUnitLauncher">
       <jvmarg value="-Dtests.dump=true" />
       <jvmarg value="-Dtests.config=${tests.config}"/>
+      <jvmarg line="${jack.runtime.version}"/>
       <classpath>
        <filelist refid="jack.junit.tests.classpath" />
        <filelist refid="ecj-test-libs"/>
@@ -2338,6 +2348,7 @@
       classname="com.android.jack.test.junit.JackJUnitLauncher">
        <jvmarg value="-Dtests.dump=true" />
        <jvmarg value="-Dtests.config=${tests.config}"/>
+       <jvmarg line="${jack.runtime.version}"/>
        <classpath>
         <filelist refid="jack.junit.tests.classpath"/>
         <filelist refid="ecj-test-libs"/>
@@ -2347,6 +2358,40 @@
 
   </target>
 
+  <target name="test-jack-java8-pre-n-dump" depends="tests-check-config">
+    <mkdir dir="${jack-tests.dir}/dump"/>
+
+    <java fork="true" failonerror="true"
+      output="${jack-tests.dir}/dump/Java8AllTestPreN.js"
+      classname="com.android.jack.test.junit.JackJUnitLauncher">
+      <jvmarg value="-Dtests.dump=true" />
+      <jvmarg value="-Dtests.config=${tests.config}"/>
+      <classpath>
+       <filelist refid="jack.junit.tests.classpath" />
+       <filelist refid="ecj-test-libs"/>
+      </classpath>
+      <arg value="com.android.jack.java8.Java8AllTestPreN"/>
+    </java>
+
+  </target>
+
+  <target name="test-jack-java8-post-m-dump" depends="tests-check-config">
+    <mkdir dir="${jack-tests.dir}/dump"/>
+
+    <java fork="true" failonerror="true"
+      output="${jack-tests.dir}/dump/Java8AllTestPostM.js"
+      classname="com.android.jack.test.junit.JackJUnitLauncher">
+      <jvmarg value="-Dtests.dump=true" />
+      <jvmarg value="-Dtests.config=${tests.config}"/>
+      <classpath>
+       <filelist refid="jack.junit.tests.classpath" />
+       <filelist refid="ecj-test-libs"/>
+      </classpath>
+      <arg value="com.android.jack.java8.Java8AllTestPostM"/>
+    </java>
+
+  </target>
+
   <target name="test-sched-dump" depends="tests-check-config">
     <mkdir dir="${jack-tests.dir}/dump"/>
 
@@ -2375,6 +2420,7 @@
       <jvmarg value="-Dfile.encoding=utf-8" />
       <jvmarg value="-Dtests.config=${tests.config}"/>
       <jvmarg value="${jack.tests.assertions}"/>
+      <jvmarg line="${jack.runtime.version}"/>
       <classpath>
        <filelist refid="jack.junit.tests.classpath" />
        <filelist refid="ecj-test-libs"/>
@@ -2407,6 +2453,7 @@
       <jvmarg value="-Dfile.encoding=utf-8" />
       <jvmarg value="-Dtests.config=${tests.config}"/>
       <jvmarg value="${jack.tests.assertions}"/>
+      <jvmarg line="${jack.runtime.version}"/>
       <classpath>
        <filelist refid="jack.junit.tests.classpath" />
        <filelist refid="ecj-test-libs"/>
@@ -2439,6 +2486,7 @@
        <jvmarg value="-Dfile.encoding=utf-8" />
        <jvmarg value="-Dtests.config=${tests.config}"/>
        <jvmarg value="${jack.tests.assertions}"/>
+       <jvmarg line="${jack.runtime.version}"/>
        <classpath>
         <filelist refid="jack.junit.tests.classpath" />
         <filelist refid="ecj-test-libs"/>
@@ -2471,6 +2519,7 @@
         <jvmarg value="-Dfile.encoding=utf-8" />
         <jvmarg value="-Dtests.config=${tests.config}"/>
         <jvmarg value="${jack.tests.assertions}"/>
+        <jvmarg line="${jack.runtime.version}"/>
         <classpath>
           <filelist refid="jack.junit.tests.classpath" />
           <filelist refid="ecj-test-libs"/>
diff --git a/jack-jacoco-reporter/src/com/android/jack/tools/jacoco/JackCoverageAnalyzer.java b/jack-jacoco-reporter/src/com/android/jack/tools/jacoco/JackCoverageAnalyzer.java
index a3829b3..d9be8f7 100644
--- a/jack-jacoco-reporter/src/com/android/jack/tools/jacoco/JackCoverageAnalyzer.java
+++ b/jack-jacoco-reporter/src/com/android/jack/tools/jacoco/JackCoverageAnalyzer.java
@@ -32,21 +32,28 @@
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.regex.Pattern;
 
+import javax.annotation.CheckForNull;
 import javax.annotation.Nonnull;
 
 /**
  * Code coverage report analyzer.
  */
 public class JackCoverageAnalyzer {
-  @Nonnull
-  private final ExecutionDataStore executionDataStore;
+  @Nonnull private static final String CURRENT_VERSION = "1.0";
 
-  @Nonnull
-  private final ICoverageVisitor coverageVisitor;
+  @Nonnull private static final String JSON_VERSION_ATTRIBUTE = "version";
+
+  @Nonnull private static final String JSON_DATA_ATTRIBUTE = "data";
+
+  @Nonnull private final ExecutionDataStore executionDataStore;
+
+  @Nonnull private final ICoverageVisitor coverageVisitor;
 
   public JackCoverageAnalyzer(
       @Nonnull ExecutionDataStore executionDataStore, @Nonnull ICoverageVisitor coverageVisitor) {
@@ -66,15 +73,52 @@
           "File " + jackCoverageDescriptionFile + " does not exist.");
     }
 
-    JsonReader jsonReader =
-        new JsonReader(new InputStreamReader(new FileInputStream(jackCoverageDescriptionFile)));
+    InputStream is = new FileInputStream(jackCoverageDescriptionFile);
     try {
-      readClasses(jsonReader);
+      analyze(is);
     } finally {
-      jsonReader.close();
+      is.close();
     }
   }
 
+  private void analyze(@Nonnull InputStream coverageDescriptionInputStream) throws IOException {
+    JsonReader jsonReader = new JsonReader(new InputStreamReader(coverageDescriptionInputStream));
+    readMetadata(jsonReader);
+  }
+
+  private void checkVersion(@CheckForNull String version) {
+    if (version == null) {
+      throw new JsonParseException("Missing 'version' attribute before coverage metadadata");
+    }
+    String[] parts = version.split(Pattern.quote("."));
+    if (parts.length != 2) {
+      throw new JsonParseException("Version number format must be x.y");
+    }
+    if (!version.equals(CURRENT_VERSION)) {
+      throw new JsonParseException("Unknown version " + version);
+    }
+  }
+
+  private void readMetadata(@Nonnull JsonReader jsonReader) throws IOException {
+    jsonReader.beginObject();
+
+    String version = null;
+    while (jsonReader.hasNext()) {
+      String attributeName = jsonReader.nextName();
+      if (attributeName.equals(JSON_VERSION_ATTRIBUTE)) {
+        // Reads the version so we can parse the JSON accordingly.
+        version = jsonReader.nextString();
+      } else if (attributeName.equals(JSON_DATA_ATTRIBUTE)) {
+        checkVersion(version);
+        readClasses(jsonReader);
+      } else {
+        jsonReader.skipValue();
+      }
+    }
+
+    jsonReader.endObject();
+  }
+
   private void readClasses(@Nonnull JsonReader jsonReader) throws IOException {
     jsonReader.beginArray();
     while (jsonReader.hasNext()) {
@@ -132,8 +176,9 @@
 
     // Build the class coverage.
     String[] interfacesArray = interfaces.toArray(new String[0]);
-    ClassCoverageImpl c = new ClassCoverageImpl(
-        className, id, noMatch, classSignature, superClassName, interfacesArray);
+    ClassCoverageImpl c =
+        new ClassCoverageImpl(
+            className, id, noMatch, classSignature, superClassName, interfacesArray);
     c.setSourceFileName(sourceFile);
 
     // Update methods with probes.
@@ -174,8 +219,10 @@
   }
 
   // Parses probes.
-  private static void readProbes(@Nonnull JsonReader jsonReader,
-      @Nonnull List<ProbeDescription> probes, @Nonnull List<? extends IMethodCoverage> methods)
+  private static void readProbes(
+      @Nonnull JsonReader jsonReader,
+      @Nonnull List<ProbeDescription> probes,
+      @Nonnull List<? extends IMethodCoverage> methods)
       throws IOException {
     jsonReader.beginArray();
     while (jsonReader.hasNext()) {
diff --git a/jack-tests/.classpath b/jack-tests/.classpath
index 9194025..3203ed2 100644
--- a/jack-tests/.classpath
+++ b/jack-tests/.classpath
@@ -11,7 +11,6 @@
 	<classpathentry combineaccessrules="false" kind="src" path="/Scheduler"/>
 	<classpathentry kind="lib" path="libs/guava-lib.jar"/>
 	<classpathentry kind="lib" path="libs/dex-lib.jar"/>
-	<classpathentry combineaccessrules="false" kind="src" path="/Jack"/>
 	<classpathentry kind="lib" path="libs/ddmlib.jar"/>
 	<classpathentry combineaccessrules="false" kind="src" path="/Dx"/>
 	<classpathentry combineaccessrules="false" kind="src" path="/jack-api"/>
@@ -21,5 +20,6 @@
 	<classpathentry kind="lib" path="prebuilts/org.eclipse.jdt.core_3.12.0.v20150913-1717.jar"/>
 	<classpathentry kind="lib" path="prebuilts/org.eclipse.jdt.core.tests.compiler_3.12.0.v20150913-1717.jar"/>
 	<classpathentry kind="lib" path="prebuilts/org.eclipse.test.performance_3.11.0.v20150223-0658.jar"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/Jack"/>
 	<classpathentry kind="output" path="bin"/>
 </classpath>
diff --git a/jack-tests/src/com/android/jack/test/junit/JackTestRunner.java b/jack-tests/src/com/android/jack/test/junit/JackTestRunner.java
index b5485a6..9fd9cc3 100644
--- a/jack-tests/src/com/android/jack/test/junit/JackTestRunner.java
+++ b/jack-tests/src/com/android/jack/test/junit/JackTestRunner.java
@@ -37,6 +37,9 @@
 
   private boolean dumpTests = false;
 
+  @Nonnull
+  private RuntimeVersion runtimeVersion;
+
   private static class ToolchainFilter extends Filter {
 
     @Nonnull
@@ -47,11 +50,15 @@
 
     private boolean dumpTests = false;
 
-    public ToolchainFilter(
-        @Nonnull IToolchain candidate, @Nonnull IToolchain reference, boolean dumpTest) {
+    @Nonnull
+    private RuntimeVersion runtimeVersion;
+
+    public ToolchainFilter(@Nonnull IToolchain candidate, @Nonnull IToolchain reference,
+        boolean dumpTest, @Nonnull RuntimeVersion runtimeVersion) {
       this.candidate = candidate;
       this.reference = reference;
       this.dumpTests = dumpTest;
+      this.runtimeVersion = runtimeVersion;
     }
 
     @Override
@@ -66,13 +73,17 @@
 
       KnownIssue knownIssueAnnot = description.getAnnotation(KnownIssue.class);
 
-      if (knownIssueAnnot == null) {
-        shouldRun = true;
-      } else {
-        shouldRun = (knownIssueAnnot.candidate().length > 0
-                     || knownIssueAnnot.reference().length > 0)
-                   && (isValidToolchain(candidate, knownIssueAnnot.candidate())
-                       && isValidToolchain(reference, knownIssueAnnot.reference()));
+      MinRuntimeVersion minRuntimeVersion = description.getAnnotation(MinRuntimeVersion.class);
+
+      if (minRuntimeVersion == null || minRuntimeVersion.value().compareTo(runtimeVersion) <= 0) {
+        if (knownIssueAnnot == null) {
+          shouldRun = true;
+        } else {
+          shouldRun = (knownIssueAnnot.candidate().length > 0
+                       || knownIssueAnnot.reference().length > 0)
+                     && (isValidToolchain(candidate, knownIssueAnnot.candidate())
+                         && isValidToolchain(reference, knownIssueAnnot.reference()));
+        }
       }
 
       if (dumpTests && description.getMethodName() != null) {
@@ -104,8 +115,11 @@
 
     dumpTests = Boolean.parseBoolean(System.getProperty("tests.dump", "false"));
 
+    runtimeVersion =
+        RuntimeVersion.valueOf(System.getProperty("runtime.version", "M").toUpperCase());
+
     ToolchainFilter filter = new ToolchainFilter(AbstractTestTools.getCandidateToolchain(),
-        AbstractTestTools.getReferenceToolchain(), dumpTests);
+        AbstractTestTools.getReferenceToolchain(), dumpTests, runtimeVersion);
 
     try {
       filter(filter);
diff --git a/jack-tests/src/com/android/jack/test/junit/MinRuntimeVersion.java b/jack-tests/src/com/android/jack/test/junit/MinRuntimeVersion.java
new file mode 100644
index 0000000..18b8f2d
--- /dev/null
+++ b/jack-tests/src/com/android/jack/test/junit/MinRuntimeVersion.java
@@ -0,0 +1,32 @@
+/*
+ * 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.test.junit;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Tests that required a post M runtime.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE, ElementType.METHOD})
+public @interface MinRuntimeVersion {
+  RuntimeVersion value();
+}
+
diff --git a/jack-tests/src/com/android/jack/test/junit/RuntimeVersion.java b/jack-tests/src/com/android/jack/test/junit/RuntimeVersion.java
new file mode 100644
index 0000000..6a933a3
--- /dev/null
+++ b/jack-tests/src/com/android/jack/test/junit/RuntimeVersion.java
@@ -0,0 +1,36 @@
+/*
+ * 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.test.junit;
+
+/**
+ * Runtime version.
+ */
+public enum RuntimeVersion {
+  D,
+  E,
+  F,
+  G,
+  H,
+  I,
+  J,
+  K,
+  L,
+  M,
+  N,
+  O
+}
+
diff --git a/jack-tests/src/com/android/jack/test/toolchain/AbstractTestTools.java b/jack-tests/src/com/android/jack/test/toolchain/AbstractTestTools.java
index b8b5dbe..5c1e34b 100644
--- a/jack-tests/src/com/android/jack/test/toolchain/AbstractTestTools.java
+++ b/jack-tests/src/com/android/jack/test/toolchain/AbstractTestTools.java
@@ -164,7 +164,8 @@
     @Override
     @Nonnull
     public JackApiV01Toolchain build() {
-      return new JackApiV01Toolchain(getPrebuilt("jack"));
+      File jackPrebuilt = isPrebuiltAvailable("jack") ? getPrebuilt("jack") : null;
+      return new JackApiV01Toolchain(jackPrebuilt);
     }
   }
 
@@ -173,7 +174,8 @@
     @Override
     @Nonnull
     public JackApiV02IncrementalToolchain build() {
-      return new JackApiV02IncrementalToolchain(getPrebuilt("jack"));
+      File jackPrebuilt = isPrebuiltAvailable("jack") ? getPrebuilt("jack") : null;
+      return new JackApiV02IncrementalToolchain(jackPrebuilt);
     }
   }
 
@@ -182,7 +184,8 @@
     @Override
     @Nonnull
     public JackApiV02TwoStepsToolchain build() {
-      return new JackApiV02TwoStepsToolchain(getPrebuilt("jack"));
+      File jackPrebuilt = isPrebuiltAvailable("jack") ? getPrebuilt("jack") : null;
+      return new JackApiV02TwoStepsToolchain(jackPrebuilt);
     }
   }
 
@@ -191,14 +194,15 @@
     @Override
     @Nonnull
     public JackApiV02Toolchain build() {
-      return new JackApiV02Toolchain(getPrebuilt("jack"));
+      File jackPrebuilt = isPrebuiltAvailable("jack") ? getPrebuilt("jack") : null;
+      return new JackApiV02Toolchain(jackPrebuilt);
     }
   }
 
   private static class LegacyJillToolchainBuilder implements ToolchainBuilder {
 
     @Override
-    public IToolchain build() {
+    public LegacyJillToolchain build() {
       return new LegacyJillToolchain(getPrebuilt("legacy-java-compiler"), getPrebuilt("jill"),
           getPrebuilt("jack"), getPrebuilt("jarjar"), getPrebuilt("proguard"));
     }
@@ -206,21 +210,30 @@
 
   private static class JillApiV01ToolchainBuilder implements ToolchainBuilder {
 
-@Override
-    public IToolchain build() {
-      return new JillApiV01Toolchain(getPrebuilt("jill"), getPrebuilt("jack"),
+    @Override
+    public JillApiV01Toolchain build() {
+      File jillPrebuilt = isPrebuiltAvailable("jill") ? getPrebuilt("jill") : null;
+      return new JillApiV01Toolchain(jillPrebuilt, getPrebuilt("jack"),
           getPrebuilt("legacy-java-compiler"), getPrebuilt("jarjar"), getPrebuilt("proguard"));
     }
   }
 
+  public static boolean isPrebuiltAvailable(@Nonnull String prebuiltName) {
+    return !getPrebuiltPath(prebuiltName).equals("");
+  }
+
+  @Nonnull
+  private static String getPrebuiltPath(@Nonnull String prebuiltName) {
+    return TestsProperties.getProperty(TOOLCHAIN_PREBUILT_PREFIX + prebuiltName).trim();
+  }
+
+  @Nonnull
   public static File getPrebuilt(@Nonnull String prebuiltName) {
     String prebuiltVarName = TOOLCHAIN_PREBUILT_PREFIX + prebuiltName;
-    String prebuiltPath;
-
-    prebuiltPath = TestsProperties.getProperty(prebuiltVarName);
+    String prebuiltPath = TestsProperties.getProperty(prebuiltVarName).trim();
 
     if (prebuiltPath.equals("")) {
-      throw new TestConfigurationException("Property '" + prebuiltVarName + "' is not set");
+      throw new AssertionError("Property '" + prebuiltVarName + "' is not set");
     }
 
     File result = new File(prebuiltPath);
@@ -761,14 +774,12 @@
     printProperty(TOOLCHAIN_CANDIDATE_KEY);
     printProperty(TOOLCHAIN_REFERENCE_KEY);
 
-    String prebuiltPath = printProperty(TOOLCHAIN_PREBUILT_PREFIX + "jack");
-    if (!prebuiltPath.equals("")) {
-      System.out.println(TOOLCHAIN_PREBUILT_PREFIX + "jack.version = " + getVersion("jack"));
-    }
-    prebuiltPath = printProperty(TOOLCHAIN_PREBUILT_PREFIX + "jill");
-    if (!prebuiltPath.equals("")) {
-      System.out.println(TOOLCHAIN_PREBUILT_PREFIX + "jill.version = " + getVersion("jill"));
-    }
+    printProperty(TOOLCHAIN_PREBUILT_PREFIX + "jack");
+    System.out.println(TOOLCHAIN_PREBUILT_PREFIX + "jack.version = " + getVersion("jack"));
+
+    printProperty(TOOLCHAIN_PREBUILT_PREFIX + "jill");
+    System.out.println(TOOLCHAIN_PREBUILT_PREFIX + "jill.version = " + getVersion("jill"));
+
     printProperty(TOOLCHAIN_PREBUILT_PREFIX + "jarjar");
     printProperty(TOOLCHAIN_PREBUILT_PREFIX + "proguard");
 
@@ -789,7 +800,7 @@
   }
 
   private static String printProperty(@Nonnull String propertyName) {
-    String value = TestsProperties.getProperty(propertyName);
+    String value = TestsProperties.getProperty(propertyName).trim();
     System.out.println(propertyName + " = " + value);
     return value;
   }
@@ -833,38 +844,60 @@
   @Nonnull
   private static String getVersion(@Nonnull String name) {
 
-    File prebuilt = getPrebuilt(name);
+    String versionFileName = name + "-version.properties";
 
-    if (prebuilt.getName().endsWith(".jar")) {
-      JarFile jarFile = null;
-      try {
-        jarFile = new JarFile(prebuilt);
-        ZipEntry entry = jarFile.getEntry(name + "-version.properties");
-        InputStream is = jarFile.getInputStream(entry);
-        Version version = new Version(is);
+    InputStream is = null;
+    JarFile jarFile = null;
 
-        return version.getVerboseVersion();
+    try {
 
-      } catch (IOException e) {
-        throw new TestConfigurationException(e);
-      } finally {
-        if (jarFile != null) {
-          try {
-            jarFile.close();
-          } catch (IOException e) {
-          }
+      if (isPrebuiltAvailable(name)) {
+        File prebuilt = getPrebuilt(name);
+
+        if (prebuilt.getName().endsWith(".jar")) {
+
+          jarFile = new JarFile(prebuilt);
+          ZipEntry entry = jarFile.getEntry(name + "-version.properties");
+          is = jarFile.getInputStream(entry);
+          Version version = new Version(is);
+
+          return version.getVerboseVersion();
+
+        } else if (prebuilt.getName().endsWith("jack")) {
+          return getServerJackVersion(prebuilt);
+        } else {
+          System.err.println(
+              "Could not fetch version of prebuilt '" + name + "': '" + prebuilt.getName() + "'");
+          return "<unknown>";
+        }
+
+      } else {
+        is = AbstractTestTools.class.getClassLoader().getResourceAsStream(versionFileName);
+
+        if (is == null) {
+          throw new TestConfigurationException("Could not find '" + versionFileName + "'");
+        }
+
+        return new Version(is).getVerboseVersion() + " (found on classpath)";
+      }
+    } catch (IOException e) {
+      throw new TestConfigurationException(e);
+    } finally {
+      if (is != null) {
+        try {
+          is.close();
+        } catch (IOException e) {
         }
       }
-    } else if (name.endsWith("jack")) {
-      return getServerJackVersion(prebuilt);
-    } else {
-      System.err.println("Could not fetch version of prebuilt '" + name + "'");
-      return "<unknown>";
+      if (jarFile != null) {
+        try {
+          jarFile.close();
+        } catch (IOException e) {
+        }
+      }
     }
-
   }
 
-
   private static String getServerJackVersion(@Nonnull File jackScript) {
 
     String[] arguments = new String[2];
diff --git a/jack-tests/src/com/android/jack/test/toolchain/JackApiToolchainBase.java b/jack-tests/src/com/android/jack/test/toolchain/JackApiToolchainBase.java
index 615e116..1550247 100644
--- a/jack-tests/src/com/android/jack/test/toolchain/JackApiToolchainBase.java
+++ b/jack-tests/src/com/android/jack/test/toolchain/JackApiToolchainBase.java
@@ -86,19 +86,40 @@
     return compilerVersion;
   }
 
-  protected <T extends JackConfig> JackApiToolchainBase(@Nonnull File jackPrebuilt,
+  protected <T extends JackConfig> JackApiToolchainBase(@CheckForNull File jackPrebuilt,
       @Nonnull Class<T> jackConfig) {
 
     try {
-      ClassLoader classLoader = URLClassLoader.newInstance(new URL[] {jackPrebuilt.toURI().toURL()},
-          JackApiToolchainBase.class.getClassLoader());
-      ServiceLoader<JackProvider> serviceLoader =
-          ServiceLoader.load(JackProvider.class, classLoader);
+      ClassLoader classLoader = null;
+
+      ServiceLoader<JackProvider> serviceLoader;
+
+      if (jackPrebuilt != null) {
+        classLoader = URLClassLoader.newInstance(
+            new URL[] {jackPrebuilt.toURI().toURL()}, JackApiToolchainBase.class.getClassLoader());
+        serviceLoader = ServiceLoader.load(JackProvider.class, classLoader);
+      } else {
+        serviceLoader = ServiceLoader.load(JackProvider.class);
+      }
+
       configProvider = serviceLoader.iterator().next();
+
+      if (jackPrebuilt != null && configProvider.getClass().getClassLoader() != classLoader) {
+        throw new TestConfigurationException("Jack compiler is not loaded from '" + jackPrebuilt
+            + "'. Unset jack prebuilt property in configuration file");
+      }
+
     } catch (MalformedURLException e1) {
       throw new TestConfigurationException(e1);
     } catch (NoSuchElementException e) {
-      throw new TestConfigurationException(e);
+      if (jackPrebuilt == null) {
+        throw new TestConfigurationException(
+            "JackProvider could not be loaded. Ensure Jack is present on classpath or prebuilt is"
+            + " specified in configuration file", e);
+      } else {
+        throw new TestConfigurationException(e);
+      }
+
     }
 
     assert configProvider != null;
diff --git a/jack-tests/src/com/android/jack/test/toolchain/JackApiV01Toolchain.java b/jack-tests/src/com/android/jack/test/toolchain/JackApiV01Toolchain.java
index ed00241..baf6808 100644
--- a/jack-tests/src/com/android/jack/test/toolchain/JackApiV01Toolchain.java
+++ b/jack-tests/src/com/android/jack/test/toolchain/JackApiV01Toolchain.java
@@ -37,6 +37,7 @@
 import java.util.Collections;
 import java.util.List;
 
+import javax.annotation.CheckForNull;
 import javax.annotation.Nonnull;
 
 /**
@@ -47,7 +48,7 @@
   @Nonnull
   private Api01Config apiV01Config;
 
-  JackApiV01Toolchain(@Nonnull File jackPrebuilt) {
+  JackApiV01Toolchain(@CheckForNull File jackPrebuilt) {
     super(jackPrebuilt, Api01Config.class);
     apiV01Config = (Api01Config) config;
     addProperty(Options.USE_DEFAULT_LIBRARIES.getName(), "false");
diff --git a/jack-tests/src/com/android/jack/test/toolchain/JackApiV02IncrementalToolchain.java b/jack-tests/src/com/android/jack/test/toolchain/JackApiV02IncrementalToolchain.java
index 97d74f5..044599b 100644
--- a/jack-tests/src/com/android/jack/test/toolchain/JackApiV02IncrementalToolchain.java
+++ b/jack-tests/src/com/android/jack/test/toolchain/JackApiV02IncrementalToolchain.java
@@ -20,6 +20,7 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import javax.annotation.CheckForNull;
 import javax.annotation.Nonnull;
 
 /**
@@ -30,7 +31,7 @@
 public class JackApiV02IncrementalToolchain
     extends JackApiV02Toolchain implements IncrementalToolchain {
 
-  JackApiV02IncrementalToolchain(@Nonnull File jackPrebuilt) {
+  JackApiV02IncrementalToolchain(@CheckForNull File jackPrebuilt) {
     super(jackPrebuilt);
   }
 
diff --git a/jack-tests/src/com/android/jack/test/toolchain/JillApiToolchainBase.java b/jack-tests/src/com/android/jack/test/toolchain/JillApiToolchainBase.java
index f0eafa4..fb2e3c5 100644
--- a/jack-tests/src/com/android/jack/test/toolchain/JillApiToolchainBase.java
+++ b/jack-tests/src/com/android/jack/test/toolchain/JillApiToolchainBase.java
@@ -57,22 +57,45 @@
   private String compilerVersion;
 
 
-  protected <T extends JillConfig> JillApiToolchainBase(@Nonnull File jillPrebuilt,
+  protected <T extends JillConfig> JillApiToolchainBase(@CheckForNull File jillPrebuilt,
       @Nonnull File jackPrebuilt, @Nonnull Class<T> jillConfig, @Nonnull File refCompilerPrebuilt,
       @Nonnull File jarjarPrebuilt, @Nonnull File proguardPrebuilt) {
-    super(jillPrebuilt, jackPrebuilt, refCompilerPrebuilt, jarjarPrebuilt, proguardPrebuilt);
+    super(jackPrebuilt, refCompilerPrebuilt, jarjarPrebuilt, proguardPrebuilt);
 
     if (configProvider == null) {
       try {
-        ClassLoader classLoader = URLClassLoader.newInstance(
-            new URL[] {jillPrebuilt.toURI().toURL()}, JillApiToolchainBase.class.getClassLoader());
-        ServiceLoader<JillProvider> serviceLoader =
-            ServiceLoader.load(JillProvider.class, classLoader);
+        ClassLoader classLoader = null;
+
+        ServiceLoader<JillProvider> serviceLoader;
+
+        if (jillPrebuilt != null) {
+          classLoader = URLClassLoader.newInstance(
+              new URL[] {jillPrebuilt.toURI().toURL()},
+              JillApiToolchainBase.class.getClassLoader());
+          serviceLoader = ServiceLoader.load(JillProvider.class, classLoader);
+        } else {
+          serviceLoader = ServiceLoader.load(JillProvider.class);
+        }
+
         configProvider = serviceLoader.iterator().next();
+
+        assert configProvider != null;
+
+        if (jillPrebuilt != null && configProvider.getClass().getClassLoader() != classLoader) {
+          throw new TestConfigurationException("Jill is not loaded from '" + jillPrebuilt
+              + "'. Unset jill prebuilt property in configuration file.");
+        }
+
       } catch (MalformedURLException e1) {
         throw new TestConfigurationException(e1);
       } catch (NoSuchElementException e) {
-        throw new TestConfigurationException(e);
+        if (jillPrebuilt == null) {
+          throw new TestConfigurationException(
+              "JillProvider could not be loaded. Ensure Jill is present on classpath or prebuilt"
+              + " is specified in configuration file", e);
+        } else {
+          throw new TestConfigurationException(e);
+        }
       }
     }
 
diff --git a/jack-tests/src/com/android/jack/test/toolchain/JillApiV01Toolchain.java b/jack-tests/src/com/android/jack/test/toolchain/JillApiV01Toolchain.java
index 15f3ef1..fdf7fe4 100644
--- a/jack-tests/src/com/android/jack/test/toolchain/JillApiV01Toolchain.java
+++ b/jack-tests/src/com/android/jack/test/toolchain/JillApiV01Toolchain.java
@@ -23,6 +23,7 @@
 
 import java.io.File;
 
+import javax.annotation.CheckForNull;
 import javax.annotation.Nonnull;
 
 /**
@@ -33,7 +34,7 @@
   @Nonnull
   private Api01Config apiV01Config;
 
-  JillApiV01Toolchain(@Nonnull File jillPrebuilt, @Nonnull File jackPrebuilt,
+  JillApiV01Toolchain(@CheckForNull File jillPrebuilt, @Nonnull File jackPrebuilt,
       @Nonnull File refCompilerPrebuilt, @Nonnull File jarjarPrebuilt,
       @Nonnull File proguardPrebuilt) {
     super(jillPrebuilt, jackPrebuilt, Api01Config.class, refCompilerPrebuilt, jarjarPrebuilt,
diff --git a/jack-tests/src/com/android/jack/test/toolchain/JillBasedToolchain.java b/jack-tests/src/com/android/jack/test/toolchain/JillBasedToolchain.java
index b2a9648..7ace762 100644
--- a/jack-tests/src/com/android/jack/test/toolchain/JillBasedToolchain.java
+++ b/jack-tests/src/com/android/jack/test/toolchain/JillBasedToolchain.java
@@ -45,19 +45,16 @@
   @Nonnull
   private static final String RSC_DIR = "rsc";
   @Nonnull
-  protected File jillPrebuilt;
-  @Nonnull
   private File refCompilerPrebuilt;
   @Nonnull
   private File jarjarPrebuilt;
   @Nonnull
   private File proguardPrebuilt;
 
-  JillBasedToolchain(@Nonnull File jillPrebuilt, @Nonnull File jackPrebuilt,
+  JillBasedToolchain(@Nonnull File jackPrebuilt,
       @Nonnull File refCompilerPrebuilt, @Nonnull File jarjarPrebuilt,
       @Nonnull File proguardPrebuilt) {
     super(jackPrebuilt);
-    this.jillPrebuilt = jillPrebuilt;
     this.refCompilerPrebuilt = refCompilerPrebuilt;
     this.jarjarPrebuilt = jarjarPrebuilt;
     this.proguardPrebuilt = proguardPrebuilt;
diff --git a/jack-tests/src/com/android/jack/test/toolchain/LegacyJillToolchain.java b/jack-tests/src/com/android/jack/test/toolchain/LegacyJillToolchain.java
index f2ceacd..038657f 100644
--- a/jack-tests/src/com/android/jack/test/toolchain/LegacyJillToolchain.java
+++ b/jack-tests/src/com/android/jack/test/toolchain/LegacyJillToolchain.java
@@ -30,9 +30,13 @@
  */
 public class LegacyJillToolchain extends JillBasedToolchain {
 
+  @Nonnull
+  private File jillPrebuilt;
+
   public LegacyJillToolchain(@Nonnull File refCompilerPrebuilt, @Nonnull File jillPrebuilt,
       @Nonnull File jackPrebuilt, @Nonnull File jarjarPrebuilt, @Nonnull File proguardPrebuilt) {
-    super(jillPrebuilt, jackPrebuilt, refCompilerPrebuilt, jarjarPrebuilt, proguardPrebuilt);
+    super(jackPrebuilt, refCompilerPrebuilt, jarjarPrebuilt, proguardPrebuilt);
+    this.jillPrebuilt = jillPrebuilt;
   }
 
   @Override
diff --git a/jack-tests/tests/com/android/jack/AllTests.java b/jack-tests/tests/com/android/jack/AllTests.java
index 7842b68..4f40777 100644
--- a/jack-tests/tests/com/android/jack/AllTests.java
+++ b/jack-tests/tests/com/android/jack/AllTests.java
@@ -39,6 +39,8 @@
 import com.android.jack.invoke.InvokeTests;
 import com.android.jack.jarjar.JarjarTests;
 import com.android.jack.java7.Java7AllTest;
+import com.android.jack.java8.Java8AllTest;
+import com.android.jack.java8.Java8AllTestPreN;
 import com.android.jack.label.LabelTest;
 import com.android.jack.library.LibraryTests;
 import com.android.jack.lookup.LookupTests;
diff --git a/jack-tests/tests/com/android/jack/java8/DefaultMethodTest.java b/jack-tests/tests/com/android/jack/java8/DefaultMethodTest.java
index 43e4ceb..8aefce1 100644
--- a/jack-tests/tests/com/android/jack/java8/DefaultMethodTest.java
+++ b/jack-tests/tests/com/android/jack/java8/DefaultMethodTest.java
@@ -16,13 +16,6 @@
 
 package com.android.jack.java8;
 
-import java.io.File;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import javax.annotation.Nonnull;
-
 import org.jf.dexlib.ClassDataItem.EncodedMethod;
 import org.jf.dexlib.ClassDefItem;
 import org.jf.dexlib.DexFile;
@@ -38,6 +31,13 @@
 import com.android.jack.test.toolchain.JillBasedToolchain;
 import com.android.jack.test.toolchain.Toolchain.SourceLevel;
 
+import java.io.File;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.annotation.Nonnull;
+
 
 /**
  * JUnit test for compilation of default method.
diff --git a/jack-tests/tests/com/android/jack/java8/EcjLambdaTest.java b/jack-tests/tests/com/android/jack/java8/EcjLambdaTest.java
index 963c665..155332f 100644
--- a/jack-tests/tests/com/android/jack/java8/EcjLambdaTest.java
+++ b/jack-tests/tests/com/android/jack/java8/EcjLambdaTest.java
@@ -60,7 +60,8 @@
           @Override
           public boolean shouldRun(Description description) {
             if (testWithApiUsage.contains(description.getMethodName())
-                || testWithOtherErrorMsg.contains(description.getMethodName())) {
+                || testWithOtherErrorMsg.contains(description.getMethodName())
+                || EcjLambdaTestPostM.testForNewRuntime.contains(description.getMethodName())) {
               return false;
             }
             return true;
diff --git a/jack-tests/tests/com/android/jack/java8/EcjLambdaTestPostM.java b/jack-tests/tests/com/android/jack/java8/EcjLambdaTestPostM.java
new file mode 100644
index 0000000..7def70f
--- /dev/null
+++ b/jack-tests/tests/com/android/jack/java8/EcjLambdaTestPostM.java
@@ -0,0 +1,75 @@
+/*
+ * 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.java8;
+
+import junit.framework.Assert;
+import junit.framework.JUnit4TestAdapter;
+import junit.framework.Test;
+
+import org.junit.runner.Description;
+import org.junit.runner.manipulation.Filter;
+import org.junit.runner.manipulation.NoTestsRemainException;
+
+import java.util.Arrays;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+
+public class EcjLambdaTestPostM extends EcjLambdaTest {
+
+  public EcjLambdaTestPostM(@Nonnull String name) {
+    super(name);
+  }
+
+  public static final List<String> testForNewRuntime =
+      Arrays.asList("testReferenceExpressionInference1",
+          "testReferenceExpressionInference2",
+          "testReferenceExpressionInference3a",
+          "test425152",
+          "test431514",
+          "test431514a",
+          "test421712",
+          "test406744d");
+
+  public static class MyAdapter extends JUnit4TestAdapter {
+
+    public MyAdapter(Class<?> newTestClass) {
+      super(newTestClass);
+      try {
+        filter(new Filter() {
+          @Override
+          public boolean shouldRun(Description description) {
+            return testForNewRuntime.contains(description.getMethodName());
+          }
+
+          @Override
+          public String describe() {
+            return "EcjLambdaTestForNewRuntime";
+          }
+        });
+      } catch (NoTestsRemainException e) {
+        Assert.fail();
+      }
+    }
+  }
+
+  public static Test suite() {
+    return new MyAdapter(EcjLambdaTest.class);
+   }
+
+}
+
diff --git a/jack-tests/tests/com/android/jack/java8/GwtTest.java b/jack-tests/tests/com/android/jack/java8/GwtTest.java
index dc7efb1..35a4988 100644
--- a/jack-tests/tests/com/android/jack/java8/GwtTest.java
+++ b/jack-tests/tests/com/android/jack/java8/GwtTest.java
@@ -21,7 +21,6 @@
 import javax.annotation.Nonnull;
 
 import com.android.jack.test.helper.RuntimeTestHelper;
-import com.android.jack.test.junit.KnownIssue;
 import com.android.jack.test.runtime.RuntimeTestInfo;
 import com.android.jack.test.toolchain.AbstractTestTools;
 import com.android.jack.test.toolchain.JackApiV01;
@@ -143,62 +142,6 @@
       AbstractTestTools.getTestRootDir("com.android.jack.java8.gwt.test027"),
       "com.android.jack.java8.gwt.test027.jack.Java8Test");
 
-  private RuntimeTestInfo GWT_LAMBDA_TEST_28 = new RuntimeTestInfo(
-      AbstractTestTools.getTestRootDir("com.android.jack.java8.gwt.test028"),
-      "com.android.jack.java8.gwt.test028.jack.Java8Test");
-
-  private RuntimeTestInfo GWT_LAMBDA_TEST_29 = new RuntimeTestInfo(
-      AbstractTestTools.getTestRootDir("com.android.jack.java8.gwt.test029"),
-      "com.android.jack.java8.gwt.test029.jack.Java8Test");
-
-  private RuntimeTestInfo GWT_LAMBDA_TEST_30 = new RuntimeTestInfo(
-      AbstractTestTools.getTestRootDir("com.android.jack.java8.gwt.test030"),
-      "com.android.jack.java8.gwt.test030.jack.Java8Test");
-
-  private RuntimeTestInfo GWT_LAMBDA_TEST_31 = new RuntimeTestInfo(
-      AbstractTestTools.getTestRootDir("com.android.jack.java8.gwt.test031"),
-      "com.android.jack.java8.gwt.test031.jack.Java8Test");
-
-  private RuntimeTestInfo GWT_LAMBDA_TEST_32 = new RuntimeTestInfo(
-      AbstractTestTools.getTestRootDir("com.android.jack.java8.gwt.test032"),
-      "com.android.jack.java8.gwt.test032.jack.Java8Test");
-
-  private RuntimeTestInfo GWT_LAMBDA_TEST_33 = new RuntimeTestInfo(
-      AbstractTestTools.getTestRootDir("com.android.jack.java8.gwt.test033"),
-      "com.android.jack.java8.gwt.test033.jack.Java8Test");
-
-  private RuntimeTestInfo GWT_LAMBDA_TEST_34 = new RuntimeTestInfo(
-      AbstractTestTools.getTestRootDir("com.android.jack.java8.gwt.test034"),
-      "com.android.jack.java8.gwt.test034.jack.Java8Test");
-
-  private RuntimeTestInfo GWT_LAMBDA_TEST_35 = new RuntimeTestInfo(
-      AbstractTestTools.getTestRootDir("com.android.jack.java8.gwt.test035"),
-      "com.android.jack.java8.gwt.test035.jack.Java8Test");
-
-  private RuntimeTestInfo GWT_LAMBDA_TEST_36 = new RuntimeTestInfo(
-      AbstractTestTools.getTestRootDir("com.android.jack.java8.gwt.test036"),
-      "com.android.jack.java8.gwt.test036.jack.Java8Test");
-
-  private RuntimeTestInfo GWT_LAMBDA_TEST_37 = new RuntimeTestInfo(
-      AbstractTestTools.getTestRootDir("com.android.jack.java8.gwt.test037"),
-      "com.android.jack.java8.gwt.test037.jack.Java8Test");
-
-  private RuntimeTestInfo GWT_LAMBDA_TEST_38 = new RuntimeTestInfo(
-      AbstractTestTools.getTestRootDir("com.android.jack.java8.gwt.test038"),
-      "com.android.jack.java8.gwt.test038.jack.Java8Test");
-
-  private RuntimeTestInfo GWT_LAMBDA_TEST_39 = new RuntimeTestInfo(
-      AbstractTestTools.getTestRootDir("com.android.jack.java8.gwt.test039"),
-      "com.android.jack.java8.gwt.test039.jack.Java8Test");
-
-  private RuntimeTestInfo GWT_LAMBDA_TEST_40 = new RuntimeTestInfo(
-      AbstractTestTools.getTestRootDir("com.android.jack.java8.gwt.test040"),
-      "com.android.jack.java8.gwt.test040.jack.Java8Test");
-
-  private RuntimeTestInfo GWT_LAMBDA_TEST_41 = new RuntimeTestInfo(
-      AbstractTestTools.getTestRootDir("com.android.jack.java8.gwt.test041"),
-      "com.android.jack.java8.gwt.test041.jack.Java8Test");
-
   private RuntimeTestInfo GWT_LAMBDA_TEST_42 = new RuntimeTestInfo(
       AbstractTestTools.getTestRootDir("com.android.jack.java8.gwt.test042"),
       "com.android.jack.java8.gwt.test042.jack.Java8Test");
@@ -219,26 +162,6 @@
       AbstractTestTools.getTestRootDir("com.android.jack.java8.gwt.test046"),
       "com.android.jack.java8.gwt.test046.jack.Java8Test");
 
-  private RuntimeTestInfo GWT_LAMBDA_TEST_47 = new RuntimeTestInfo(
-      AbstractTestTools.getTestRootDir("com.android.jack.java8.gwt.test047"),
-      "com.android.jack.java8.gwt.test047.jack.Java8Test");
-
-  private RuntimeTestInfo GWT_LAMBDA_TEST_48 = new RuntimeTestInfo(
-      AbstractTestTools.getTestRootDir("com.android.jack.java8.gwt.test048"),
-      "com.android.jack.java8.gwt.test048.jack.Java8Test");
-
-  private RuntimeTestInfo GWT_LAMBDA_TEST_49 = new RuntimeTestInfo(
-      AbstractTestTools.getTestRootDir("com.android.jack.java8.gwt.test049"),
-      "com.android.jack.java8.gwt.test049.jack.Java8Test");
-
-  private RuntimeTestInfo GWT_LAMBDA_TEST_50 = new RuntimeTestInfo(
-      AbstractTestTools.getTestRootDir("com.android.jack.java8.gwt.test050"),
-      "com.android.jack.java8.gwt.test050.jack.Java8Test");
-
-  private RuntimeTestInfo GWT_LAMBDA_TEST_51 = new RuntimeTestInfo(
-      AbstractTestTools.getTestRootDir("com.android.jack.java8.gwt.test051"),
-      "com.android.jack.java8.gwt.test051.jack.Java8Test");
-
   @Test
   public void testLambdaNoCapture() throws Exception {
     run(GWT_LAMBDA_TEST_1);
@@ -375,77 +298,6 @@
   }
 
   @Test
-  public void testDefaultInterfaceMethod() throws Exception {
-    run(GWT_LAMBDA_TEST_28);
-  }
-
-  @Test
-  @KnownIssue
-  public void testDefaultInterfaceMethodVirtualUpRef() throws Exception {
-    run(GWT_LAMBDA_TEST_29);
-  }
-
-  @Test
-  public void DefaultInterfaceImplVirtualUpRefTwoInterfaces() throws Exception {
-    run(GWT_LAMBDA_TEST_30);
-  }
-
-  @Test
-  public void testDefenderMethodByInterfaceInstance() throws Exception {
-    run(GWT_LAMBDA_TEST_31);
-  }
-
-  @Test
-  public void testDefaultMethodReference() throws Exception {
-    run(GWT_LAMBDA_TEST_32);
-  }
-
-  @Test
-  public void testThisRefInDefenderMethod() throws Exception {
-    run(GWT_LAMBDA_TEST_33);
-  }
-
-  @Test
-  public void testClassImplementsTwoInterfacesWithSameDefenderMethod() throws Exception {
-    run(GWT_LAMBDA_TEST_34);
-  }
-
-  @Test
-  public void testAbstractClassImplementsInterface() throws Exception {
-    run(GWT_LAMBDA_TEST_35);
-  }
-
-  @Test
-  public void testSuperRefInDefenderMethod() throws Exception {
-    run(GWT_LAMBDA_TEST_36);
-  }
-
-  @Test
-  public void testSuperThisRefsInDefenderMethod() throws Exception {
-    run(GWT_LAMBDA_TEST_37);
-  }
-
-  @Test
-  public void testNestedInterfaceClass() throws Exception {
-    run(GWT_LAMBDA_TEST_38);
-  }
-
-  @Test
-  public void testBaseIntersectionCast() throws Exception {
-    run(GWT_LAMBDA_TEST_39);
-  }
-
-  @Test
-  public void testIntersectionCastWithLambdaExpr() throws Exception {
-    run(GWT_LAMBDA_TEST_40);
-  }
-
-  @Test
-  public void testIntersectionCastPolymorphism() throws Exception {
-    run(GWT_LAMBDA_TEST_41);
-  }
-
-  @Test
   public void testLambdaNestingInAnonymousCaptureLocal() throws Exception {
     run(GWT_LAMBDA_TEST_42);
   }
@@ -470,31 +322,6 @@
     run(GWT_LAMBDA_TEST_46);
   }
 
-  @Test
-  public void testMultipleDefaults_fromInterfaces_left() throws Exception {
-    run(GWT_LAMBDA_TEST_47);
-  }
-
-  @Test
-  public void testMultipleDefaults_fromInterfaces_right() throws Exception {
-    run(GWT_LAMBDA_TEST_48);
-  }
-
-  @Test
-  public void testMultipleDefaults_superclass_left() throws Exception {
-    run(GWT_LAMBDA_TEST_49);
-  }
-
-  @Test
-  public void testMultipleDefaults_superclass_right() throws Exception {
-    run(GWT_LAMBDA_TEST_50);
-  }
-
-  @Test
-  public void testInterfaceThis() throws Exception {
-    run(GWT_LAMBDA_TEST_51);
-  }
-
   private void run(@Nonnull RuntimeTestInfo rti) throws Exception {
     new RuntimeTestHelper(rti)
         .setSourceLevel(SourceLevel.JAVA_8)
diff --git a/jack-tests/tests/com/android/jack/java8/GwtTestPostM.java b/jack-tests/tests/com/android/jack/java8/GwtTestPostM.java
new file mode 100644
index 0000000..1007c3f
--- /dev/null
+++ b/jack-tests/tests/com/android/jack/java8/GwtTestPostM.java
@@ -0,0 +1,216 @@
+/*
+ * 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.java8;
+
+import com.android.jack.test.helper.RuntimeTestHelper;
+import com.android.jack.test.junit.KnownIssue;
+import com.android.jack.test.runtime.RuntimeTestInfo;
+import com.android.jack.test.toolchain.AbstractTestTools;
+import com.android.jack.test.toolchain.JillBasedToolchain;
+import com.android.jack.test.toolchain.Toolchain.SourceLevel;
+
+import org.junit.Test;
+
+import javax.annotation.Nonnull;
+
+
+
+/**
+ * JUnit test for Java 8 forked from GWT. These tests require a post N runtime.
+ */
+public class GwtTestPostM {
+
+  private RuntimeTestInfo GWT_LAMBDA_TEST_28 = new RuntimeTestInfo(
+      AbstractTestTools.getTestRootDir("com.android.jack.java8.gwt.test028"),
+      "com.android.jack.java8.gwt.test028.jack.Java8Test");
+
+  private RuntimeTestInfo GWT_LAMBDA_TEST_29 = new RuntimeTestInfo(
+      AbstractTestTools.getTestRootDir("com.android.jack.java8.gwt.test029"),
+      "com.android.jack.java8.gwt.test029.jack.Java8Test");
+
+  private RuntimeTestInfo GWT_LAMBDA_TEST_30 = new RuntimeTestInfo(
+      AbstractTestTools.getTestRootDir("com.android.jack.java8.gwt.test030"),
+      "com.android.jack.java8.gwt.test030.jack.Java8Test");
+
+  private RuntimeTestInfo GWT_LAMBDA_TEST_31 = new RuntimeTestInfo(
+      AbstractTestTools.getTestRootDir("com.android.jack.java8.gwt.test031"),
+      "com.android.jack.java8.gwt.test031.jack.Java8Test");
+
+  private RuntimeTestInfo GWT_LAMBDA_TEST_32 = new RuntimeTestInfo(
+      AbstractTestTools.getTestRootDir("com.android.jack.java8.gwt.test032"),
+      "com.android.jack.java8.gwt.test032.jack.Java8Test");
+
+  private RuntimeTestInfo GWT_LAMBDA_TEST_33 = new RuntimeTestInfo(
+      AbstractTestTools.getTestRootDir("com.android.jack.java8.gwt.test033"),
+      "com.android.jack.java8.gwt.test033.jack.Java8Test");
+
+  private RuntimeTestInfo GWT_LAMBDA_TEST_34 = new RuntimeTestInfo(
+      AbstractTestTools.getTestRootDir("com.android.jack.java8.gwt.test034"),
+      "com.android.jack.java8.gwt.test034.jack.Java8Test");
+
+  private RuntimeTestInfo GWT_LAMBDA_TEST_35 = new RuntimeTestInfo(
+      AbstractTestTools.getTestRootDir("com.android.jack.java8.gwt.test035"),
+      "com.android.jack.java8.gwt.test035.jack.Java8Test");
+
+  private RuntimeTestInfo GWT_LAMBDA_TEST_36 = new RuntimeTestInfo(
+      AbstractTestTools.getTestRootDir("com.android.jack.java8.gwt.test036"),
+      "com.android.jack.java8.gwt.test036.jack.Java8Test");
+
+  private RuntimeTestInfo GWT_LAMBDA_TEST_37 = new RuntimeTestInfo(
+      AbstractTestTools.getTestRootDir("com.android.jack.java8.gwt.test037"),
+      "com.android.jack.java8.gwt.test037.jack.Java8Test");
+
+  private RuntimeTestInfo GWT_LAMBDA_TEST_38 = new RuntimeTestInfo(
+      AbstractTestTools.getTestRootDir("com.android.jack.java8.gwt.test038"),
+      "com.android.jack.java8.gwt.test038.jack.Java8Test");
+
+  private RuntimeTestInfo GWT_LAMBDA_TEST_39 = new RuntimeTestInfo(
+      AbstractTestTools.getTestRootDir("com.android.jack.java8.gwt.test039"),
+      "com.android.jack.java8.gwt.test039.jack.Java8Test");
+
+  private RuntimeTestInfo GWT_LAMBDA_TEST_40 = new RuntimeTestInfo(
+      AbstractTestTools.getTestRootDir("com.android.jack.java8.gwt.test040"),
+      "com.android.jack.java8.gwt.test040.jack.Java8Test");
+
+  private RuntimeTestInfo GWT_LAMBDA_TEST_41 = new RuntimeTestInfo(
+      AbstractTestTools.getTestRootDir("com.android.jack.java8.gwt.test041"),
+      "com.android.jack.java8.gwt.test041.jack.Java8Test");
+
+  private RuntimeTestInfo GWT_LAMBDA_TEST_47 = new RuntimeTestInfo(
+      AbstractTestTools.getTestRootDir("com.android.jack.java8.gwt.test047"),
+      "com.android.jack.java8.gwt.test047.jack.Java8Test");
+
+  private RuntimeTestInfo GWT_LAMBDA_TEST_48 = new RuntimeTestInfo(
+      AbstractTestTools.getTestRootDir("com.android.jack.java8.gwt.test048"),
+      "com.android.jack.java8.gwt.test048.jack.Java8Test");
+
+  private RuntimeTestInfo GWT_LAMBDA_TEST_49 = new RuntimeTestInfo(
+      AbstractTestTools.getTestRootDir("com.android.jack.java8.gwt.test049"),
+      "com.android.jack.java8.gwt.test049.jack.Java8Test");
+
+  private RuntimeTestInfo GWT_LAMBDA_TEST_50 = new RuntimeTestInfo(
+      AbstractTestTools.getTestRootDir("com.android.jack.java8.gwt.test050"),
+      "com.android.jack.java8.gwt.test050.jack.Java8Test");
+
+  private RuntimeTestInfo GWT_LAMBDA_TEST_51 = new RuntimeTestInfo(
+      AbstractTestTools.getTestRootDir("com.android.jack.java8.gwt.test051"),
+      "com.android.jack.java8.gwt.test051.jack.Java8Test");
+
+  @Test
+  public void testDefaultInterfaceMethod() throws Exception {
+    run(GWT_LAMBDA_TEST_28);
+  }
+
+  @Test
+  @KnownIssue
+  public void testDefaultInterfaceMethodVirtualUpRef() throws Exception {
+    run(GWT_LAMBDA_TEST_29);
+  }
+
+  @Test
+  public void DefaultInterfaceImplVirtualUpRefTwoInterfaces() throws Exception {
+    run(GWT_LAMBDA_TEST_30);
+  }
+
+  @Test
+  public void testDefenderMethodByInterfaceInstance() throws Exception {
+    run(GWT_LAMBDA_TEST_31);
+  }
+
+  @Test
+  public void testDefaultMethodReference() throws Exception {
+    run(GWT_LAMBDA_TEST_32);
+  }
+
+  @Test
+  public void testThisRefInDefenderMethod() throws Exception {
+    run(GWT_LAMBDA_TEST_33);
+  }
+
+  @Test
+  public void testClassImplementsTwoInterfacesWithSameDefenderMethod() throws Exception {
+    run(GWT_LAMBDA_TEST_34);
+  }
+
+  @Test
+  public void testAbstractClassImplementsInterface() throws Exception {
+    run(GWT_LAMBDA_TEST_35);
+  }
+
+  @Test
+  public void testSuperRefInDefenderMethod() throws Exception {
+    run(GWT_LAMBDA_TEST_36);
+  }
+
+  @Test
+  public void testSuperThisRefsInDefenderMethod() throws Exception {
+    run(GWT_LAMBDA_TEST_37);
+  }
+
+  @Test
+  public void testNestedInterfaceClass() throws Exception {
+    run(GWT_LAMBDA_TEST_38);
+  }
+
+  @Test
+  public void testBaseIntersectionCast() throws Exception {
+    run(GWT_LAMBDA_TEST_39);
+  }
+
+  @Test
+  public void testIntersectionCastWithLambdaExpr() throws Exception {
+    run(GWT_LAMBDA_TEST_40);
+  }
+
+  @Test
+  public void testIntersectionCastPolymorphism() throws Exception {
+    run(GWT_LAMBDA_TEST_41);
+  }
+
+  @Test
+  public void testMultipleDefaults_fromInterfaces_left() throws Exception {
+    run(GWT_LAMBDA_TEST_47);
+  }
+
+  @Test
+  public void testMultipleDefaults_fromInterfaces_right() throws Exception {
+    run(GWT_LAMBDA_TEST_48);
+  }
+
+  @Test
+  public void testMultipleDefaults_superclass_left() throws Exception {
+    run(GWT_LAMBDA_TEST_49);
+  }
+
+  @Test
+  public void testMultipleDefaults_superclass_right() throws Exception {
+    run(GWT_LAMBDA_TEST_50);
+  }
+
+  @Test
+  public void testInterfaceThis() throws Exception {
+    run(GWT_LAMBDA_TEST_51);
+  }
+
+  private void run(@Nonnull RuntimeTestInfo rti) throws Exception {
+    new RuntimeTestHelper(rti)
+        .setSourceLevel(SourceLevel.JAVA_8)
+        .addIgnoredCandidateToolchain(JillBasedToolchain.class)
+        .compileAndRunTest();
+  }
+
+}
diff --git a/jack-tests/tests/com/android/jack/java8/IntersectionTypeTest.java b/jack-tests/tests/com/android/jack/java8/IntersectionTypeTest.java
index afb82c1..fbd6c7f 100644
--- a/jack-tests/tests/com/android/jack/java8/IntersectionTypeTest.java
+++ b/jack-tests/tests/com/android/jack/java8/IntersectionTypeTest.java
@@ -57,6 +57,10 @@
       AbstractTestTools.getTestRootDir("com.android.jack.java8.intersectiontype.test005"),
       "com.android.jack.java8.intersectiontype.test005.jack.Tests");
 
+  private RuntimeTestInfo INTERSECTION_TYPE_006 = new RuntimeTestInfo(
+      AbstractTestTools.getTestRootDir("com.android.jack.java8.intersectiontype.test006"),
+      "com.android.jack.java8.intersectiontype.test006.jack.Tests");
+
   @Test
   public void testIntersectionType001() throws Exception {
     new RuntimeTestHelper(INTERSECTION_TYPE_001)
@@ -114,4 +118,14 @@
       // Compilation error is ok
     }
   }
+
+  @Test
+  public void testIntersectionType006() throws Exception {
+    new RuntimeTestHelper(INTERSECTION_TYPE_006)
+    .setSourceLevel(SourceLevel.JAVA_8)
+    .addProperty(Options.LAMBDA_TO_ANONYMOUS_CONVERTER.getName(), Boolean.TRUE.toString())
+    .addIgnoredCandidateToolchain(JillBasedToolchain.class)
+    .addIgnoredCandidateToolchain(JackApiV01.class)
+    .compileAndRunTest();
+  }
 }
diff --git a/jack-tests/tests/com/android/jack/java8/Java8AllTest.java b/jack-tests/tests/com/android/jack/java8/Java8AllTest.java
index 58c9837..70477f2 100644
--- a/jack-tests/tests/com/android/jack/java8/Java8AllTest.java
+++ b/jack-tests/tests/com/android/jack/java8/Java8AllTest.java
@@ -27,18 +27,8 @@
  */
 @RunWith(JackTestRunner.class)
 @SuiteClasses(value = {
-    AnnotationTest.class,
-    BridgeTest.class,
-    DefaultMethodTest.class,
-    EcjInterfaceMethodsTest.class,
-    EcjLambdaTest.class,
-    GwtTest.class,
-    IntersectionTypeTest.class,
-    LambdaTest.class,
-    MethodRefTest.class,
-    RetroLambdaTests.class,
-    StaticMethodTest.class,
-    TypeInferenceTest.class
+    Java8AllTestPreN.class,
+    Java8AllTestPostM.class
     })
 public class Java8AllTest {
 }
diff --git a/jack-tests/tests/com/android/jack/java8/Java8AllTestPostM.java b/jack-tests/tests/com/android/jack/java8/Java8AllTestPostM.java
new file mode 100644
index 0000000..2f33a4b
--- /dev/null
+++ b/jack-tests/tests/com/android/jack/java8/Java8AllTestPostM.java
@@ -0,0 +1,42 @@
+/*
+ * 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.java8;
+
+
+import com.android.jack.test.junit.JackTestRunner;
+import com.android.jack.test.junit.MinRuntimeVersion;
+import com.android.jack.test.junit.RuntimeVersion;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite.SuiteClasses;
+
+/**
+ * JUnit tests for compilation of Java 8 features which require a post M runtime.
+ */
+@RunWith(JackTestRunner.class)
+@SuiteClasses(value = {
+    DefaultMethodTest.class,
+    EcjInterfaceMethodsTest.class,
+    EcjLambdaTestPostM.class,
+    GwtTestPostM.class,
+    RetroLambdaTests.class,
+    StaticMethodTest.class,
+    })
+@MinRuntimeVersion(RuntimeVersion.N)
+public class Java8AllTestPostM {
+
+}
diff --git a/jack-tests/tests/com/android/jack/java8/Java8AllTestPreN.java b/jack-tests/tests/com/android/jack/java8/Java8AllTestPreN.java
new file mode 100644
index 0000000..922a462
--- /dev/null
+++ b/jack-tests/tests/com/android/jack/java8/Java8AllTestPreN.java
@@ -0,0 +1,40 @@
+/*
+ * 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.java8;
+
+
+import com.android.jack.test.junit.JackTestRunner;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite.SuiteClasses;
+
+/**
+ * JUnit test for compilation of Java 8 features
+ */
+@RunWith(JackTestRunner.class)
+@SuiteClasses(value = {
+    AnnotationTest.class,
+    BridgeTest.class,
+    EcjLambdaTest.class,
+    GwtTest.class,
+    IntersectionTypeTest.class,
+    LambdaTest.class,
+    MethodRefTest.class,
+    TypeInferenceTest.class
+    })
+public class Java8AllTestPreN {
+}
diff --git a/jack-tests/tests/com/android/jack/java8/MethodRefTest.java b/jack-tests/tests/com/android/jack/java8/MethodRefTest.java
index e6f41c0..cfd74d7 100644
--- a/jack-tests/tests/com/android/jack/java8/MethodRefTest.java
+++ b/jack-tests/tests/com/android/jack/java8/MethodRefTest.java
@@ -69,6 +69,10 @@
       AbstractTestTools.getTestRootDir("com.android.jack.java8.methodref.test009"),
       "com.android.jack.java8.methodref.test009.jack.Tests");
 
+  private RuntimeTestInfo METHODREF010 = new RuntimeTestInfo(
+      AbstractTestTools.getTestRootDir("com.android.jack.java8.methodref.test010"),
+      "com.android.jack.java8.methodref.test010.jack.Tests");
+
   @Test
   public void testMethodRef001() throws Exception {
     run(METHODREF001);
@@ -114,6 +118,11 @@
     run(METHODREF009);
   }
 
+  @Test
+  public void testMethodRef010() throws Exception {
+    run(METHODREF010);
+  }
+
   private void run(@Nonnull RuntimeTestInfo rti) throws Exception {
     new RuntimeTestHelper(rti)
         .setSourceLevel(SourceLevel.JAVA_8)
diff --git a/jack-tests/tests/com/android/jack/java8/StaticMethodTest.java b/jack-tests/tests/com/android/jack/java8/StaticMethodTest.java
index 6d477cc..76210fc 100644
--- a/jack-tests/tests/com/android/jack/java8/StaticMethodTest.java
+++ b/jack-tests/tests/com/android/jack/java8/StaticMethodTest.java
@@ -28,6 +28,7 @@
 import com.android.jack.test.toolchain.Toolchain.SourceLevel;
 
 
+
 /**
  * JUnit test for compilation of static method.
  */
diff --git a/jack-tests/tests/com/android/jack/java8/intersectiontype/test006/jack/Tests.java b/jack-tests/tests/com/android/jack/java8/intersectiontype/test006/jack/Tests.java
new file mode 100644
index 0000000..1f90fac
--- /dev/null
+++ b/jack-tests/tests/com/android/jack/java8/intersectiontype/test006/jack/Tests.java
@@ -0,0 +1,37 @@
+/*
+ * 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.java8.intersectiontype.test006.jack;
+
+import junit.framework.Assert;
+
+import org.junit.Test;
+
+import java.io.Serializable;
+
+interface P<T> {
+  boolean check(T t);
+}
+
+
+public class Tests {
+
+  @Test
+  public void test() {
+    P<String> p = (P<String> & Serializable) s -> true;
+    Assert.assertTrue(p.check("abc"));
+  }
+}
diff --git a/jack-tests/tests/com/android/jack/java8/methodref/test010/jack/Tests.java b/jack-tests/tests/com/android/jack/java8/methodref/test010/jack/Tests.java
new file mode 100644
index 0000000..593d2f5
--- /dev/null
+++ b/jack-tests/tests/com/android/jack/java8/methodref/test010/jack/Tests.java
@@ -0,0 +1,46 @@
+/*
+ * 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.java8.methodref.test010.jack;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+interface IntOp {
+  int apply(int i);
+}
+
+interface I {
+  char charAt0(String str);
+}
+
+/**
+ * Check that cast it correctly generated for the lambda representing string::charAt
+ */
+public class Tests {
+
+  public char test(IntOp op) {
+    return (char) op.apply(0);
+  }
+
+  @Test
+  public void test001() {
+    I i = string -> {
+      return test(string::charAt);
+    };
+    Assert.assertEquals('A', i.charAt0("ABC"));
+  }
+}
diff --git a/jack/src/com/android/jack/Jack.java b/jack/src/com/android/jack/Jack.java
index c34cc08..593c6cb 100644
--- a/jack/src/com/android/jack/Jack.java
+++ b/jack/src/com/android/jack/Jack.java
@@ -87,14 +87,11 @@
 import com.android.jack.incremental.InputFilter;
 import com.android.jack.ir.JackFormatIr;
 import com.android.jack.ir.JavaSourceIr;
-import com.android.jack.ir.ast.JClass;
 import com.android.jack.ir.ast.JDefinedClassOrInterface;
 import com.android.jack.ir.ast.JField;
 import com.android.jack.ir.ast.JMethod;
 import com.android.jack.ir.ast.JPackage;
 import com.android.jack.ir.ast.JSession;
-import com.android.jack.ir.ast.JType;
-import com.android.jack.ir.ast.JVisitor;
 import com.android.jack.ir.formatter.InternalFormatter;
 import com.android.jack.ir.formatter.TypePackageAndMethodFormatter;
 import com.android.jack.ir.formatter.UserFriendlyFormatter;
@@ -107,7 +104,6 @@
 import com.android.jack.library.LibraryIOException;
 import com.android.jack.library.LibraryReadingException;
 import com.android.jack.library.OutputJackLibrary;
-import com.android.jack.lookup.CommonTypes;
 import com.android.jack.lookup.JPhantomLookup;
 import com.android.jack.meta.LibraryMetaWriter;
 import com.android.jack.meta.MetaImporter;
@@ -895,27 +891,6 @@
       session.getReporter().report(Severity.FATAL, e);
       throw new JackAbortException(e);
     }
-
-    if (options.flags != null && (options.flags.shrink() || options.flags.obfuscate())) {
-      Event eventIdMerger = tracer.start(JackEventType.METHOD_ID_MERGER);
-
-      try {
-        JClass javaLangObject = session.getPhantomLookup().getClass(CommonTypes.JAVA_LANG_OBJECT);
-        MethodIdMerger merger = new MethodIdMerger(javaLangObject);
-        for (JType type : session.getTypesToEmit()) {
-          merger.accept(type);
-        }
-        JVisitor remover = new VirtualMethodsMarker.Remover(javaLangObject);
-        for (JType type : session.getTypesToEmit()) {
-          remover.accept(type);
-        }
-      } finally {
-        eventIdMerger.end();
-      }
-
-      MethodIdDuplicateRemover methodIdDupRemover = new MethodIdDuplicateRemover();
-      methodIdDupRemover.accept(session.getTypesToEmit());
-    }
   }
 
   private static void addPackageLoaderForLibrary(JSession session,
@@ -1032,6 +1007,14 @@
     boolean hasSanityChecks = features.contains(SanityChecks.class);
 
     // Build the plan
+    if (features.contains(Shrinking.class) || features.contains(Obfuscation.class)
+        || features.contains(MultiDexLegacy.class)) {
+      planBuilder.append(MethodIdMerger.class);
+      planBuilder.append(VirtualMethodsMarker.Remover.class);
+      planBuilder.appendSubPlan(JDefinedClassOrInterfaceAdapter.class)
+        .append(MethodIdDuplicateRemover.class);
+    }
+
     if (hasSanityChecks) {
       planBuilder.append(TypeDuplicateRemoverChecker.class);
     }
diff --git a/jack/src/com/android/jack/JackAbortException.java b/jack/src/com/android/jack/JackAbortException.java
index ed1634b..70f1dcf 100644
--- a/jack/src/com/android/jack/JackAbortException.java
+++ b/jack/src/com/android/jack/JackAbortException.java
@@ -18,24 +18,35 @@
 
 import com.android.jack.reporting.ReportableException;
 
+import javax.annotation.CheckForNull;
 import javax.annotation.Nonnull;
 
 /**
  * A {@link RuntimeException} that should be fatal and cause Jack to abort. It should not be caught,
- * should have a cause {@link ReportableException} and no message.
+ * and should have no message.
+ * It can have a cause {@link ReportableException}
  */
 public class JackAbortException extends RuntimeException {
 
   private static final long serialVersionUID = 1L;
 
+  public JackAbortException() {
+
+  }
+
   public JackAbortException(@Nonnull ReportableException cause) {
     super(cause);
   }
 
   @Override
-  @Nonnull
+  @CheckForNull
   public String getMessage() {
-    return getCause().getMessage();
+    Throwable cause = getCause();
+    if (cause != null) {
+      return cause.getMessage();
+    } else {
+      return null;
+    }
   }
 
 }
diff --git a/jack/src/com/android/jack/analysis/tracer/Tracer.java b/jack/src/com/android/jack/analysis/tracer/Tracer.java
index f89bd6d..307cdfa 100644
--- a/jack/src/com/android/jack/analysis/tracer/Tracer.java
+++ b/jack/src/com/android/jack/analysis/tracer/Tracer.java
@@ -17,6 +17,7 @@
 package com.android.jack.analysis.tracer;
 
 import com.android.jack.Jack;
+import com.android.jack.frontend.MethodIdDuplicateRemover.UniqMethodIds;
 import com.android.jack.ir.ast.Annotable;
 import com.android.jack.ir.ast.JAbstractMethodBody;
 import com.android.jack.ir.ast.JAbstractStringLiteral;
@@ -65,6 +66,7 @@
 import com.android.jack.shrob.shrink.PartialTypeHierarchy;
 import com.android.sched.item.Description;
 import com.android.sched.marker.LocalMarkerManager;
+import com.android.sched.schedulable.Constraint;
 import com.android.sched.util.log.LoggerFactory;
 import com.android.sched.util.log.TracerFactory;
 
@@ -78,6 +80,7 @@
  * A visitor that traces dependencies
  */
 @Description("traces dependencies")
+@Constraint(need = UniqMethodIds.class)
 public class Tracer extends JVisitor {
 
   @Nonnull
diff --git a/jack/src/com/android/jack/backend/dex/MainDexTracer.java b/jack/src/com/android/jack/backend/dex/MainDexTracer.java
index a8e06ff..9d2e81b 100644
--- a/jack/src/com/android/jack/backend/dex/MainDexTracer.java
+++ b/jack/src/com/android/jack/backend/dex/MainDexTracer.java
@@ -35,7 +35,7 @@
  */
 @Description("Trace for main dex.")
 @Constraint(need = ExtendingOrImplementingClassMarker.class)
-@Use(MultiDexLegacyTracerBrush.class)
+@Use({Tracer.class, MultiDexLegacyTracerBrush.class})
 @Optional(@ToSupport(feature = SourceVersion8.class,
     add = @Constraint(need = JAnnotation.RepeatedAnnotation.class)))
 public class MainDexTracer implements RunnableSchedulable<JDefinedClassOrInterface> {
diff --git a/jack/src/com/android/jack/coverage/CodeCoverageMetadataFileWriter.java b/jack/src/com/android/jack/coverage/CodeCoverageMetadataFileWriter.java
index 7098460..0a8a5c9 100644
--- a/jack/src/com/android/jack/coverage/CodeCoverageMetadataFileWriter.java
+++ b/jack/src/com/android/jack/coverage/CodeCoverageMetadataFileWriter.java
@@ -61,6 +61,18 @@
 @Produce(CodeCoverageMetadataFile.class)
 public class CodeCoverageMetadataFileWriter implements RunnableSchedulable<JSession> {
 
+  /**
+   * The version of emitted JSON coverage information.
+   */
+  @Nonnull
+  private static final String VERSION = "1.0";
+
+  @Nonnull
+  private static final String JSON_VERSION_ATTRIBUTE = "version";
+
+  @Nonnull
+  private static final String JSON_DATA_ATTRIBUTE = "data";
+
   @Nonnull
   public static final PropertyId<OutputStreamFile> COVERAGE_METADATA_FILE = PropertyId.create(
       "jack.coverage.metadata.file", "File where the coverage metadata will be emitted",
@@ -232,8 +244,7 @@
 
   @Override
   public void run(@Nonnull JSession session) throws Exception {
-    OutputStreamFile outputFile = ThreadConfig.get(COVERAGE_METADATA_FILE);
-    PrintStream writer = outputFile.getPrintStream();
+    PrintStream writer = ThreadConfig.get(COVERAGE_METADATA_FILE).getPrintStream();
     try {
       writeMetadata(session, writer);
     } finally {
@@ -242,7 +253,13 @@
   }
 
   private void writeMetadata(@Nonnull JSession session, @Nonnull PrintStream writer) {
-    writer.println('[');
+    writer.print("{ \"");
+    writer.print(JSON_VERSION_ATTRIBUTE);
+    writer.print("\":\"");
+    writer.print(VERSION);
+    writer.print("\", \"");
+    writer.print(JSON_DATA_ATTRIBUTE);
+    writer.println("\":[");
     Iterator<JDefinedClassOrInterface> list = session.getTypesToEmit().iterator();
     boolean first = true;
     while (list.hasNext()) {
@@ -268,6 +285,7 @@
         writer.println();
       }
     }
-    writer.println(']');
+    writer.println("]}");
+    writer.flush();
   }
 }
diff --git a/jack/src/com/android/jack/frontend/MethodIdDuplicateRemover.java b/jack/src/com/android/jack/frontend/MethodIdDuplicateRemover.java
index 65cc08f..68c6cde 100644
--- a/jack/src/com/android/jack/frontend/MethodIdDuplicateRemover.java
+++ b/jack/src/com/android/jack/frontend/MethodIdDuplicateRemover.java
@@ -16,8 +16,11 @@
 
 package com.android.jack.frontend;
 
+import com.android.jack.frontend.MethodIdDuplicateRemover.UniqMethodIds;
+import com.android.jack.frontend.MethodIdMerger.MethodIdMerged;
 import com.android.jack.ir.ast.JAnnotation;
 import com.android.jack.ir.ast.JClassOrInterface;
+import com.android.jack.ir.ast.JDefinedClassOrInterface;
 import com.android.jack.ir.ast.JInterface;
 import com.android.jack.ir.ast.JLambda;
 import com.android.jack.ir.ast.JMethod;
@@ -28,59 +31,86 @@
 import com.android.jack.ir.ast.JVisitor;
 import com.android.jack.ir.ast.MethodKind;
 import com.android.jack.lookup.JMethodWithReturnLookupException;
+import com.android.sched.item.Description;
+import com.android.sched.item.Name;
+import com.android.sched.item.Tag;
+import com.android.sched.schedulable.Constraint;
+import com.android.sched.schedulable.RunnableSchedulable;
+import com.android.sched.schedulable.Transform;
 
 import java.util.Collection;
 
 import javax.annotation.Nonnull;
 
 /**
- * Update methodIds of {@link JMethodCall}s and {@link JNameValuePair}s.
+ * Update methodIds of {@link JMethodCall}s and {@link JNameValuePair}s so that they used the merged
+ * uniq ids.
  */
-public class MethodIdDuplicateRemover extends JVisitor {
+@Description("Update methodIds of JMethodCalls and JNameValuePairs so that they used the merged"
+    + " uniq ids")
+@Constraint(need = MethodIdMerged.class)
+@Transform(add = UniqMethodIds.class)
+public class MethodIdDuplicateRemover implements RunnableSchedulable<JDefinedClassOrInterface> {
 
-  public MethodIdDuplicateRemover() {
-    super(false /* needLoading */);
+  /**
+   * This tag means that Jack IR is using uniq JMethodIds.
+   */
+  @Description("Jack IR is using uniq JMethodIds")
+  @Name("UniqMethodIds")
+  public static class UniqMethodIds implements Tag {
   }
 
-  @Nonnull
-  private JMethodId getResolvedMethodId(
-      @Nonnull JClassOrInterface receiverType, @Nonnull JMethodId id) {
-    Collection<JMethod> methods = id.getMethods();
-    if (!methods.isEmpty()) {
-      JMethod method = methods.iterator().next();
-      return method.getMethodId();
-    } else {
-      return receiverType.getOrCreateMethodId(id.getName(), id.getParamTypes(), id.getKind());
+  private static class Visitor extends JVisitor {
+
+    private Visitor() {
+      super(false /* needLoading */);
+    }
+
+    @Nonnull
+    private JMethodId getResolvedMethodId(
+        @Nonnull JClassOrInterface receiverType, @Nonnull JMethodId id) {
+      Collection<JMethod> methods = id.getMethods();
+      if (!methods.isEmpty()) {
+        JMethod method = methods.iterator().next();
+        return method.getMethodId();
+      } else {
+        return receiverType.getOrCreateMethodId(id.getName(), id.getParamTypes(), id.getKind());
+      }
+    }
+
+    @Override
+    public boolean visit(@Nonnull JLambda lambda) {
+      JInterface lambdaType = lambda.getType();
+      try {
+        JMethodIdWithReturnType mthIdWithReturnType = lambda.getMethodIdToImplement();
+        JMethodId newMthId = lambdaType.getMethodId(mthIdWithReturnType.getName(),
+            mthIdWithReturnType.getParameterTypes(), MethodKind.INSTANCE_VIRTUAL);
+        mthIdWithReturnType.setMethodId(newMthId);
+      } catch (JMethodWithReturnLookupException e) {
+        throw new AssertionError();
+      }
+      return super.visit(lambda);
+    }
+
+    @Override
+    public boolean visit(@Nonnull JMethodCall call) {
+      JMethodId id = getResolvedMethodId(call.getReceiverType(), call.getMethodId());
+      call.resolveMethodId(id);
+      return super.visit(call);
+    }
+
+    @Override
+    public boolean visit(@Nonnull JAnnotation annotation) {
+      for (JNameValuePair pair : annotation.getNameValuePairs()) {
+        JMethodId id = getResolvedMethodId(annotation.getType(), pair.getMethodId());
+        pair.resolveMethodId(id);
+      }
+      return super.visit(annotation);
     }
   }
 
   @Override
-  public boolean visit(@Nonnull JLambda lambda) {
-    JInterface lambdaType = lambda.getType();
-    try {
-      JMethodIdWithReturnType mthIdWithReturnType = lambda.getMethodIdToImplement();
-      JMethodId newMthId = lambdaType.getMethodId(mthIdWithReturnType.getName(),
-          mthIdWithReturnType.getParameterTypes(), MethodKind.INSTANCE_VIRTUAL);
-      mthIdWithReturnType.setMethodId(newMthId);
-    } catch (JMethodWithReturnLookupException e) {
-      throw new AssertionError();
-    }
-    return super.visit(lambda);
-  }
-
-  @Override
-  public boolean visit(@Nonnull JMethodCall call) {
-    JMethodId id = getResolvedMethodId(call.getReceiverType(), call.getMethodId());
-    call.resolveMethodId(id);
-    return super.visit(call);
-  }
-
-  @Override
-  public boolean visit(@Nonnull JAnnotation annotation) {
-    for (JNameValuePair pair : annotation.getNameValuePairs()) {
-      JMethodId id = getResolvedMethodId(annotation.getType(), pair.getMethodId());
-      pair.resolveMethodId(id);
-    }
-    return super.visit(annotation);
+  public void run(JDefinedClassOrInterface type) throws Exception {
+    new Visitor().accept(type);
   }
 }
diff --git a/jack/src/com/android/jack/frontend/MethodIdMerger.java b/jack/src/com/android/jack/frontend/MethodIdMerger.java
index 622a607..82459db 100644
--- a/jack/src/com/android/jack/frontend/MethodIdMerger.java
+++ b/jack/src/com/android/jack/frontend/MethodIdMerger.java
@@ -16,6 +16,8 @@
 
 package com.android.jack.frontend;
 
+import com.android.jack.Jack;
+import com.android.jack.frontend.MethodIdMerger.MethodIdMerged;
 import com.android.jack.ir.ast.JClass;
 import com.android.jack.ir.ast.JClassOrInterface;
 import com.android.jack.ir.ast.JConstructor;
@@ -27,7 +29,14 @@
 import com.android.jack.ir.ast.JMethodId;
 import com.android.jack.ir.ast.JNode;
 import com.android.jack.ir.ast.JPhantomClassOrInterface;
+import com.android.jack.ir.ast.JSession;
 import com.android.jack.ir.ast.JVisitor;
+import com.android.jack.lookup.CommonTypes;
+import com.android.sched.item.Description;
+import com.android.sched.item.Name;
+import com.android.sched.item.Tag;
+import com.android.sched.schedulable.RunnableSchedulable;
+import com.android.sched.schedulable.Transform;
 
 import java.util.Iterator;
 
@@ -37,140 +46,155 @@
 /**
  * Merges ids of {@link JMethod}s with the same name and arguments in the same hierarchy tree.
  */
-public class MethodIdMerger extends JVisitor {
-
-  @Nonnull
-  private final JClass javaLangObject;
-
-  public MethodIdMerger(@Nonnull JClass javaLangObject) {
-    this.javaLangObject = javaLangObject;
-  }
-
-  @Override
-  public boolean visit(@Nonnull JDefinedClass node) {
-    if (node.getMarker(VirtualMethodsMarker.class) != null) {
-      return false;
-    }
-    handleDefinedClassOrInterface(node);
-    return super.visit(node);
-  }
-
-  @Override
-  public boolean visit(@Nonnull JDefinedInterface node) {
-    if (node.getMarker(VirtualMethodsMarker.class) != null) {
-      return false;
-    }
-    handleDefinedClassOrInterface(node);
-    return super.visit(node);
-  }
-
-  @Override
-  public boolean visit(@Nonnull JPhantomClassOrInterface node) {
-    if (node.getMarker(VirtualMethodsMarker.class) != null) {
-      return false;
-    }
-    ensureHierarchyVisited(node);
-    return super.visit(node);
-  }
-
-  private void ensureHierarchyVisited(@Nonnull JClassOrInterface node) {
-    JClass zuper = getSuper(node);
-    if (zuper != null) {
-      accept(zuper);
-    }
-    if (node instanceof JDefinedClassOrInterface) {
-      for (JClassOrInterface interfaze : ((JDefinedClassOrInterface) node).getImplements()) {
-        accept(interfaze);
-      }
-    }
-  }
-
-  private void handleDefinedClassOrInterface(@Nonnull JDefinedClassOrInterface node) {
-    ensureHierarchyVisited(node);
-
-    JClass zuper = getSuper(node);
-    while (zuper instanceof JPhantomClassOrInterface) {
-      zuper = getSuper(zuper);
-    }
-
-    VirtualMethodsMarker virtualMethods;
-    if (zuper != null) {
-      VirtualMethodsMarker superMarker = ((JNode) zuper).getMarker(VirtualMethodsMarker.class);
-      assert superMarker != null;
-      virtualMethods = superMarker.clone();
-    } else {
-      virtualMethods = new VirtualMethodsMarker();
-    }
-
-    for (JInterface interfaze : node.getImplements()) {
-      if (interfaze instanceof JDefinedClassOrInterface) {
-        addIds(virtualMethods, (JNode) interfaze);
-      }
-    }
-
-    for (JMethod method : node.getMethods()) {
-      if (((!method.isStatic()) && (!method.isPrivate()) && !(method instanceof JConstructor))) {
-        addId(virtualMethods, method.getMethodId());
-      }
-    }
-
-    node.addMarker(virtualMethods);
-  }
-
-  private void addIds(@Nonnull VirtualMethodsMarker mergeInto, @Nonnull JNode toMerge) {
-    VirtualMethodsMarker methodsToMerge = toMerge.getMarker(VirtualMethodsMarker.class);
-    assert methodsToMerge != null;
-    for (JMethodId jMethodId : methodsToMerge) {
-      addId(mergeInto, jMethodId);
-    }
-  }
-
-  private void addId(@Nonnull VirtualMethodsMarker virtualMethods, @Nonnull JMethodId toAdd) {
-    JMethodId existingMethod = virtualMethods.get(toAdd);
-    if (existingMethod != null) {
-      mergeId(existingMethod, toAdd);
-    } else {
-      virtualMethods.add(toAdd);
-    }
-  }
-
-  private void mergeId(@Nonnull JMethodId keep, @Nonnull JMethodId duplicate) {
-
-    keep = getKeptId(keep);
-    duplicate = getKeptId(duplicate);
-
-    if (keep == duplicate) {
-      return;
-    }
-
-    for (JMethod method : duplicate.getMethods()) {
-      method.setMethodId(keep);
-    }
-  }
+@Description("Merges ids of JMethods with the same name and arguments in the same hierarchy tree.")
+@Transform(add = {MethodIdMerged.class, VirtualMethodsMarker.class})
+public class MethodIdMerger implements RunnableSchedulable<JSession> {
 
   /**
-   * During the merge of {@link JMethodId} some are kept and some are dropped and because
-   * {@link VirtualMethodsMarker} are not rewritten during merge, they contain dropped
-   * {@code JMethodId}. This method retrieves the kept id from an id found in a
-   * {@code VirtualMethodsMarker}
+   * This tag means that JMethodIds were merged when needed.
    */
-  @Nonnull
-  private JMethodId getKeptId(@Nonnull JMethodId possiblyDroppedId) {
-    Iterator<JMethod> methods1 = possiblyDroppedId.getMethods().iterator();
-    assert methods1.hasNext() :
-      "Only method id contained in JMethod are considered by this visitor";
-    return methods1.next().getMethodId();
+  @Description("JMethodId were merged")
+  @Name("MethodIdMerged")
+  public static class MethodIdMerged implements Tag {
   }
 
-  @CheckForNull
-  private JClass getSuper(@Nonnull JClassOrInterface node) {
-    if (node instanceof JDefinedClass) {
-      return ((JDefinedClass) node).getSuperClass();
+  private static class Visitor extends JVisitor {
+
+    @Nonnull
+    private final JClass javaLangObject = Jack.getSession().getPhantomLookup()
+    .getClass(CommonTypes.JAVA_LANG_OBJECT);
+
+    @Override
+    public boolean visit(@Nonnull JDefinedClass node) {
+      if (node.getMarker(VirtualMethodsMarker.class) != null) {
+        return false;
+      }
+      handleDefinedClassOrInterface(node);
+      return super.visit(node);
     }
-    if (!node.isSameType(javaLangObject)) {
-      return javaLangObject;
-    } else {
-      return null;
+
+    @Override
+    public boolean visit(@Nonnull JDefinedInterface node) {
+      if (node.getMarker(VirtualMethodsMarker.class) != null) {
+        return false;
+      }
+      handleDefinedClassOrInterface(node);
+      return super.visit(node);
     }
+
+    @Override
+    public boolean visit(@Nonnull JPhantomClassOrInterface node) {
+      if (node.getMarker(VirtualMethodsMarker.class) != null) {
+        return false;
+      }
+      ensureHierarchyVisited(node);
+      return super.visit(node);
+    }
+
+    private void ensureHierarchyVisited(@Nonnull JClassOrInterface node) {
+      JClass zuper = getSuper(node);
+      if (zuper != null) {
+        accept(zuper);
+      }
+      if (node instanceof JDefinedClassOrInterface) {
+        for (JClassOrInterface interfaze : ((JDefinedClassOrInterface) node).getImplements()) {
+          accept(interfaze);
+        }
+      }
+    }
+
+    private void handleDefinedClassOrInterface(@Nonnull JDefinedClassOrInterface node) {
+      ensureHierarchyVisited(node);
+
+      JClass zuper = getSuper(node);
+      while (zuper instanceof JPhantomClassOrInterface) {
+        zuper = getSuper(zuper);
+      }
+
+      VirtualMethodsMarker virtualMethods;
+      if (zuper != null) {
+        VirtualMethodsMarker superMarker = ((JNode) zuper).getMarker(VirtualMethodsMarker.class);
+        assert superMarker != null;
+        virtualMethods = superMarker.clone();
+      } else {
+        virtualMethods = new VirtualMethodsMarker();
+      }
+
+      for (JInterface interfaze : node.getImplements()) {
+        if (interfaze instanceof JDefinedClassOrInterface) {
+          addIds(virtualMethods, (JNode) interfaze);
+        }
+      }
+
+      for (JMethod method : node.getMethods()) {
+        if (((!method.isStatic()) && (!method.isPrivate()) && !(method instanceof JConstructor))) {
+          addId(virtualMethods, method.getMethodId());
+        }
+      }
+
+      node.addMarker(virtualMethods);
+    }
+
+    private void addIds(@Nonnull VirtualMethodsMarker mergeInto, @Nonnull JNode toMerge) {
+      VirtualMethodsMarker methodsToMerge = toMerge.getMarker(VirtualMethodsMarker.class);
+      assert methodsToMerge != null;
+      for (JMethodId jMethodId : methodsToMerge) {
+        addId(mergeInto, jMethodId);
+      }
+    }
+
+    private void addId(@Nonnull VirtualMethodsMarker virtualMethods, @Nonnull JMethodId toAdd) {
+      JMethodId existingMethod = virtualMethods.get(toAdd);
+      if (existingMethod != null) {
+        mergeId(existingMethod, toAdd);
+      } else {
+        virtualMethods.add(toAdd);
+      }
+    }
+
+    private void mergeId(@Nonnull JMethodId keep, @Nonnull JMethodId duplicate) {
+
+      keep = getKeptId(keep);
+      duplicate = getKeptId(duplicate);
+
+      if (keep == duplicate) {
+        return;
+      }
+
+      for (JMethod method : duplicate.getMethods()) {
+        method.setMethodId(keep);
+      }
+    }
+
+    /**
+     * During the merge of {@link JMethodId} some are kept and some are dropped and because
+     * {@link VirtualMethodsMarker} are not rewritten during merge, they contain dropped
+     * {@code JMethodId}. This method retrieves the kept id from an id found in a
+     * {@code VirtualMethodsMarker}
+     */
+    @Nonnull
+    private JMethodId getKeptId(@Nonnull JMethodId possiblyDroppedId) {
+      Iterator<JMethod> methods1 = possiblyDroppedId.getMethods().iterator();
+      assert methods1.hasNext() :
+        "Only method id contained in JMethod are considered by this visitor";
+      return methods1.next().getMethodId();
+    }
+
+    @CheckForNull
+    private JClass getSuper(@Nonnull JClassOrInterface node) {
+      if (node instanceof JDefinedClass) {
+        return ((JDefinedClass) node).getSuperClass();
+      }
+      if (!node.isSameType(javaLangObject)) {
+        return javaLangObject;
+      } else {
+        return null;
+      }
+    }
+  }
+
+  @Override
+  public void run(JSession session) throws Exception {
+    new Visitor().accept(session.getTypesToEmit());
   }
 }
diff --git a/jack/src/com/android/jack/frontend/VirtualMethodsMarker.java b/jack/src/com/android/jack/frontend/VirtualMethodsMarker.java
index bcc4838..d3b7936 100644
--- a/jack/src/com/android/jack/frontend/VirtualMethodsMarker.java
+++ b/jack/src/com/android/jack/frontend/VirtualMethodsMarker.java
@@ -16,6 +16,7 @@
 
 package com.android.jack.frontend;
 
+import com.android.jack.Jack;
 import com.android.jack.ir.ast.JClass;
 import com.android.jack.ir.ast.JClassOrInterface;
 import com.android.jack.ir.ast.JDefinedClass;
@@ -23,11 +24,16 @@
 import com.android.jack.ir.ast.JDefinedInterface;
 import com.android.jack.ir.ast.JMethodId;
 import com.android.jack.ir.ast.JPhantomClassOrInterface;
+import com.android.jack.ir.ast.JSession;
 import com.android.jack.ir.ast.JType;
 import com.android.jack.ir.ast.JVisitor;
+import com.android.jack.lookup.CommonTypes;
 import com.android.sched.item.Description;
 import com.android.sched.marker.Marker;
 import com.android.sched.marker.ValidOn;
+import com.android.sched.schedulable.Constraint;
+import com.android.sched.schedulable.RunnableSchedulable;
+import com.android.sched.schedulable.Transform;
 
 import java.util.HashMap;
 import java.util.Iterator;
@@ -47,62 +53,72 @@
   /**
    * A remover for {@link VirtualMethodsMarker}
    */
-  public static class Remover extends JVisitor {
+  @Description("Removes VirtualMethodsMarker")
+  @Transform(remove = VirtualMethodsMarker.class)
+  @Constraint(need = VirtualMethodsMarker.class)
+  public static class Remover implements RunnableSchedulable<JSession> {
+    private static class Visitor extends JVisitor {
 
-    @Nonnull
-    private final JClass javaLangObject;
+      @Nonnull
+      private final JClass javaLangObject = Jack.getSession().getPhantomLookup()
+      .getClass(CommonTypes.JAVA_LANG_OBJECT);
 
-    public Remover(@Nonnull JClass javaLangObject) {
-      super(false /* needLoading */);
-      this.javaLangObject = javaLangObject;
-    }
-
-    @Override
-    public boolean visit(@Nonnull JDefinedClass definedClass) {
-      if (definedClass.removeMarker(VirtualMethodsMarker.class) != null) {
-        ensureHierarchyVisited(definedClass);
+      private Visitor() {
+        super(false /* needLoading */);
       }
-      return false;
-    }
 
-    @Override
-    public boolean visit(@Nonnull JDefinedInterface defineInterface) {
-      if (defineInterface.removeMarker(VirtualMethodsMarker.class) != null) {
-        ensureHierarchyVisited(defineInterface);
+      @Override
+      public boolean visit(@Nonnull JDefinedClass definedClass) {
+        if (definedClass.removeMarker(VirtualMethodsMarker.class) != null) {
+          ensureHierarchyVisited(definedClass);
+        }
+        return false;
       }
-      return false;
-    }
 
-    @Override
-    public boolean visit(@Nonnull JPhantomClassOrInterface phantomClassOrInterface) {
-      if (phantomClassOrInterface.removeMarker(VirtualMethodsMarker.class) != null) {
-        ensureHierarchyVisited(phantomClassOrInterface);
+      @Override
+      public boolean visit(@Nonnull JDefinedInterface defineInterface) {
+        if (defineInterface.removeMarker(VirtualMethodsMarker.class) != null) {
+          ensureHierarchyVisited(defineInterface);
+        }
+        return false;
       }
-      return false;
-    }
 
-    private void ensureHierarchyVisited(@Nonnull JClassOrInterface node) {
-      JClass zuper = getSuper(node);
-      if (zuper != null) {
-        accept(zuper);
+      @Override
+      public boolean visit(@Nonnull JPhantomClassOrInterface phantomClassOrInterface) {
+        if (phantomClassOrInterface.removeMarker(VirtualMethodsMarker.class) != null) {
+          ensureHierarchyVisited(phantomClassOrInterface);
+        }
+        return false;
       }
-      if (node instanceof JDefinedClassOrInterface) {
-        for (JClassOrInterface interfaze : ((JDefinedClassOrInterface) node).getImplements()) {
-          accept(interfaze);
+
+      private void ensureHierarchyVisited(@Nonnull JClassOrInterface node) {
+        JClass zuper = getSuper(node);
+        if (zuper != null) {
+          accept(zuper);
+        }
+        if (node instanceof JDefinedClassOrInterface) {
+          for (JClassOrInterface interfaze : ((JDefinedClassOrInterface) node).getImplements()) {
+            accept(interfaze);
+          }
+        }
+      }
+
+      @CheckForNull
+      private JClass getSuper(@Nonnull JClassOrInterface node) {
+        if (node instanceof JDefinedClass) {
+          return ((JDefinedClass) node).getSuperClass();
+        }
+        if (!node.isSameType(javaLangObject)) {
+          return javaLangObject;
+        } else {
+          return null;
         }
       }
     }
 
-    @CheckForNull
-    private JClass getSuper(@Nonnull JClassOrInterface node) {
-      if (node instanceof JDefinedClass) {
-        return ((JDefinedClass) node).getSuperClass();
-      }
-      if (!node.isSameType(javaLangObject)) {
-        return javaLangObject;
-      } else {
-        return null;
-      }
+    @Override
+    public void run(JSession session) throws Exception {
+      new Visitor().accept(session.getTypesToEmit());
     }
   }
 
diff --git a/jack/src/com/android/jack/ir/ast/JSession.java b/jack/src/com/android/jack/ir/ast/JSession.java
index 5e2a4f6..c51809a 100644
--- a/jack/src/com/android/jack/ir/ast/JSession.java
+++ b/jack/src/com/android/jack/ir/ast/JSession.java
@@ -143,6 +143,7 @@
     return phantomLookup;
   }
 
+  @Deprecated
   @Nonnull
   public Logger getUserLogger() {
     return userLogger;
diff --git a/jack/src/com/android/jack/ir/impl/JackIrBuilder.java b/jack/src/com/android/jack/ir/impl/JackIrBuilder.java
index 966b172..44e1d9f 100644
--- a/jack/src/com/android/jack/ir/impl/JackIrBuilder.java
+++ b/jack/src/com/android/jack/ir/impl/JackIrBuilder.java
@@ -1411,7 +1411,7 @@
         boolean shouldCaptureInstance = isSuperRef || isThisRef;
 
         exprRepresentingLambda = new JLambda(sourceInfo, methodIdToImplement, newMethodInfo.method,
-            (JDefinedInterface) getTypeMap().get(referenceExpression.resolvedType),
+            (JDefinedInterface) getTypeMap().get(getLambdaType(referenceExpression, blockScope)),
             shouldCaptureInstance, getInterfaceBounds(referenceExpression, blockScope));
         ((JLambda) exprRepresentingLambda).addBridgeMethodIds(getBridges(referenceExpression));
 
@@ -1477,7 +1477,8 @@
 
         exprRepresentingLambda =
             new JLambda(sourceInfo, methodIdToImplement, newMethodInfo.method,
-                (JDefinedInterface) getTypeMap().get(referenceExpression.resolvedType),
+                (JDefinedInterface) getTypeMap()
+                    .get(getLambdaType(referenceExpression, blockScope)),
                 /* captureInstance= */ false, getInterfaceBounds(referenceExpression, blockScope));
         ((JLambda) exprRepresentingLambda)
             .addBridgeMethodIds(getBridges(referenceExpression));
@@ -1539,7 +1540,7 @@
       JLambda lambda = new JLambda(sourceInfo,
           getJMethodIdWithReturnType(referenceExpression.descriptor.original()),
           newMethodInfo.method,
-          (JDefinedInterface) getTypeMap().get(referenceExpression.resolvedType),
+          (JDefinedInterface) getTypeMap().get(getLambdaType(referenceExpression, blockScope)),
           shouldCaptureInstance, getInterfaceBounds(referenceExpression, blockScope));
       lambda.addBridgeMethodIds(getBridges(referenceExpression));
 
@@ -1733,7 +1734,7 @@
       JLambda lambda = new JLambda(sourceInfo,
           getJMethodIdWithReturnType(lambdaExpression.descriptor.original()),
           lambdaMethodInfo.method,
-          (JDefinedInterface) getTypeMap().get(lambdaExpression.resolvedType),
+          (JDefinedInterface) getTypeMap().get(getLambdaType(lambdaExpression, blockScope)),
           lambdaExpression.shouldCaptureInstance, getInterfaceBounds(lambdaExpression, blockScope));
       lambda.addBridgeMethodIds(getBridges(lambdaExpression));
 
@@ -1755,6 +1756,19 @@
     }
 
     @Nonnull
+    private TypeBinding getLambdaType(@Nonnull FunctionalExpression functionalExpression,
+        @Nonnull BlockScope blockScope) {
+      TypeBinding resolvedType = functionalExpression.resolvedType;
+
+      if (resolvedType instanceof IntersectionTypeBinding18) {
+        resolvedType = ((IntersectionTypeBinding18) resolvedType).getSAMType(blockScope);
+      }
+
+      assert resolvedType != null;
+      return resolvedType;
+    }
+
+    @Nonnull
     private List<JInterface> getInterfaceBounds(
         @Nonnull FunctionalExpression functionalExpression,
         @Nonnull BlockScope blockScope) {
@@ -3154,9 +3168,7 @@
 
     private JExpression maybeCast(JType expected, JExpression expression) {
       if (!expected.isSameType(expression.getType())) {
-        // Must be a generic; insert a cast operation.
-        JReferenceType toType = (JReferenceType) expected;
-        return new JDynamicCastOperation(expression.getSourceInfo(), expression, toType);
+        return new JDynamicCastOperation(expression.getSourceInfo(), expression, expected);
       } else {
         return expression;
       }
diff --git a/jack/src/com/android/jack/shrob/obfuscation/MappingApplier.java b/jack/src/com/android/jack/shrob/obfuscation/MappingApplier.java
index 08b7852..ab372e2 100644
--- a/jack/src/com/android/jack/shrob/obfuscation/MappingApplier.java
+++ b/jack/src/com/android/jack/shrob/obfuscation/MappingApplier.java
@@ -20,6 +20,7 @@
 
 import com.android.jack.Jack;
 import com.android.jack.JackIOException;
+import com.android.jack.frontend.MethodIdDuplicateRemover.UniqMethodIds;
 import com.android.jack.ir.ast.CanBeRenamed;
 import com.android.jack.ir.ast.HasName;
 import com.android.jack.ir.ast.JClassOrInterface;
@@ -39,6 +40,7 @@
 import com.android.jack.transformations.request.TransformationRequest;
 import com.android.jack.util.NamingTools;
 import com.android.sched.marker.MarkerManager;
+import com.android.sched.schedulable.Constraint;
 import com.android.sched.schedulable.Transform;
 import com.android.sched.util.log.LoggerFactory;
 
@@ -58,6 +60,7 @@
  * A class that parses a mapping file and rename the remapped nodes.
  */
 @Transform(add = {OriginalNameMarker.class, OriginalPackageMarker.class, KeepNameMarker.class})
+@Constraint(need = UniqMethodIds.class)
 public class MappingApplier {
 
   @Nonnull
diff --git a/jack/src/com/android/jack/shrob/obfuscation/Renamer.java b/jack/src/com/android/jack/shrob/obfuscation/Renamer.java
index bd9835c..a71f38a 100644
--- a/jack/src/com/android/jack/shrob/obfuscation/Renamer.java
+++ b/jack/src/com/android/jack/shrob/obfuscation/Renamer.java
@@ -18,6 +18,7 @@
 package com.android.jack.shrob.obfuscation;
 
 import com.android.jack.Jack;
+import com.android.jack.frontend.MethodIdDuplicateRemover.UniqMethodIds;
 import com.android.jack.ir.ast.CanBeRenamed;
 import com.android.jack.ir.ast.HasName;
 import com.android.jack.ir.ast.JClassOrInterface;
@@ -65,7 +66,8 @@
  */
 @HasKeyId
 @Description("Visitor that renames JNodes")
-@Constraint(need = {KeepNameMarker.class, OriginalNames.class}, no = FinalNames.class)
+@Constraint(need = {KeepNameMarker.class, OriginalNames.class, UniqMethodIds.class},
+    no = FinalNames.class)
 @Transform(remove = OriginalNames.class,
     add = {OriginalNameMarker.class, OriginalPackageMarker.class, FinalNames.class})
 @Use(MappingApplier.class)
diff --git a/jack/src/com/android/jack/shrob/shrink/Keeper.java b/jack/src/com/android/jack/shrob/shrink/Keeper.java
index f9a8652..55d276f 100644
--- a/jack/src/com/android/jack/shrob/shrink/Keeper.java
+++ b/jack/src/com/android/jack/shrob/shrink/Keeper.java
@@ -36,7 +36,7 @@
  */
 @Description("Marks all classes and members that will be kept when shrinking.")
 @Constraint(need = {ExtendingOrImplementingClassMarker.class})
-@Use(KeeperBrush.class)
+@Use({Tracer.class, KeeperBrush.class})
 @Optional(@ToSupport(feature = SourceVersion8.class,
     add = @Constraint(need = JAnnotation.RepeatedAnnotation.class)))
 public class Keeper implements RunnableSchedulable<JDefinedClassOrInterface> {
diff --git a/jack/src/com/android/jack/shrob/shrink/ShrinkAndMainDexTracer.java b/jack/src/com/android/jack/shrob/shrink/ShrinkAndMainDexTracer.java
index 3bffa40..707496a 100644
--- a/jack/src/com/android/jack/shrob/shrink/ShrinkAndMainDexTracer.java
+++ b/jack/src/com/android/jack/shrob/shrink/ShrinkAndMainDexTracer.java
@@ -38,7 +38,7 @@
  */
 @Description("Trace for shrink and main dex.")
 @Constraint(need = ExtendingOrImplementingClassMarker.class)
-@Use({KeeperBrush.class, MultiDexLegacyTracerBrush.class})
+@Use({Tracer.class, KeeperBrush.class, MultiDexLegacyTracerBrush.class})
 @Optional(@ToSupport(feature = SourceVersion8.class,
     add = @Constraint(need = JAnnotation.RepeatedAnnotation.class)))
 public class ShrinkAndMainDexTracer implements RunnableSchedulable<JDefinedClassOrInterface> {