Version 1.0.9

Merge: c25649a8b187dbcabfa8973be5f96fdc01008c1c
CL: Remove warnings from main dex calculation

Merge: 5429584751948942d30dd352388e387370df439e
CL: Mark test as requiring Art VM

Bug: 72312389

Change-Id: I76f1f63830fc0247bd8d684e22a249e2f6a81bd7
diff --git a/src/main/java/com/android/tools/r8/CompatProguardCommandBuilder.java b/src/main/java/com/android/tools/r8/CompatProguardCommandBuilder.java
index 5755c04..9fbca07 100644
--- a/src/main/java/com/android/tools/r8/CompatProguardCommandBuilder.java
+++ b/src/main/java/com/android/tools/r8/CompatProguardCommandBuilder.java
@@ -37,6 +37,13 @@
     this(true);
   }
 
+  public CompatProguardCommandBuilder(
+      boolean forceProguardCompatibility, DiagnosticsHandler diagnosticsHandler) {
+    super(forceProguardCompatibility, diagnosticsHandler);
+    setIgnoreDexInArchive(true);
+    addProguardConfiguration(REFLECTIONS, EmbeddedOrigin.INSTANCE);
+  }
+
   public CompatProguardCommandBuilder(boolean forceProguardCompatibility) {
     super(forceProguardCompatibility);
     setIgnoreDexInArchive(true);
diff --git a/src/main/java/com/android/tools/r8/GenerateMainDexListCommand.java b/src/main/java/com/android/tools/r8/GenerateMainDexListCommand.java
index b5e1a49..5e41085 100644
--- a/src/main/java/com/android/tools/r8/GenerateMainDexListCommand.java
+++ b/src/main/java/com/android/tools/r8/GenerateMainDexListCommand.java
@@ -27,6 +27,7 @@
   private final ImmutableList<ProguardConfigurationRule> mainDexKeepRules;
   private final StringConsumer mainDexListConsumer;
   private final DexItemFactory factory;
+  private final Reporter reporter;
 
   /** Get the output path for the main-dex list. Null if not set. */
   @Deprecated
@@ -42,6 +43,14 @@
     private final List<ProguardConfigurationSource> mainDexRules = new ArrayList<>();
     private Path mainDexListOutput = null;
 
+    private Builder() {
+    }
+
+    private Builder(DiagnosticsHandler diagnosticsHandler) {
+      super(diagnosticsHandler);
+    }
+
+
     @Override
     GenerateMainDexListCommand.Builder self() {
       return this;
@@ -119,7 +128,7 @@
           mainDexListOutput != null ? new StringConsumer.FileConsumer(mainDexListOutput) : null;
 
       return new GenerateMainDexListCommand(
-          factory, getAppBuilder().build(), mainDexKeepRules, mainDexListConsumer);
+          factory, getAppBuilder().build(), mainDexKeepRules, mainDexListConsumer, getReporter());
     }
   }
 
@@ -139,6 +148,10 @@
     return new GenerateMainDexListCommand.Builder();
   }
 
+  public static GenerateMainDexListCommand.Builder builder(DiagnosticsHandler diagnosticsHandler) {
+    return new GenerateMainDexListCommand.Builder(diagnosticsHandler);
+  }
+
   public static GenerateMainDexListCommand.Builder parse(String[] args) {
     GenerateMainDexListCommand.Builder builder = builder();
     parse(args, builder);
@@ -174,11 +187,13 @@
       DexItemFactory factory,
       AndroidApp inputApp,
       ImmutableList<ProguardConfigurationRule> mainDexKeepRules,
-      StringConsumer mainDexListConsumer) {
+      StringConsumer mainDexListConsumer,
+      Reporter reporter) {
     super(inputApp);
     this.factory = factory;
     this.mainDexKeepRules = mainDexKeepRules;
     this.mainDexListConsumer = mainDexListConsumer;
+    this.reporter = reporter;
   }
 
   private GenerateMainDexListCommand(boolean printHelp, boolean printVersion) {
@@ -186,18 +201,17 @@
     this.factory = new DexItemFactory();
     this.mainDexKeepRules = ImmutableList.of();
     this.mainDexListConsumer = null;
+    this.reporter = new Reporter(new DefaultDiagnosticsHandler());
   }
 
   @Override
   InternalOptions getInternalOptions() {
-    InternalOptions internal =
-        new InternalOptions(factory, new Reporter(new DefaultDiagnosticsHandler()));
+    InternalOptions internal = new InternalOptions(factory, reporter);
     internal.mainDexKeepRules = mainDexKeepRules;
     internal.mainDexListConsumer = mainDexListConsumer;
     internal.minimalMainDex = internal.debug;
     internal.removeSwitchMaps = false;
     internal.inlineAccessors = false;
-    internal.allowLibraryClassesToExtendProgramClasses = true;
     return internal;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 71c82a8..e6e8019 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -75,6 +75,12 @@
       this.forceProguardCompatibility = forceProguardCompatibility;
     }
 
