Merge "Cleanup of -assumexxx tests"
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 215ced9..320180e 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -369,11 +369,11 @@
           timing.end();
         }
         appView.setGraphLense(new MemberRebindingAnalysis(appViewWithLiveness).run());
-        // Class merging requires inlining.
-        if (options.enableClassMerging && options.enableInlining) {
+        if (options.enableClassMerging) {
           timing.begin("ClassMerger");
           VerticalClassMerger classMerger =
-              new VerticalClassMerger(application, appViewWithLiveness, executorService, timing);
+              new VerticalClassMerger(
+                  application, appViewWithLiveness, executorService, options, timing);
           appView.setGraphLense(classMerger.run());
           timing.end();
           application = application.asDirect().rewrittenWithLense(appView.getGraphLense());
diff --git a/src/main/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilder.java b/src/main/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilder.java
index 3953b22..f4f3c7e 100644
--- a/src/main/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilder.java
+++ b/src/main/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilder.java
@@ -135,7 +135,7 @@
           ZipEntry entry = entries.nextElement();
           if (!entry.getName().endsWith(".class")) {
             try (InputStream stream = zipFile.getInputStream(entry)) {
-              addEntry(entry.getName(), stream, entry.getTime(), out);
+              addEntry(entry.getName(), stream, out);
             }
           } else {
             toDex.add(entry);
@@ -150,7 +150,7 @@
         for (int i = 0; i < futures.size(); i++) {
           ZipEntry entry = toDex.get(i);
           DexConsumer consumer = futures.get(i).get();
-          addEntry(entry.getName() + ".dex", consumer.getBytes(), entry.getTime(), out);
+          addEntry(entry.getName() + ".dex", consumer.getBytes(), out);
         }
       }
     } finally {
@@ -178,20 +178,19 @@
     return consumer;
   }
 
-  private static void addEntry(String name, InputStream stream, long time, ZipOutputStream out)
+  private static void addEntry(String name, InputStream stream, ZipOutputStream out)
       throws IOException {
-    addEntry(name, ByteStreams.toByteArray(stream), time, out);
+    addEntry(name, ByteStreams.toByteArray(stream), out);
   }
 
-  private static void addEntry(String name, byte[] bytes, long time, ZipOutputStream out)
-      throws IOException {
+  private static void addEntry(String name, byte[] bytes, ZipOutputStream out) throws IOException {
     ZipEntry zipEntry = new ZipEntry(name);
     CRC32 crc32 = new CRC32();
     crc32.update(bytes);
     zipEntry.setSize(bytes.length);
     zipEntry.setMethod(ZipEntry.STORED);
     zipEntry.setCrc(crc32.getValue());
-    zipEntry.setTime(time);
+    zipEntry.setTime(0);
     out.putNextEntry(zipEntry);
     out.write(bytes);
     out.closeEntry();
diff --git a/src/main/java/com/android/tools/r8/dexfilemerger/DexFileMerger.java b/src/main/java/com/android/tools/r8/dexfilemerger/DexFileMerger.java
index 8b0f6a8..b5db1ae 100644
--- a/src/main/java/com/android/tools/r8/dexfilemerger/DexFileMerger.java
+++ b/src/main/java/com/android/tools/r8/dexfilemerger/DexFileMerger.java
@@ -249,8 +249,7 @@
             getStream(handler),
             getDexFileName(fileIndex),
             ByteDataView.of(data),
-            ZipEntry.DEFLATED,
-            true);
+            ZipEntry.DEFLATED);
         hasWrittenSomething = true;
       } catch (IOException e) {
         handler.error(new ExceptionDiagnostic(e, origin));
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index 8dec498..3e4700c 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -39,6 +39,7 @@
 import com.android.tools.r8.logging.Log;
 import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
 import com.android.tools.r8.utils.FieldSignatureEquivalence;
+import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.MethodSignatureEquivalence;
 import com.android.tools.r8.utils.Timing;
 import com.google.common.base.Equivalence;
@@ -163,6 +164,7 @@
   private final ExecutorService executorService;
   private final GraphLense graphLense;
   private final MethodPoolCollection methodPoolCollection;
+  private final InternalOptions options;
   private final Timing timing;
   private Collection<DexMethod> invokes;
 
@@ -185,12 +187,14 @@
       DexApplication application,
       AppView<AppInfoWithLiveness> appView,
       ExecutorService executorService,
+      InternalOptions options,
       Timing timing) {
     this.application = application;
     this.appInfo = appView.getAppInfo();
     this.executorService = executorService;
     this.graphLense = appView.getGraphLense();
     this.methodPoolCollection = new MethodPoolCollection(application);
+    this.options = options;
     this.renamedMembersLense = new VerticalClassMergerGraphLense.Builder();
     this.timing = timing;
 
@@ -1494,20 +1498,20 @@
   }
 
   private boolean disallowInlining(DexEncodedMethod method, DexType invocationContext) {
-    // TODO(christofferqa): Determine the situations where markForceInline() may fail, and ensure
-    // that we always return true here in these cases.
-    if (method.getCode().isJarCode()) {
-      JarCode jarCode = method.getCode().asJarCode();
-      ConstraintWithTarget constraint =
-          jarCode.computeInliningConstraint(
-              method,
-              appInfo,
-              new SingleTypeMapperGraphLense(method.method.holder, invocationContext),
-              invocationContext);
-      return constraint == ConstraintWithTarget.NEVER;
+    if (options.enableInlining) {
+      if (method.getCode().isJarCode()) {
+        JarCode jarCode = method.getCode().asJarCode();
+        ConstraintWithTarget constraint =
+            jarCode.computeInliningConstraint(
+                method,
+                appInfo,
+                new SingleTypeMapperGraphLense(method.method.holder, invocationContext),
+                invocationContext);
+        return constraint == ConstraintWithTarget.NEVER;
+      }
+      // TODO(christofferqa): For non-jar code we currently cannot guarantee that markForceInline()
+      // will succeed.
     }
-    // TODO(christofferqa): For non-jar code we currently cannot guarantee that markForceInline()
-    // will succeed.
     return true;
   }
 
diff --git a/src/main/java/com/android/tools/r8/utils/ArchiveBuilder.java b/src/main/java/com/android/tools/r8/utils/ArchiveBuilder.java
index a656a58..4111bed 100644
--- a/src/main/java/com/android/tools/r8/utils/ArchiveBuilder.java
+++ b/src/main/java/com/android/tools/r8/utils/ArchiveBuilder.java
@@ -90,6 +90,7 @@
       name += DataResource.SEPARATOR;
     }
     ZipEntry entry = new ZipEntry(name);
