Merge "Revert "Support code between def and use in IfWithConstantSimplifier"" into ub-jack
diff --git a/build.xml b/build.xml
index 285e4f5..95ee120 100644
--- a/build.xml
+++ b/build.xml
@@ -954,12 +954,14 @@
       <exclude name="com/android/jack/frontend/test007/**"/>
       <exclude name="com/android/jack/jill/test001/**"/>
       <exclude name="com/android/jack/jill/test002/**"/>
+      <exclude name="com/android/jack/jill/test003/**"/>
       <exclude name="com/android/jack/optimizations/lambdas/test001/**"/>
       <exclude name="com/android/jack/optimizations/lambdas/test002/**"/>
       <exclude name="com/android/jack/optimizations/lambdas/test003/**"/>
       <exclude name="com/android/jack/optimizations/lambdas/test004/**"/>
       <exclude name="com/android/jack/optimizations/lambdas/test005/**"/>
       <exclude name="com/android/jack/optimizations/lambdas/test006/**"/>
+      <exclude name="com/android/jack/optimizations/lambdas/test007/**"/>
       <exclude name="com/android/jack/sourcepath/test002/jack/Sourcepath002.java"/>
       <exclude name="com/android/jack/shrob/test062/jack/**"/>
       <classpath>
diff --git a/jack-api/src/com/android/jack/api/v03/Api03Config.java b/jack-api/src/com/android/jack/api/v03/Api03Config.java
index 5deb735..a70f57a 100644
--- a/jack-api/src/com/android/jack/api/v03/Api03Config.java
+++ b/jack-api/src/com/android/jack/api/v03/Api03Config.java
@@ -20,7 +20,7 @@
 import com.android.jack.api.v02.Api02Config;
 
 import java.io.File;
-import java.util.Collection;
+import java.util.List;
 
 import javax.annotation.Nonnull;
 
@@ -33,12 +33,12 @@
    * @param pluginNames Plugin names, each name must be unique
    * @throws ConfigurationException if something is wrong in Jack's configuration
    */
-  void setPluginNames(@Nonnull Collection<String> pluginNames) throws ConfigurationException;
+  void setPluginNames(@Nonnull List<String> pluginNames) throws ConfigurationException;
 
   /**
    * Sets the path where to find Jack plugins.
-   * @param pluginPath A collection of jar files where Jack plugins reside
+   * @param pluginPath The plugin path as a list
    * @throws ConfigurationException if something is wrong in Jack's configuration
    */
-  void setPluginPath(@Nonnull Collection<File> pluginPath) throws ConfigurationException;
+  void setPluginPath(@Nonnull List<File> pluginPath) throws ConfigurationException;
 }
diff --git a/jack-api/src/com/android/jack/api/v04c/Api04CandidateConfig.java b/jack-api/src/com/android/jack/api/v04/Api04Config.java
similarity index 92%
rename from jack-api/src/com/android/jack/api/v04c/Api04CandidateConfig.java
rename to jack-api/src/com/android/jack/api/v04/Api04Config.java
index 690970f..87665aa 100644
--- a/jack-api/src/com/android/jack/api/v04c/Api04CandidateConfig.java
+++ b/jack-api/src/com/android/jack/api/v04/Api04Config.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.jack.api.v04c;
+package com.android.jack.api.v04;
 
 import com.android.jack.api.v01.ConfigurationException;
 import com.android.jack.api.v03.Api03Config;
@@ -26,7 +26,7 @@
 /**
  * A configuration for API level 04 of the Jack compiler compatible with API level 03
  */