+    Builder(boolean forceProguardCompatibility, DiagnosticsHandler diagnosticsHandler) {
+      super(diagnosticsHandler);
+      setMode(CompilationMode.RELEASE);
+      this.forceProguardCompatibility = forceProguardCompatibility;
+    }
+
     private Builder(DiagnosticsHandler diagnosticsHandler) {
       super(diagnosticsHandler);
       setMode(CompilationMode.DEBUG);
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index eba3f9e..94585ce 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
 
   // This field is accessed from release scripts using simple pattern matching.
   // Therefore, changing this field could break our release scripts.
-  public static final String LABEL = "v1.0.8";
+  public static final String LABEL = "v1.0.9";
 
   private Version() {
   }
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index fe2f063..0bd3068 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -78,6 +78,7 @@
  * field descriptions for details.
  */
 public class Enqueuer {
+  private boolean tracingMainDex = false;
 
   private final AppInfoWithSubtyping appInfo;
   private final InternalOptions options;
@@ -499,13 +500,19 @@
   }
 
   private void ensureFromLibraryOrThrow(DexType type, DexType context) {
+    if (tracingMainDex) {
+      // b/72312389: android.jar contains parts of JUnit and most developers include JUnit in
+      // their programs. This leads to library classes extending program classes. When tracing
+      // main dex lists we allow this.
+      return;
+    }
+
     DexClass holder = appInfo.definitionFor(type);
     if (holder != null && !holder.isLibraryClass()) {
       Diagnostic message = new StringDiagnostic("Library class " + context.toSourceString()
           + (holder.isInterface() ? " implements " : " extends ")
           + "program class " + type.toSourceString());
-      if (options.forceProguardCompatibility
-          || options.allowLibraryClassesToExtendProgramClasses) {
+      if (options.forceProguardCompatibility) {
         options.reporter.warning(message);
       } else {
         options.reporter.error(message);
@@ -944,6 +951,7 @@
   }
 
   public Set<DexType> traceMainDex(RootSet rootSet, Timing timing) {
+    this.tracingMainDex = true;
     this.rootSet = rootSet;
     // Translate the result of root-set computation into enqueuer actions.
     enqueueRootItems(rootSet.noShrinking);
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index fa32b89..7bb23d4 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -169,10 +169,6 @@
   public boolean disableAssertions = true;
   public boolean debugKeepRules = false;
 
-  // TODO(72312389): android.jar contains parts of JUnit and most developers include JUnit in
-  // their programs, which can lead to library classes extending program classes.
-  public boolean allowLibraryClassesToExtendProgramClasses = false;
-
   public boolean debug = false;
   public final TestingOptions testing = new TestingOptions();
 
diff --git a/src/test/examplesAndroidApi/instrumentationtest/InstrumentationTest.java b/src/test/examplesAndroidApi/instrumentationtest/InstrumentationTest.java
new file mode 100644
index 0000000..5541ccd
--- /dev/null
+++ b/src/test/examplesAndroidApi/instrumentationtest/InstrumentationTest.java
@@ -0,0 +1,21 @@
+// 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 instrumentationtest;
+
+import android.content.Context;
+import android.test.InstrumentationTestCase;
+
+public class InstrumentationTest extends InstrumentationTestCase {
+  Context context;
+
+  public void setUp() throws Exception {
+    super.setUp();
+    context = getInstrumentation().getContext();
+    assertNotNull(context);
+  }
+
+  public void testSomething() {
+    assertEquals(false, true);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index fffdc48..6cc9bb9 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -513,6 +513,13 @@
     }
   }
 
+  /**
+   * Get the junit jar bundled with the framework.
+   */
+  public static Path getFrameworkJunitJarPath(DexVm version) {
+    return Paths.get(getArtDir(version), "framework", "junit.jar");
+  }
+
   static class RetainedTemporaryFolder extends TemporaryFolder {
 
     RetainedTemporaryFolder(java.io.File parentFolder) {
diff --git a/src/test/java/com/android/tools/r8/maindexlist/b72312389/B72312389.java b/src/test/java/com/android/tools/r8/maindexlist/b72312389/B72312389.java
new file mode 100644
index 0000000..1d2a942
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/maindexlist/b72312389/B72312389.java
@@ -0,0 +1,153 @@
+// 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.maindexlist.b72312389;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.android.tools.r8.BaseCommand;
+import com.android.tools.r8.CompatProguardCommandBuilder;
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.DexIndexedConsumer;
+import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.GenerateMainDexList;
+import com.android.tools.r8.GenerateMainDexListCommand;
+import com.android.tools.r8.R8;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.VmTestRunner;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.DexInspector;
+import com.google.common.collect.ImmutableList;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(VmTestRunner.class)
+public class B72312389 extends TestBase {
+  // Build a app with a class extending InstrumentationTestCase and including both the junit
+  // and the Android library.
+  private void buildInstrumentationTestCaseApplication(BaseCommand.Builder builder) {
+    builder
+        .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.O.getLevel()))
+        .addProgramFiles(
+            Paths.get("build", "test", "examplesAndroidApi",
+                "classes", "instrumentationtest", "InstrumentationTest.class"))
+        .addProgramFiles(ToolHelper.getFrameworkJunitJarPath(DexVm.ART_7_0_0_HOST));
+  }
+
+  private List<String> keepInstrumentationTestCaseRules = ImmutableList.of(
+      "-keep class instrumentationtest.InstrumentationTest {",
+      "  *;",
+      "}");
+
+  @Test
+  public void testGenerateMainDexList() throws Exception {
+    CollectingDiagnosticHandler diagnostics = new CollectingDiagnosticHandler();
+    GenerateMainDexListCommand.Builder builder = GenerateMainDexListCommand.builder(diagnostics);
+    buildInstrumentationTestCaseApplication(builder);
+    GenerateMainDexListCommand command = builder
+        .addMainDexRules(keepInstrumentationTestCaseRules, Origin.unknown())
+        .build();
+    List<String> mainDexList = GenerateMainDexList.run(command);
+    assertTrue(mainDexList.contains("junit/framework/TestCase.class"));
+    diagnostics.assertEmpty();
+  }
+
+  private static class StringBox {
+    String content;
+  }
+
+  @Test
+  public void testR8ForceProguardCompatibility() throws Exception {
+    StringBox mainDexList = new StringBox();
+    // Build a app with a class extending InstrumentationTestCase and including both the junit
+    // and the Android library.
+    CollectingDiagnosticHandler diagnostics = new CollectingDiagnosticHandler();
+    R8Command.Builder builder = new CompatProguardCommandBuilder(true, diagnostics);
+    buildInstrumentationTestCaseApplication(builder);
+    R8Command command = builder
+        .setMinApiLevel(AndroidApiLevel.K.getLevel())
+        // TODO(72793900): This should not be required.
+        .addProguardConfiguration(ImmutableList.of("-keep class ** { *; }"), Origin.unknown())
+        .addProguardConfiguration(ImmutableList.of("-dontobfuscate"), Origin.unknown())
+        .addMainDexRules(keepInstrumentationTestCaseRules, Origin.unknown())
+        .setProgramConsumer(DexIndexedConsumer.emptyConsumer())
+        .setMainDexListConsumer(
+            (string, handler) -> mainDexList.content = string)
+        .build();
+    DexInspector inspector = new DexInspector(ToolHelper.runR8(command));
+    assertTrue(inspector.clazz("instrumentationtest.InstrumentationTest").isPresent());
+    assertTrue(mainDexList.content.contains("junit/framework/TestCase.class"));
+    // TODO(72794301): Two copies of this message is a bit over the top.
+    assertEquals(2,
+        diagnostics.countLibraryClassExtensdProgramClassWarnings(
+            "android.test.InstrumentationTestCase", "junit.framework.TestCase"));
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    CollectingDiagnosticHandler diagnostics = new CollectingDiagnosticHandler();
+    R8Command.Builder builder = R8Command.builder(diagnostics);
+    buildInstrumentationTestCaseApplication(builder);
+    R8Command command = builder
+        .addMainDexRules(keepInstrumentationTestCaseRules, Origin.unknown())
+        .setProgramConsumer(DexIndexedConsumer.emptyConsumer())
+        .build();
+    try {
+      R8.run(command);
+      fail();
+    } catch (CompilationFailedException e) {
+      // Expected, as library class extending program class is an error for R8.
+    }
+  }
+
+  private static class CollectingDiagnosticHandler implements DiagnosticsHandler {
+    private final List<Diagnostic> infos = new ArrayList<>();
+    private final List<Diagnostic> warnings = new ArrayList<>();
+    private final List<Diagnostic> errors = new ArrayList<>();
+
+    @Override
+    public void info(Diagnostic info) {
+      infos.add(info);
+    }
+
+    @Override
+    public void warning(Diagnostic warning) {
+      warnings.add(warning);
+    }
+
+    @Override
+    public void error(Diagnostic error) {
+      errors.add(error);
+    }
+
+    public void assertEmpty() {
+      assertEquals(0, errors.size());
+      assertEquals(0, warnings.size());
+      assertEquals(0, infos.size());
+    }
+
+    private boolean isLibraryClassExtensdProgramClassWarnings(
+        String libraryClass, String programClass, Diagnostic diagnostic) {
+      return diagnostic.getDiagnosticMessage().equals(
+          "Library class "+ libraryClass + " extends program class " + programClass);
+    }
+
+    public long countLibraryClassExtensdProgramClassWarnings(
+        String libraryClass, String programClass) {
+      return warnings.stream()
+          .filter(diagnostics ->
+              isLibraryClassExtensdProgramClassWarnings(libraryClass, programClass, diagnostics))
+          .count();
+    }
+  }
+}