+    entry.setTime(0);
     ZipOutputStream zip = getStream(handler);
     synchronized (this) {
       try {
diff --git a/src/main/java/com/android/tools/r8/utils/ZipUtils.java b/src/main/java/com/android/tools/r8/utils/ZipUtils.java
index 740bf9c..97f3699 100644
--- a/src/main/java/com/android/tools/r8/utils/ZipUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ZipUtils.java
@@ -74,22 +74,12 @@
   public static void writeToZipStream(
       ZipOutputStream stream, String entry, byte[] content, int compressionMethod)
       throws IOException {
-    writeToZipStream(stream, entry, ByteDataView.of(content), compressionMethod, false);
+    writeToZipStream(stream, entry, ByteDataView.of(content), compressionMethod);
   }
 
   public static void writeToZipStream(
       ZipOutputStream stream, String entry, ByteDataView content, int compressionMethod)
       throws IOException {
-    writeToZipStream(stream, entry, content, compressionMethod, false);
-  }
-
-  public static void writeToZipStream(
-      ZipOutputStream stream,
-      String entry,
-      ByteDataView content,
-      int compressionMethod,
-      boolean setZeroTime)
-      throws IOException {
     byte[] buffer = content.getBuffer();
     int offset = content.getOffset();
     int length = content.getLength();
@@ -99,9 +89,7 @@
     zipEntry.setMethod(compressionMethod);
     zipEntry.setSize(length);
     zipEntry.setCrc(crc.getValue());
-    if (setZeroTime) {
-      zipEntry.setTime(0);
-    }
+    zipEntry.setTime(0);
     stream.putNextEntry(zipEntry);
     stream.write(buffer, offset, length);
     stream.closeEntry();
diff --git a/src/test/java/com/android/tools/r8/classmerging/ClassMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/ClassMergingTest.java
index 2a56ef5..aad109a 100644
--- a/src/test/java/com/android/tools/r8/classmerging/ClassMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/ClassMergingTest.java
@@ -262,6 +262,31 @@
   }
 
   @Test
+  public void testMergeInterfaceWithoutInlining() throws Throwable {
+    String main = "classmerging.ConflictingInterfaceSignaturesTest";
+    Path[] programFiles =
+        new Path[] {
+          CF_DIR.resolve("ConflictingInterfaceSignaturesTest.class"),
+          CF_DIR.resolve("ConflictingInterfaceSignaturesTest$A.class"),
+          CF_DIR.resolve("ConflictingInterfaceSignaturesTest$B.class"),
+          CF_DIR.resolve("ConflictingInterfaceSignaturesTest$InterfaceImpl.class")
+        };
+    Set<String> preservedClassNames =
+        ImmutableSet.of(
+            "classmerging.ConflictingInterfaceSignaturesTest",
+            "classmerging.ConflictingInterfaceSignaturesTest$InterfaceImpl");
+    runTestOnInput(
+        main,
+        readProgramFiles(programFiles),
+        preservedClassNames::contains,
+        getProguardConfig(EXAMPLE_KEEP),
+        options -> {
+          this.configure(options);
+          options.enableInlining = false;
+        });
+  }
+
+  @Test
   public void testMethodCollision() throws Throwable {
     String main = "classmerging.MethodCollisionTest";
     Path[] programFiles =
diff --git a/src/test/java/com/android/tools/r8/shaking/ParameterTypeTest.java b/src/test/java/com/android/tools/r8/shaking/ParameterTypeTest.java
new file mode 100644
index 0000000..97042b8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/ParameterTypeTest.java
@@ -0,0 +1,291 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.not;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import com.android.tools.r8.DexIndexedConsumer;
+import com.android.tools.r8.OutputMode;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.VmTestRunner;
+import com.android.tools.r8.jasmin.JasminBuilder;
+import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
+import java.nio.file.Path;
+import java.util.List;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+interface B112452064SuperInterface1 {
+  void foo();
+}
+
+interface B112452064SuperInterface2 {
+  void bar();
+}
+
+interface B112452064SubInterface extends B112452064SuperInterface1, B112452064SuperInterface2 {
+}
+
+class B112452064TestMain {
+
+  static void bazSuper1(B112452064SuperInterface1 instance) {
+    instance.foo();
+  }
+
+  static void bazSub(B112452064SubInterface instance) {
+    instance.foo();
+  }
+
+  public static void main(String[] args) {
+    bazSuper1(new B112452064SuperInterface1() {
+      @Override
+      public void foo() {
+        System.out.println("Anonymous1::foo");
+      }
+    });
+    bazSub(new B112452064SubInterface() {
+      @Override
+      public void foo() {
+        System.out.println("Anonymous2::foo");
+      }
+      @Override
+      public void bar() {
+        System.out.println("Anonymous2::bar");
+      }
+    });
+  }
+}
+
+@RunWith(VmTestRunner.class)
+public class ParameterTypeTest extends TestBase {
+
+  @Test
+  public void test_fromJavac() throws Exception {
+    String mainName = B112452064TestMain.class.getCanonicalName();
+    ProcessResult javaResult = ToolHelper.runJava(ToolHelper.getClassPathForTests(), mainName);
+    assertEquals(0, javaResult.exitCode);
+    assertThat(javaResult.stdout, containsString("Anonymous"));
+    assertThat(javaResult.stdout, containsString("::foo"));
+    assertEquals(-1, javaResult.stderr.indexOf("ClassNotFoundException"));
+
+    List<String> config = ImmutableList.of(
+        "-printmapping",
+        "-keep class " + mainName + " {",
+        "  public static void main(...);",
+        "}"
+    );
+    R8Command.Builder builder = R8Command.builder();
+    builder.addProgramFiles(ToolHelper.getClassFilesForTestDirectory(
+        ToolHelper.getPackageDirectoryForTestPackage(B112452064TestMain.class.getPackage()),
+        path -> path.getFileName().toString().startsWith("B112452064")));
+    builder.setProgramConsumer(DexIndexedConsumer.emptyConsumer());
+    builder.setMinApiLevel(ToolHelper.getMinApiLevelForDexVm().getLevel());
+    builder.addProguardConfiguration(config, Origin.unknown());
+    AndroidApp processedApp = ToolHelper.runR8(builder.build(), options -> {
+      options.enableInlining = false;
+    });
+
+    Path outDex = temp.getRoot().toPath().resolve("dex.zip");
+    processedApp.writeToZip(outDex, OutputMode.DexIndexed);
+    ProcessResult artResult = ToolHelper.runArtNoVerificationErrorsRaw(outDex.toString(), mainName);
+    assertEquals(0, artResult.exitCode);
+    assertEquals(javaResult.stdout, artResult.stdout);
+    assertEquals(-1, artResult.stderr.indexOf("ClassNotFoundException"));
+
+    CodeInspector inspector = new CodeInspector(processedApp);
+    ClassSubject superInterface1 = inspector.clazz(B112452064SuperInterface1.class);
+    assertThat(superInterface1, isRenamed());
+    MethodSubject foo = superInterface1.method("void", "foo", ImmutableList.of());
+    assertThat(foo, isRenamed());
+    ClassSubject superInterface2 = inspector.clazz(B112452064SuperInterface2.class);
+    assertThat(superInterface2, isRenamed());
+    MethodSubject bar = superInterface1.method("void", "bar", ImmutableList.of());
+    assertThat(bar, not(isPresent()));
+    ClassSubject subInterface = inspector.clazz(B112452064SubInterface.class);
+    assertThat(subInterface, isRenamed());
+  }
+
+  @Ignore("b/112452064")
+  @Test
+  public void test_brokenTypeHierarchy_singleInterface() throws Exception {
+    JasminBuilder jasminBuilder = new JasminBuilder();
+    // interface SuperInterface {
+    //   void foo();
+    // }
+    ClassBuilder sup = jasminBuilder.addInterface("SuperInterface");
+    MethodSignature foo = sup.addAbstractMethod("foo", ImmutableList.of(), "V");
+    // interface SubInterface extends SuperInterface
+    ClassBuilder sub = jasminBuilder.addInterface("SubInterface", sup.name);
+
+    // class Foo implements SuperInterface /* supposed to implement SubInterface */
+    ClassBuilder impl = jasminBuilder.addClass("Foo", "java/lang/Object", sup.name);
+    impl.addDefaultConstructor();
+    impl.addVirtualMethod(foo.name, ImmutableList.of(), "V",
+        ".limit locals 2",
+        ".limit stack 2",
+        "getstatic java/lang/System/out Ljava/io/PrintStream;",
+        "ldc \"" + foo.name + "\"",
+        "invokevirtual java/io/PrintStream/print(Ljava/lang/String;)V",
+        "return");
+
+    // class TestMain {
+    //   static bar(SubInterface instance) {
+    //     // instance.foo();
+    //   }
+    //   public static void main(String[] args) {
+    //     // ART verifies the argument (Foo) is an instance of the parameter type (SubInterface).
+    //     bar(new Foo());
+    //   }
+    // }
+    ClassBuilder mainClass = jasminBuilder.addClass("Main");
+    MethodSignature bar =
+        mainClass.addStaticMethod("bar", ImmutableList.of(sub.getDescriptor()), "V",
+            ".limit locals 2",
+            ".limit stack 2",
+            "getstatic java/lang/System/out Ljava/io/PrintStream;",
+            "ldc \"" + "bar" + "\"",
+            "invokevirtual java/io/PrintStream/print(Ljava/lang/String;)V",
+            "return");
+    mainClass.addMainMethod(
+        ".limit locals 1",
+        ".limit stack 2",
+        "new " + impl.name,
+        "dup",
+        "invokespecial " + impl.name + "/<init>()V",
+        "invokestatic " + mainClass.name + "/bar(" + sub.getDescriptor() + ")V",
+        "return");
+
+
+    final String mainClassName = mainClass.name;
+    String proguardConfig = keepMainProguardConfiguration(mainClassName, false, false);
+
+    // Run input program on java.
+    Path outputDirectory = temp.newFolder().toPath();
+    jasminBuilder.writeClassFiles(outputDirectory);
+    ProcessResult javaResult = ToolHelper.runJava(outputDirectory, mainClassName);
+    assertEquals(0, javaResult.exitCode);
+    assertThat(javaResult.stdout, containsString(bar.name));
+    assertEquals(-1, javaResult.stderr.indexOf("ClassNotFoundException"));
+
+    AndroidApp processedApp = compileWithR8(jasminBuilder.build(), proguardConfig,
+        // Disable inlining to avoid the (short) tested method from being inlined and then removed.
+        internalOptions -> internalOptions.enableInlining = false);
+
+    // Run processed (output) program on ART
+    ProcessResult artResult = runOnArtRaw(processedApp, mainClassName);
+    assertEquals(0, artResult.exitCode);
+    assertThat(artResult.stdout, containsString(bar.name));
+    assertEquals(-1, artResult.stderr.indexOf("ClassNotFoundException"));
+
+    CodeInspector inspector = new CodeInspector(processedApp);
+    ClassSubject subSubject = inspector.clazz(sub.name);
+    assertThat(subSubject, isPresent());
+  }
+
+  @Ignore("b/112452064")
+  @Test
+  public void test_brokenTypeHierarchy_doubleInterfaces() throws Exception {
+    JasminBuilder jasminBuilder = new JasminBuilder();
+    // interface SuperInterface1 {
+    //   void foo();
+    // }
+    ClassBuilder sup1 = jasminBuilder.addInterface("SuperInterface1");
+    MethodSignature foo = sup1.addAbstractMethod("foo", ImmutableList.of(), "V");
+    // interface SuperInterface2 {
+    //   void bar();
+    // }
+    ClassBuilder sup2 = jasminBuilder.addInterface("SuperInterface2");
+    MethodSignature bar = sup1.addAbstractMethod("bar", ImmutableList.of(), "V");
+    // interface SubInterface extends SuperInterface1, SuperInterface2
+    ClassBuilder sub = jasminBuilder.addInterface("SubInterface", sup1.name, sup2.name);
+
+    // class Foo implements SuperInterface1, SuperInterface2
+    //   /* supposed to implement SubInterface */
+    ClassBuilder impl = jasminBuilder.addClass("Foo", "java/lang/Object", sup1.name, sup2.name);
+    impl.addDefaultConstructor();
+    impl.addVirtualMethod(foo.name, ImmutableList.of(), "V",
+        ".limit locals 2",
+        ".limit stack 2",
+        "getstatic java/lang/System/out Ljava/io/PrintStream;",
+        "ldc \"" + foo.name + "\"",
+        "invokevirtual java/io/PrintStream/print(Ljava/lang/String;)V",
+        "return");
+    impl.addVirtualMethod(bar.name, ImmutableList.of(), "V",
+        ".limit locals 2",
+        ".limit stack 2",
+        "getstatic java/lang/System/out Ljava/io/PrintStream;",
+        "ldc \"" + bar.name + "\"",
+        "invokevirtual java/io/PrintStream/print(Ljava/lang/String;)V",
+        "return");
+
+    // class TestMain {
+    //   static bar(SubInterface instance) {
+    //     // instance.foo();
+    //   }
+    //   public static void main(String[] args) {
+    //     // ART verifies the argument (Foo) is an instance of the parameter type (SubInterface).
+    //     bar(new Foo());
+    //   }
+    // }
+    ClassBuilder mainClass = jasminBuilder.addClass("Main");
+    MethodSignature baz =
+        mainClass.addStaticMethod("baz", ImmutableList.of(sub.getDescriptor()), "V",
+            ".limit locals 2",
+            ".limit stack 2",
+            "getstatic java/lang/System/out Ljava/io/PrintStream;",
+            "ldc \"" + "baz" + "\"",
+            "invokevirtual java/io/PrintStream/print(Ljava/lang/String;)V",
+            "return");
+    mainClass.addMainMethod(
+        ".limit locals 1",
+        ".limit stack 2",
+        "new " + impl.name,
+        "dup",
+        "invokespecial " + impl.name + "/<init>()V",
+        "invokestatic " + mainClass.name + "/baz(" + sub.getDescriptor() + ")V",
+        "return");
+
+    final String mainClassName = mainClass.name;
+    String proguardConfig = keepMainProguardConfiguration(mainClassName, false, false);
+
+    // Run input program on java.
+    Path outputDirectory = temp.newFolder().toPath();
+    jasminBuilder.writeClassFiles(outputDirectory);
+    ProcessResult javaResult = ToolHelper.runJava(outputDirectory, mainClassName);
+    assertEquals(0, javaResult.exitCode);
+    assertThat(javaResult.stdout, containsString(baz.name));
+    assertEquals(-1, javaResult.stderr.indexOf("ClassNotFoundException"));
+
+    AndroidApp processedApp = compileWithR8(jasminBuilder.build(), proguardConfig,
+        // Disable inlining to avoid the (short) tested method from being inlined and then removed.
+        internalOptions -> internalOptions.enableInlining = false);
+
+    // Run processed (output) program on ART
+    ProcessResult artResult = runOnArtRaw(processedApp, mainClassName);
+    assertEquals(0, artResult.exitCode);
+    assertThat(artResult.stdout, containsString(baz.name));
+    assertEquals(javaResult.stdout, artResult.stdout);
+    assertEquals(-1, artResult.stderr.indexOf("ClassNotFoundException"));
+
+    CodeInspector inspector = new CodeInspector(processedApp);
+    ClassSubject subSubject = inspector.clazz(sub.name);
+    assertThat(subSubject, isPresent());
+  }
+}