-public interface Api04CandidateConfig extends Api03Config {
+public interface Api04Config extends Api03Config {
   /**
    * Set the default {@link Charset} used when no charset is specified by file with
    * {@link HasCharset}.
diff --git a/jack-api/src/com/android/jack/api/v04c/HasCharset.java b/jack-api/src/com/android/jack/api/v04/HasCharset.java
similarity index 87%
rename from jack-api/src/com/android/jack/api/v04c/HasCharset.java
rename to jack-api/src/com/android/jack/api/v04/HasCharset.java
index a94a5b0..ec97efd 100644
--- a/jack-api/src/com/android/jack/api/v04c/HasCharset.java
+++ b/jack-api/src/com/android/jack/api/v04/HasCharset.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.jack.api.v04c;
+package com.android.jack.api.v04;
 
 import java.io.File;
 import java.nio.charset.Charset;
@@ -23,7 +23,7 @@
 
 /**
  * A {@link File} with a {@link Charset} different from the default one (
- * {@link Api04CandidateConfig#setDefaultCharset(Charset)}) must implement that interface.
+ * {@link Api04Config#setDefaultCharset(Charset)}) must implement that interface.
  */
 public interface HasCharset {
   /**
diff --git a/jack-tests/.classpath b/jack-tests/.classpath
index ce416be..f80657a 100644
--- a/jack-tests/.classpath
+++ b/jack-tests/.classpath
@@ -2,7 +2,7 @@
 <classpath>
 	<classpathentry kind="src" path="src"/>
 	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
-	<classpathentry excluding="com/android/jack/annotation/test014/jack/|com/android/jack/annotation/test015/jack/|com/android/jack/annotation/test016/jack/|com/android/jack/annotation/test017/jack/|com/android/jack/classpath/test002/lib1override/|com/android/jack/compiletime/test*/**|com/android/jack/enums/test003/link/Other.java|com/android/jack/enums/test003/link/Values.java|com/android/jack/error/test001/jack/A.java|com/android/jack/error/test002/jack/A.java|com/android/jack/frontend/test002/jack/PackageName/ClassInConflictingPackage.java|com/android/jack/frontend/test005/jack/|com/android/jack/frontend/test006/jack/|com/android/jack/frontend/test007/jack/|com/android/jack/frontend/test007/jackduplicate/|com/android/jack/frontend/test008/jack/NoOuterContext.java|com/android/jack/frontend/test010/jack/UnusedLocalVar.java|com/android/jack/frontend/test013/jack/ExtendingInnerOnly.java|com/android/jack/frontend/test014/jack/ExtendingInnerInStaticContext.java|com/android/jack/frontend/test015/jack/WithOuterContextButStatic.java|com/android/jack/frontend/test016/jack/WithDuplicated.java|com/android/jack/frontend/test017/jack/InvalidQualification.java|com/android/jack/jarjar/test003/dontcompile/|com/android/jack/jarjar/test004/dontcompile/|com/android/jack/java8/annotation/|com/android/jack/java8/defaultmethod/|com/android/jack/java8/gwt/|com/android/jack/java8/inference/|com/android/jack/java8/intersectiontype/|com/android/jack/java8/lambda/|com/android/jack/java8/methodref/|com/android/jack/java8/retrolambda/|com/android/jack/java8/staticmethod/|com/android/jack/java8/variable/|com/android/jack/lookup/test001/liboverride/|com/android/jack/nopackage/test*/**|com/android/jack/optimizations/lambdas/test*/**|com/android/jack/jarjar/test006/dontcompile/|com/android/jack/java8/bridges/|com/android/jack/jill/test001/|com/android/jack/jill/test002/|com/android/jack/sourcepath/test002/jack/Sourcepath002.java|com/android/jack/annotation/test019/jack/|com/android/jack/frontend/test018/jack/|com/android/jack/annotation/test020/jack/|com/android/jack/assign/test002/jack/|com/android/jack/annotation/processor/sample2/src/|com/android/jack/shrob/test062/jack/Tests.java|com/android/jack/shrob/test062/jack/A.java|com/android/jack/coverage/test006_v1/jack/|com/android/jack/coverage/test006_v2/jack/" kind="src" path="tests"/>
+	<classpathentry excluding="com/android/jack/annotation/test014/jack/|com/android/jack/annotation/test015/jack/|com/android/jack/annotation/test016/jack/|com/android/jack/annotation/test017/jack/|com/android/jack/classpath/test002/lib1override/|com/android/jack/compiletime/test*/**|com/android/jack/enums/test003/link/Other.java|com/android/jack/enums/test003/link/Values.java|com/android/jack/error/test001/jack/A.java|com/android/jack/error/test002/jack/A.java|com/android/jack/frontend/test002/jack/PackageName/ClassInConflictingPackage.java|com/android/jack/frontend/test005/jack/|com/android/jack/frontend/test006/jack/|com/android/jack/frontend/test007/jack/|com/android/jack/frontend/test007/jackduplicate/|com/android/jack/frontend/test008/jack/NoOuterContext.java|com/android/jack/frontend/test010/jack/UnusedLocalVar.java|com/android/jack/frontend/test013/jack/ExtendingInnerOnly.java|com/android/jack/frontend/test014/jack/ExtendingInnerInStaticContext.java|com/android/jack/frontend/test015/jack/WithOuterContextButStatic.java|com/android/jack/frontend/test016/jack/WithDuplicated.java|com/android/jack/frontend/test017/jack/InvalidQualification.java|com/android/jack/jarjar/test003/dontcompile/|com/android/jack/jarjar/test004/dontcompile/|com/android/jack/java8/annotation/|com/android/jack/java8/defaultmethod/|com/android/jack/java8/gwt/|com/android/jack/java8/inference/|com/android/jack/java8/intersectiontype/|com/android/jack/java8/lambda/|com/android/jack/java8/methodref/|com/android/jack/java8/retrolambda/|com/android/jack/java8/staticmethod/|com/android/jack/java8/variable/|com/android/jack/lookup/test001/liboverride/|com/android/jack/nopackage/test*/**|com/android/jack/optimizations/lambdas/test*/**|com/android/jack/jarjar/test006/dontcompile/|com/android/jack/java8/bridges/|com/android/jack/jill/test001/|com/android/jack/jill/test002/|com/android/jack/jill/test003/|com/android/jack/sourcepath/test002/jack/Sourcepath002.java|com/android/jack/annotation/test019/jack/|com/android/jack/frontend/test018/jack/|com/android/jack/annotation/test020/jack/|com/android/jack/assign/test002/jack/|com/android/jack/annotation/processor/sample2/src/|com/android/jack/shrob/test062/jack/Tests.java|com/android/jack/shrob/test062/jack/A.java|com/android/jack/coverage/test006_v1/jack/|com/android/jack/coverage/test006_v2/jack/" kind="src" path="tests"/>
 	<classpathentry kind="lib" path="libs/junit4.jar"/>
 	<classpathentry kind="lib" path="libs/antlr-runtime-lib.jar"/>
 	<classpathentry kind="lib" path="libs/dx-ref.jar"/>
diff --git a/jack-tests/src/com/android/jack/test/toolchain/JackApiV04Toolchain.java b/jack-tests/src/com/android/jack/test/toolchain/JackApiV04Toolchain.java
index 3764363..ee1cb29 100644
--- a/jack-tests/src/com/android/jack/test/toolchain/JackApiV04Toolchain.java
+++ b/jack-tests/src/com/android/jack/test/toolchain/JackApiV04Toolchain.java
@@ -26,7 +26,7 @@
 import com.android.jack.api.v01.ReporterKind;
 import com.android.jack.api.v01.VerbosityLevel;
 import com.android.jack.api.v02.JavaSourceVersion;
-import com.android.jack.api.v04c.Api04CandidateConfig;
+import com.android.jack.api.v04.Api04Config;
 import com.android.jack.test.TestConfigurationException;
 import com.android.sched.util.log.LoggerFactory;
 import com.android.sched.vfs.Container;
@@ -47,11 +47,11 @@
 public class JackApiV04Toolchain extends JackApiToolchainBase implements JackApiV04 {
 
   @Nonnull
-  private Api04CandidateConfig apiV04Config;
+  private Api04Config apiV04Config;
 
   JackApiV04Toolchain(@CheckForNull File jackPrebuilt) {
-    super(jackPrebuilt, Api04CandidateConfig.class);
-    apiV04Config = (Api04CandidateConfig) config;
+    super(jackPrebuilt, Api04Config.class);
+    apiV04Config = (Api04Config) config;
     addProperty(Options.USE_DEFAULT_LIBRARIES.getName(), "false");
   }
 
diff --git a/jack-tests/tests/com/android/jack/annotations/ForceInMainDex.java b/jack-tests/tests/com/android/jack/annotations/ForceInMainDex.java
index e37c392..466a685 100644
--- a/jack-tests/tests/com/android/jack/annotations/ForceInMainDex.java
+++ b/jack-tests/tests/com/android/jack/annotations/ForceInMainDex.java
@@ -24,7 +24,7 @@
 /**
  * Indicate that the type must always be in the main dex.
  */
-@Retention(RetentionPolicy.SOURCE)
+@Retention(RetentionPolicy.CLASS)
 @Target(ElementType.TYPE)
 public @interface ForceInMainDex {
 
diff --git a/jack-tests/tests/com/android/jack/annotations/MultiDexInstaller.java b/jack-tests/tests/com/android/jack/annotations/MultiDexInstaller.java
index b8d03dc..3f6cfb1 100644
--- a/jack-tests/tests/com/android/jack/annotations/MultiDexInstaller.java
+++ b/jack-tests/tests/com/android/jack/annotations/MultiDexInstaller.java
@@ -25,7 +25,7 @@
  * Indicate that the element may be used during the multidex legacy support installation. As such it
  * must be kept in main dex along with its dependencies.
  */
-@Retention(RetentionPolicy.SOURCE)
+@Retention(RetentionPolicy.CLASS)
 @Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR})
 public @interface MultiDexInstaller {
 
diff --git a/jack-tests/tests/com/android/jack/jill/JillTests.java b/jack-tests/tests/com/android/jack/jill/JillTests.java
index 08c2e50..1dec89a 100644
--- a/jack-tests/tests/com/android/jack/jill/JillTests.java
+++ b/jack-tests/tests/com/android/jack/jill/JillTests.java
@@ -17,6 +17,7 @@
 package com.android.jack.jill;
 
 import com.android.jack.test.TestsProperties;
+import com.android.jack.test.junit.KnownIssue;
 import com.android.jack.test.junit.Runtime;
 import com.android.jack.test.runner.RuntimeRunner;
 import com.android.jack.test.toolchain.AbstractTestTools;
@@ -45,6 +46,12 @@
     runJillTest("002");
   }
 
+  @Test
+  @Runtime
+  public void test003() throws Exception {
+    runJillTest("003");
+  }
+
   private static void runJillTest(@Nonnull String testNumber) throws Exception {
     String jackFolder = getJackFolder(testNumber);
     String jasminFolder = getJasminFolder(testNumber);
diff --git a/jack-tests/tests/com/android/jack/jill/test003/jack/Test003.java b/jack-tests/tests/com/android/jack/jill/test003/jack/Test003.java
new file mode 100644
index 0000000..2bdc26d
--- /dev/null
+++ b/jack-tests/tests/com/android/jack/jill/test003/jack/Test003.java
@@ -0,0 +1,54 @@
+/*
+ * 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.jill.test003.jack;
+
+import junit.framework.Assert;
+
+import org.junit.Test;
+
+public class Test003 {
+
+  @Test
+  public void test001() {
+    Assert.assertEquals(1.0 > 2.0, External.greaterThan1(1.0, 2.0));
+    Assert.assertEquals(2.0 > 1.0, External.greaterThan1(2.0, 1.0));
+    Assert.assertEquals(2.0 > 2.0, External.greaterThan1(2.0, 2.0));
+    Assert.assertEquals(2.0 > Double.NaN, External.greaterThan1(2.0, Double.NaN));
+    Assert.assertEquals(Double.NaN > Double.NaN, External.greaterThan1(Double.NaN, Double.NaN));
+    Assert.assertEquals(Double.NaN > 2.0, External.greaterThan1(Double.NaN, 2.0));
+  }
+
+  @Test
+  public void test002() {
+    Assert.assertEquals(1.0 > 2.0, External.greaterThan2(1.0, 2.0));
+    Assert.assertEquals(2.0 > 1.0, External.greaterThan2(2.0, 1.0));
+    Assert.assertEquals(2.0 > 2.0, External.greaterThan2(2.0, 2.0));
+    Assert.assertEquals(2.0 > Double.NaN, External.greaterThan2(2.0, Double.NaN));
+    Assert.assertEquals(Double.NaN > Double.NaN, External.greaterThan2(Double.NaN, Double.NaN));
+    Assert.assertEquals(Double.NaN > 2.0, External.greaterThan2(Double.NaN, 2.0));
+  }
+
+  @Test
+  public void test003() {
+    Assert.assertEquals(1.0 > 2.0, External.greaterThan3(1.0, 2.0));
+    Assert.assertEquals(2.0 > 1.0, External.greaterThan3(2.0, 1.0));
+    Assert.assertEquals(2.0 > 2.0, External.greaterThan3(2.0, 2.0));
+    Assert.assertEquals(2.0 > Double.NaN, External.greaterThan3(2.0, Double.NaN));
+    Assert.assertEquals(Double.NaN > Double.NaN, External.greaterThan3(Double.NaN, Double.NaN));
+    Assert.assertEquals(Double.NaN > 2.0, External.greaterThan3(Double.NaN, 2.0));
+  }
+}
\ No newline at end of file
diff --git a/jack-tests/tests/com/android/jack/jill/test003/jasmin/External.j b/jack-tests/tests/com/android/jack/jill/test003/jasmin/External.j
new file mode 100644
index 0000000..73fbe02
--- /dev/null
+++ b/jack-tests/tests/com/android/jack/jill/test003/jasmin/External.j
@@ -0,0 +1,77 @@
+; 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.
+
+.class public com/android/jack/jill/test003/jack/External
+.super java/lang/Object
+
+.method public <init>()V
+    aload_0
+    invokespecial java/lang/Object/<init>()V
+    return
+.end method
+
+.method public static greaterThan1(DD)Z
+        .limit locals 4
+        .limit stack 3
+    dload_0
+    dload_2
+    dcmpl
+    aconst_null
+    swap
+    ifle if_label
+    pop
+    iconst_1
+    ireturn
+if_label:
+    pop
+    iconst_0
+    ireturn
+.end method
+
+.method public static greaterThan2(DD)Z
+        .limit locals 4
+        .limit stack 5
+    aconst_null
+    dload_0
+    dload_2
+    dcmpl
+    swap
+    pop
+    ifle if_label
+    iconst_1
+    ireturn
+if_label:
+    iconst_0
+    ireturn
+.end method
+
+.method public static greaterThan3(DD)Z
+        .limit locals 4
+        .limit stack 3
+    dload_0
+    dload_2
+    dcmpl
+    iconst_1
+    iconst_2
+    iadd
+    swap
+    ifle if_label
+    pop
+    iconst_1
+    ireturn
+if_label:
+    pop
+    iconst_0
+    ireturn
+.end method
diff --git a/jack-tests/tests/com/android/jack/jill/test003/jasmin/jarInput.jar b/jack-tests/tests/com/android/jack/jill/test003/jasmin/jarInput.jar
new file mode 100644
index 0000000..6e92ca3
--- /dev/null
+++ b/jack-tests/tests/com/android/jack/jill/test003/jasmin/jarInput.jar
Binary files differ
diff --git a/jack-tests/tests/com/android/jack/optimizations/lambdas/LambdaClassesValidator.java b/jack-tests/tests/com/android/jack/optimizations/lambdas/LambdaClassesValidator.java
index 4185cd5..87b0007 100644
--- a/jack-tests/tests/com/android/jack/optimizations/lambdas/LambdaClassesValidator.java
+++ b/jack-tests/tests/com/android/jack/optimizations/lambdas/LambdaClassesValidator.java
@@ -83,28 +83,31 @@
 
     // Interfaces
     List<String> items = new ArrayList<>(type.getInterfaceNames());
-    print(sb, "implements", items);
+    print(sb, "implements", items, false);
 
     // Fields
     items.clear();
     for (DexField field : type.getFields()) {
       items.add(field.getId());
     }
-    print(sb, "fields", items);
+    print(sb, "fields", items, true);
 
     // Methods
     items.clear();
     for (DexMethod method : type.getMethods()) {
       items.add(method.getId());
     }
-    print(sb, "methods", items);
+    print(sb, "methods", items, true);
   }
 
   private static void print(
-      @Nonnull StringBuilder sb, @Nonnull String header, @Nonnull List<String> items) {
+      @Nonnull StringBuilder sb, @Nonnull String header,
+      @Nonnull List<String> items, boolean ordered) {
     if (items.size() > 0) {
       sb.append("  - ").append(header).append(":\n");
-      Collections.sort(items);
+      if (ordered) {
+        Collections.sort(items);
+      }
       for (String item : items) {
         sb.append("    ").append(item).append('\n');
       }
diff --git a/jack-tests/tests/com/android/jack/optimizations/lambdas/LambdaTests.java b/jack-tests/tests/com/android/jack/optimizations/lambdas/LambdaTests.java
index 280a1db..5d9ee8b 100644
--- a/jack-tests/tests/com/android/jack/optimizations/lambdas/LambdaTests.java
+++ b/jack-tests/tests/com/android/jack/optimizations/lambdas/LambdaTests.java
@@ -69,24 +69,26 @@
 
     Class(@Nonnull String name, @Nonnull String... interfaces) {
       builder.append(name).append("\n");
-      impl(interfaces, "  - implements:\n");
+      printImpl(interfaces, "  - implements:\n", false);
     }
 
     @Nonnull
     Class methods(@Nonnull String... signatures) {
-      return impl(signatures, "  - methods:\n");
+      return printImpl(signatures, "  - methods:\n", true);
     }
 
     @Nonnull
     Class fields(@Nonnull String... fields) {
-      return impl(fields, "  - fields:\n");
+      return printImpl(fields, "  - fields:\n", true);
     }
 
     @Nonnull
-    private Class impl(@Nonnull String[] names, @Nonnull String str) {
+    private Class printImpl(@Nonnull String[] names, @Nonnull String str, boolean ordered) {
       if (names.length > 0) {
         builder.append(str);
-        Arrays.sort(names);
+        if (ordered) {
+          Arrays.sort(names);
+        }
         for (String n : names) {
           builder.append("    ").append(n).append("\n");
         }
@@ -635,4 +637,43 @@
         config(LambdaGroupingScope.PACKAGE, /* interfaces: */false, /* stateless: */false),
         new LambdaClassesValidator(TEST006_PACKAGE));
   }
+
+  // ===============================================================================================
+
+  @Nonnull
+  private static final String PKG_007 = "com.android.jack.optimizations.lambdas.test007";
+
+  @Nonnull
+  private static final String TEST007_DEFAULT = "" +
+      new Class(lambda(PKG_007, 0), types(PKG_007, "Ic", "Ia", "Ib"))
+          .methods("<init>()V", "$m$0()Ljava/lang/String;", "m()Ljava/lang/String;") +
+      new Class(lambda(PKG_007, 1), types(PKG_007, "Ic", "Iaa"))
+          .methods("<init>()V", "$m$0()Ljava/lang/String;", "m()Ljava/lang/String;") +
+      new Class(lambda(PKG_007, 2), types(PKG_007, "Ic", "Ib"))
+          .methods("<init>()V", "$m$0()Ljava/lang/String;", "m()Ljava/lang/String;") +
+      new Class(lambda(PKG_007, 3), types(PKG_007, "Ic", "Ib", "Ia"))
+          .methods("<init>()V", "$m$0()Ljava/lang/String;", "m()Ljava/lang/String;") +
+      "";
+
+  @Nonnull
+  private static final String TEST007_MERGE_INTERFACES = "" +
+      new Class(lambda(PKG_007, 0), types(PKG_007, "Ia", "Iaa", "Ib", "Ic"))
+          .fields("$id:B")
+          .methods("<init>(B)V", "m()Ljava/lang/String;",
+              "$m$0()Ljava/lang/String;", "$m$1()Ljava/lang/String;",
+              "$m$2()Ljava/lang/String;", "$m$3()Ljava/lang/String;") +
+      "";
+
+
+  @Test
+  @Runtime
+  public void test007() throws Exception {
+    compileAndValidate(PKG_007,
+        config(LambdaGroupingScope.TYPE, /* interfaces: */false, /* stateless: */false),
+        new LambdaClassesValidator(TEST007_DEFAULT));
+
+    compileAndValidate(PKG_007,
+        config(LambdaGroupingScope.TYPE, /* interfaces: */true, /* stateless: */false),
+        new LambdaClassesValidator(TEST007_MERGE_INTERFACES));
+  }
 }
diff --git a/jack-api/src/com/android/jack/api/v04c/HasCharset.java b/jack-tests/tests/com/android/jack/optimizations/lambdas/test007/dx/Tests.java
similarity index 60%
copy from jack-api/src/com/android/jack/api/v04c/HasCharset.java
copy to jack-tests/tests/com/android/jack/optimizations/lambdas/test007/dx/Tests.java
index a94a5b0..f8d4ba1 100644
--- a/jack-api/src/com/android/jack/api/v04c/HasCharset.java
+++ b/jack-tests/tests/com/android/jack/optimizations/lambdas/test007/dx/Tests.java
@@ -14,21 +14,18 @@
  * limitations under the License.
  */
 
-package com.android.jack.api.v04c;
+package com.android.jack.optimizations.lambdas.test007.dx;
 
-import java.io.File;
-import java.nio.charset.Charset;
+import com.android.jack.optimizations.lambdas.test007.jack.*;
 
-import javax.annotation.Nonnull;
+import junit.framework.Assert;
+import org.junit.Test;
 
-/**
- * A {@link File} with a {@link Charset} different from the default one (
- * {@link Api04CandidateConfig#setDefaultCharset(Charset)}) must implement that interface.
- */
-public interface HasCharset {
-  /**
-   * @return the {@link Charset}
-   */
-  @Nonnull
-  Charset getCharset();
+/** Just touch all the classes */
+public class Tests {
+  @Test
+  public void test001() throws Throwable {
+    Assert.assertEquals(
+      "Ia&Ic&Ib Ib&Ic&Ia Iaa&Ic&Ia Ib&Ic", A.test());
+  }
 }
diff --git a/jack-api/src/com/android/jack/api/v04c/HasCharset.java b/jack-tests/tests/com/android/jack/optimizations/lambdas/test007/jack/A.java
similarity index 60%
copy from jack-api/src/com/android/jack/api/v04c/HasCharset.java
copy to jack-tests/tests/com/android/jack/optimizations/lambdas/test007/jack/A.java
index a94a5b0..cfc733e 100644
--- a/jack-api/src/com/android/jack/api/v04c/HasCharset.java
+++ b/jack-tests/tests/com/android/jack/optimizations/lambdas/test007/jack/A.java
@@ -14,21 +14,27 @@
  * limitations under the License.
  */
 
-package com.android.jack.api.v04c;
+package com.android.jack.optimizations.lambdas.test007.jack;
 
-import java.io.File;
-import java.nio.charset.Charset;
+interface Ia {
+}
 
-import javax.annotation.Nonnull;
+interface Iaa extends Ia {
+}
 
-/**
- * A {@link File} with a {@link Charset} different from the default one (
- * {@link Api04CandidateConfig#setDefaultCharset(Charset)}) must implement that interface.
- */
-public interface HasCharset {
-  /**
-   * @return the {@link Charset}
-   */
-  @Nonnull
-  Charset getCharset();
+interface Ib {
+}
+
+interface Ic {
+  String m();
+}
+
+public class A {
+  public static String test() {
+    return
+        ((Ia & Ic & Ib) () -> "Ia&Ic&Ib ").m() +
+        ((Ib & Ic & Ia) () -> "Ib&Ic&Ia ").m() +
+        ((Iaa & Ic & Ia) () -> "Iaa&Ic&Ia ").m() +
+        ((Ib & Ic) () -> "Ib&Ic").m();
+  }
 }
diff --git a/jack/src/com/android/jack/api/impl/JackProviderImpl.java b/jack/src/com/android/jack/api/impl/JackProviderImpl.java
index 6e241e9..1192187 100644
--- a/jack/src/com/android/jack/api/impl/JackProviderImpl.java
+++ b/jack/src/com/android/jack/api/impl/JackProviderImpl.java
@@ -31,9 +31,9 @@
 import com.android.jack.api.v02.impl.Api02Feature;
 import com.android.jack.api.v03.Api03Config;
 import com.android.jack.api.v03.impl.Api03Feature;
+import com.android.jack.api.v04.Api04Config;
 import com.android.jack.api.v04.impl.Api04ConfigImpl;
 import com.android.jack.api.v04.impl.Api04Feature;
-import com.android.jack.api.v04c.Api04CandidateConfig;
 import com.android.jack.management.CleanCodeRequest;
 import com.android.jack.management.CleanDiskRequest;
 import com.android.jack.management.CleanMemoryRequest;
@@ -63,13 +63,13 @@
     impl.put(Api01Config.class, Api04ConfigImpl.class);
     impl.put(Api02Config.class, Api04ConfigImpl.class);
     impl.put(Api03Config.class, Api04ConfigImpl.class);
-    impl.put(Api04CandidateConfig.class, Api04ConfigImpl.class);
+    impl.put(Api04Config.class, Api04ConfigImpl.class);
     impl.put(Cli01Config.class, Cli01ConfigImpl.class);
 
     features.put(Api01Config.class, Api01Feature.class);
     features.put(Api02Config.class, Api02Feature.class);
     features.put(Api03Config.class, Api03Feature.class);
-    features.put(Api04CandidateConfig.class, Api04Feature.class);
+    features.put(Api04Config.class, Api04Feature.class);
     features.put(Cli01Config.class, Api04Feature.class);
   }
 
diff --git a/jack/src/com/android/jack/api/v03/impl/Api03ConfigImpl.java b/jack/src/com/android/jack/api/v03/impl/Api03ConfigImpl.java
index 4222b89..9b6ead1 100644
--- a/jack/src/com/android/jack/api/v03/impl/Api03ConfigImpl.java
+++ b/jack/src/com/android/jack/api/v03/impl/Api03ConfigImpl.java
@@ -22,7 +22,7 @@
 import com.android.jack.api.v03.Api03Config;
 
 import java.io.File;
-import java.util.Collection;
+import java.util.List;
 
 import javax.annotation.Nonnull;
 
@@ -35,12 +35,12 @@
   }
 
   @Override
-  public void setPluginPath(@Nonnull Collection<File> pluginPath) {
+  public void setPluginPath(@Nonnull List<File> pluginPath) {
     options.setPluginPath(Joiner.on(File.pathSeparator).join(pluginPath));
   }
 
   @Override
-  public void setPluginNames(@Nonnull Collection<String> pluginNames) {
+  public void setPluginNames(@Nonnull List<String> pluginNames) {
     options.setPluginNames(Joiner.on(',').join(pluginNames));
   }
 }
diff --git a/jack/src/com/android/jack/api/v04/impl/Api04ConfigImpl.java b/jack/src/com/android/jack/api/v04/impl/Api04ConfigImpl.java
index c4ec09c..48d4a45 100644
--- a/jack/src/com/android/jack/api/v04/impl/Api04ConfigImpl.java
+++ b/jack/src/com/android/jack/api/v04/impl/Api04ConfigImpl.java
@@ -17,8 +17,8 @@
 package com.android.jack.api.v04.impl;
 
 import com.android.jack.api.v03.impl.Api03ConfigImpl;
-import com.android.jack.api.v04c.Api04CandidateConfig;
-import com.android.jack.api.v04c.HasCharset;
+import com.android.jack.api.v04.Api04Config;
+import com.android.jack.api.v04.HasCharset;
 
 import java.io.File;
 import java.nio.charset.Charset;
@@ -31,7 +31,7 @@
 /**
  * A configuration implementation for API level 04 of the Jack compiler.
  */
-public class Api04ConfigImpl extends Api03ConfigImpl implements Api04CandidateConfig {
+public class Api04ConfigImpl extends Api03ConfigImpl implements Api04Config {
   public Api04ConfigImpl() {
     super();
   }
diff --git a/jack/src/com/android/jack/transformations/lambda/LambdaCollection.java b/jack/src/com/android/jack/transformations/lambda/LambdaCollection.java
index d09beb1..381585f 100644
--- a/jack/src/com/android/jack/transformations/lambda/LambdaCollection.java
+++ b/jack/src/com/android/jack/transformations/lambda/LambdaCollection.java
@@ -158,8 +158,8 @@
   private Key createKey(@Nonnull String lambdaId, @Nonnull JLambda lambda,
       @Nonnull JDefinedClassOrInterface currentType) {
 
-    String interfaceSignatureId =
-        mergeInterfaces ? "" : LambdaInterfaceSignature.forLambda(lambda).getUniqueId();
+    String interfaceSignatureId = mergeInterfaces ? "" :
+        LambdaInterfaceSignature.forLambda(lambda, /* normalize = */ false).getUniqueId();
 
     String scopeId;
     switch (groupingScope) {
diff --git a/jack/src/com/android/jack/transformations/lambda/LambdaGroupClassFinalizer.java b/jack/src/com/android/jack/transformations/lambda/LambdaGroupClassFinalizer.java
index fa6521b..3b70569 100644
--- a/jack/src/com/android/jack/transformations/lambda/LambdaGroupClassFinalizer.java
+++ b/jack/src/com/android/jack/transformations/lambda/LambdaGroupClassFinalizer.java
@@ -102,6 +102,8 @@
 
   private final boolean simplifyStateless =
       ThreadConfig.get(Options.LAMBDA_SIMPLIFY_STATELESS).booleanValue();
+  private final boolean mergeInterfaces =
+      ThreadConfig.get(Options.LAMBDA_MERGE_INTERFACES).booleanValue();
   @Nonnull
   private final JPhantomLookup phantomLookup =
       Jack.getSession().getPhantomLookup();
@@ -272,9 +274,7 @@
       JDefinedClass groupClass = this.group.getGroupClass();
 
       // Add implements clause
-      List<JInterface> interfaces =
-          LambdaInterfaceSignature.normalizeInterfaces(this.group.getLambdas());
-      for (JInterface inter : interfaces) {
+      for (JInterface inter : getInterfaces()) {
         groupClass.addImplements(inter);
       }
 
@@ -312,6 +312,20 @@
       request.commit();
     }
 
+    @Nonnull
+    private List<JInterface> getInterfaces() {
+      List<JLambda> lambdas = this.group.getLambdas();
+      if (mergeInterfaces) {
+        return LambdaInterfaceSignature.normalizeInterfaces(lambdas);
+      }
+
+      // If there is no interface merging, we use non-normalized interface signature
+      // which means that all the lambdas in the group should have the same
+      // set and order of the interfaces, so we just take them from the first one.
+      assert lambdas.size() > 0;
+      return LambdaInterfaceSignature.extractOrderedInterfaces(lambdas.get(0));
+    }
+
     /**
      * Create a static constructor of the lambda group class in a form:
      * <pre>
diff --git a/jack/src/com/android/jack/transformations/lambda/LambdaInterfaceSignature.java b/jack/src/com/android/jack/transformations/lambda/LambdaInterfaceSignature.java
index b959978..6e24e9b 100644
--- a/jack/src/com/android/jack/transformations/lambda/LambdaInterfaceSignature.java
+++ b/jack/src/com/android/jack/transformations/lambda/LambdaInterfaceSignature.java
@@ -36,6 +36,28 @@
  * by a particular lambda class, i.e. essentially the transitive closure of the
  * interfaces the class explicitly specifies in its 'implements' clause.
  *
+ * Interface signature can be normalized or not. If it is normalized, it represents
+ * interfaces in a way, when order and set of interfaces 'directly' implemented
+ * by the lambda does not make difference, like in the example below. If the signature
+ * is not normalized, the set and order of 'directly' implemented interfaces matter.
+ *
+ * <pre>
+ *    interface A {}
+ *    interface B extends A {}
+ *    interface C { void m(); }
+ *
+ *    class Foo {
+ *      void bar() {
+ *        ((C & A & B) () -> {}).m();
+ *        ((C & B & A) () -> {}).m();
+ *        ((C & B) () -> {}).m();
+ *      }
+ *    }
+ * </pre>
+ *
+ * In the example above, normalized interface signature of these three
+ * lambdas will be the same, but not normalized signature will be different.
+ *
  * NOTE: interface signature is used for hashing and sorting purposes only, it is
  * not intended to actually store the exact set of the interfaces.
  */
@@ -51,19 +73,25 @@
   @Nonnull
   private final String allInterfaces;
 
-  private LambdaInterfaceSignature(@Nonnull JLambda lambda) {
+  private LambdaInterfaceSignature(@Nonnull JLambda lambda, boolean normalize) {
     StringBuilder sb = new StringBuilder();
-    List<JInterface> interfaces = new ArrayList<>(lambda.getInterfaceBounds());
-    interfaces.add(lambda.getType());
-    for (String key : getNormalizedInterfacesMap(interfaces).keySet()) {
-      sb.append(key);
+    List<JInterface> interfaces = extractOrderedInterfaces(lambda);
+    if (normalize) {
+      for (String key : getNormalizedInterfacesMap(interfaces).keySet()) {
+        sb.append(key);
+      }
+    } else {
+      for (JInterface inter : interfaces) {
+        sb.append(FORMATTER.getName(inter));
+      }
     }
     allInterfaces = sb.toString();
   }
 
   /** create an interface signature for a given lambda */
-  static LambdaInterfaceSignature forLambda(@Nonnull JLambda lambda) {
-    return new LambdaInterfaceSignature(lambda);
+  @Nonnull
+  static LambdaInterfaceSignature forLambda(@Nonnull JLambda lambda, boolean normalize) {
+    return new LambdaInterfaceSignature(lambda, normalize);
   }
 
   /** A short id that can be used to uniquely identify and sort interface signatures */
@@ -73,6 +101,23 @@
   }
 
   /**
+   * Extracts interfaces from lambda in the order they should be present
+   * in the implementation class.
+   */
+  @Nonnull
+  static List<JInterface> extractOrderedInterfaces(@Nonnull JLambda lambda) {
+    ArrayList<JInterface> interfaces = new ArrayList<>();
+    JInterface mainInterface = lambda.getType();
+    interfaces.add(mainInterface);
+    for (JInterface bound : lambda.getInterfaceBounds()) {
+      if (!bound.isSameType(mainInterface)) {
+        interfaces.add(bound);
+      }
+    }
+    return interfaces;
+  }
+
+  /**
    * Returns a normalized set of interfaces implemented by a given list of lambdas.
    *
    * We call a set of interfaces normalized if removing any of the interfaces from
@@ -88,8 +133,7 @@
   static List<JInterface> normalizeInterfaces(@Nonnull List<JLambda> lambdas) {
     ArrayList<JInterface> interfaces = new ArrayList<>();
     for (JLambda lambda : lambdas) {
-      interfaces.addAll(lambda.getInterfaceBounds());
-      interfaces.add(lambda.getType());
+      interfaces.addAll(extractOrderedInterfaces(lambda));
     }
     return new ArrayList<>(getNormalizedInterfacesMap(interfaces).values());
   }
diff --git a/jill/src/com/android/jill/frontend/java/MethodBodyWriter.java b/jill/src/com/android/jill/frontend/java/MethodBodyWriter.java
index 7410744..cf109c1 100644
--- a/jill/src/com/android/jill/frontend/java/MethodBodyWriter.java
+++ b/jill/src/com/android/jill/frontend/java/MethodBodyWriter.java
@@ -2058,40 +2058,63 @@
         // be use to known the type of variable to read and nextFrame must be use to known the type
         // of variable to write.
         Variable tmpVar = getTempVarFromTopOfStackMinus1(frame);
+        boolean topMinus1IsVirtual = isVirtualStackVariable(frame, TOP_OF_STACK - 1);
+        boolean topIsVirtual = isVirtualStackVariable(frame, TOP_OF_STACK);
 
-        // tmpVar = frame.stack[frame.stack.size() + TOP_OF_STACK - 1]
-        writeDebugBegin(currentClass, currentLine);
-        writer.writeCatchBlockIds(currentCatchList);
-        writer.writeKeyword(Token.EXPRESSION_STATEMENT);
-        writer.writeOpen();
-        writeDebugBegin(currentClass, currentLine);
-        writer.writeKeyword(Token.ASG_OPERATION);
-        writer.writeOpen();
-        writeLocalRef(tmpVar);
-        writeStackAccess(frame, TOP_OF_STACK - 1);
-        writeDebugEnd(currentClass, currentLine);
-        writer.writeClose();
-        writeDebugEnd(currentClass, currentLine);
-        writer.writeClose();
+        if (topMinus1IsVirtual) {
+          // Virtual swap, stack variables will be transform as below:
+          // var_stk_top = value_stk_top; var_stk_top-1 is virtual
+          // =>
+          // var_stk_top is virtual, var_stk_top-1 = value_stk_top
+          cmpOperands.put(getStackVariable(nextFrame, TOP_OF_STACK),
+              cmpOperands.remove(getStackVariable(frame, TOP_OF_STACK - 1)));
+        } else {
+          // tmpVar = frame.stack[frame.stack.size() + TOP_OF_STACK - 1]
+          writeDebugBegin(currentClass, currentLine);
+          writer.writeCatchBlockIds(currentCatchList);
+          writer.writeKeyword(Token.EXPRESSION_STATEMENT);
+          writer.writeOpen();
+          writeDebugBegin(currentClass, currentLine);
+          writer.writeKeyword(Token.ASG_OPERATION);
+          writer.writeOpen();
+          writeLocalRef(tmpVar);
+          writeStackAccess(frame, TOP_OF_STACK - 1);
+          writeDebugEnd(currentClass, currentLine);
+          writer.writeClose();
+          writeDebugEnd(currentClass, currentLine);
+          writer.writeClose();
+        }
 
-        // nexFrame.stack[nexFrame.stack.size() + TOP_OF_STACK - 1] =
-        // frame.stack[frame.stack.size() + TOP_OF_STACK]
-        writeAssign(frame, TOP_OF_STACK, nextFrame, TOP_OF_STACK - 1);
+        if (topIsVirtual) {
+          // Virtual swap, stack variables will be transform as below:
+          // var_stk_top is virtual; var_stk_top-1 = value_stk_top-1
+          // =>
+          // var_stk_top = value_stk_top-1, var_stk_top-1 is virtual
+          cmpOperands.put(getStackVariable(nextFrame, TOP_OF_STACK - 1),
+              cmpOperands.remove(getStackVariable(frame, TOP_OF_STACK)));
+        } else {
+          // nexFrame.stack[nexFrame.stack.size() + TOP_OF_STACK - 1] =
+          // frame.stack[frame.stack.size() + TOP_OF_STACK]
+          writeAssign(frame, TOP_OF_STACK, nextFrame, TOP_OF_STACK - 1);
+        }
 
-        // nextFrame.stack[nextFrame.stack.size() + TOP_OF_STACK] = tmpVar
-        writeDebugBegin(currentClass, currentLine);
-        writer.writeCatchBlockIds(currentCatchList);
-        writer.writeKeyword(Token.EXPRESSION_STATEMENT);
-        writer.writeOpen();
-        writeDebugBegin(currentClass, currentLine);
-        writer.writeKeyword(Token.ASG_OPERATION);
-        writer.writeOpen();
-        writeStackAccess(nextFrame, TOP_OF_STACK);
-        writeLocalRef(tmpVar);
-        writeDebugEnd(currentClass, currentLine);
-        writer.writeClose();
-        writeDebugEnd(currentClass, currentLine);
-        writer.writeClose();
+        // No need to assign the variable (nextFrame, top_of_stack) because it is virtual variable
+        if (!topMinus1IsVirtual) {
+          // nextFrame.stack[nextFrame.stack.size() + TOP_OF_STACK] = tmpVar
+          writeDebugBegin(currentClass, currentLine);
+          writer.writeCatchBlockIds(currentCatchList);
+          writer.writeKeyword(Token.EXPRESSION_STATEMENT);
+          writer.writeOpen();
+          writeDebugBegin(currentClass, currentLine);
+          writer.writeKeyword(Token.ASG_OPERATION);
+          writer.writeOpen();
+          writeStackAccess(nextFrame, TOP_OF_STACK);
+          writeLocalRef(tmpVar);
+          writeDebugEnd(currentClass, currentLine);
+          writer.writeClose();
+          writeDebugEnd(currentClass, currentLine);
+          writer.writeClose();
+        }
         break;
       }
       case DUP: {
@@ -2195,6 +2218,11 @@
     }
   }
 
+  private boolean isVirtualStackVariable(@Nonnull Frame<BasicValue> frame, int stackIdx) {
+    Variable stackVar = getStackVariable(frame, stackIdx);
+    return cmpOperands.containsKey(stackVar);
+  }
+
   private void writeInsn(@Nonnull Frame<BasicValue> nextFrame, @Nonnull LdcInsnNode ldcInsn)
       throws IOException {
     writeDebugBegin(currentClass, currentLine);