Merge "Always keep default constructor in force proguard compatibility mode"
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 39aadcb..67add9c 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -399,6 +399,14 @@
       if (annotations != null) {
         annotations.forEach(this::handleAnnotationOfLiveType);
       }
+
+      // For Proguard compatibility mark default initializer for live type as live.
+      if (options.forceProguardCompatibility) {
+        if (holder.hasDefaultInitializer()) {
+          DexEncodedMethod init = holder.getDefaultInitializer();
+          markDirectStaticOrConstructorMethodAsLive(init, KeepReason.reachableFromLiveType(type));
+        }
+      }
     }
   }
 
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index 384c1ff..ea51c65 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -76,7 +76,7 @@
       for (Class clazz : classes) {
         try (FileInputStream in =
             new FileInputStream(ToolHelper.getClassFileForTestClass(clazz).toFile())) {
-          out.putNextEntry(new ZipEntry(clazz.getCanonicalName().replace('.', '/') + ".class"));
+          out.putNextEntry(new ZipEntry(ToolHelper.getJarEntryForTestClass(clazz)));
           ByteStreams.copy(in, out);
           out.closeEntry();
         }
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 92d12c1..5753d84 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -75,6 +75,8 @@
   private static final String ANDROID_JAR_PATTERN = "third_party/android_jar/lib-v%d/android.jar";
   private static final int DEFAULT_MIN_SDK = AndroidApiLevel.I.getLevel();
 
+  private static final String PROGUARD = "third_party/proguard/proguard5.2.1/bin/proguard.sh";
+
   public enum DexVm {
     ART_4_4_4_TARGET(Version.V4_4_4, Kind.TARGET),
     ART_4_4_4_HOST(Version.V4_4_4, Kind.HOST),
@@ -567,7 +569,7 @@
     return Paths.get(BUILD_DIR, "classes", "test");
   }
 
-  public static Path getClassFileForTestClass(Class clazz) {
+  private static List<String> getNamePartsForTestClass(Class clazz) {
     List<String> parts = Lists.newArrayList(clazz.getCanonicalName().split("\\."));
     Class enclosing = clazz;
     while (enclosing.getEnclosingClass() != null) {
@@ -576,10 +578,20 @@
       enclosing = clazz.getEnclosingClass();
     }
     parts.set(parts.size() - 1, parts.get(parts.size() - 1) + ".class");
+    return parts;
+  }
+
+  public static Path getClassFileForTestClass(Class clazz) {
+    List<String> parts = getNamePartsForTestClass(clazz);
     return getClassPathForTests().resolve(
         Paths.get("", parts.toArray(new String[parts.size() - 1])));
   }
 
+  public static String getJarEntryForTestClass(Class clazz) {
+    List<String> parts = getNamePartsForTestClass(clazz);
+    return String.join("/", parts);
+  }
+
   public static DexApplication buildApplication(List<String> fileNames)
       throws IOException, ExecutionException {
     return buildApplicationWithAndroidJar(fileNames, getDefaultAndroidJar());
@@ -958,6 +970,26 @@
     }
   }
 
+  public static void runProguard(Path inJar, Path outJar, Path config) throws IOException {
+    List<String> command = new ArrayList<>();
+    command.add(PROGUARD);
+    command.add("-forceprocessing");  // Proguard just checks the creation time on the in/out jars.
+    command.add("-injars");
+    command.add(inJar.toString());
+    command.add("-libraryjars");
+    command.add(ToolHelper.getDefaultAndroidJar());
+    command.add("@" + config);
+    command.add("-outjar");
+    command.add(outJar.toString());
+    command.add("-printmapping");
+    ProcessBuilder builder = new ProcessBuilder(command);
+    ToolHelper.ProcessResult result = ToolHelper.runProcess(builder);
+    if (result.exitCode != 0) {
+      fail("Proguard failed, exit code " + result.exitCode + ", stderr:\n" + result.stderr);
+    }
+  }
+
+
   public static class ProcessResult {
 
     public final int exitCode;
diff --git a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
index 298876f..dae1163 100644
--- a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
@@ -5,7 +5,6 @@
 package com.android.tools.r8.shaking.forceproguardcompatibility;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.R8Command;
@@ -16,12 +15,18 @@
 import com.android.tools.r8.utils.DexInspector;
 import com.android.tools.r8.utils.DexInspector.ClassSubject;
 import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import com.android.tools.r8.utils.FileUtils;
 import com.google.common.collect.ImmutableList;
+import java.io.File;
+import java.nio.file.Path;
+import java.util.List;
 import org.junit.Test;
 
 public class ForceProguardCompatibilityTest extends TestBase {
-  private void test(Class mainClass, Class mentionedClass, boolean arrayClass,
-      boolean forceProguardCompatibility)
+  // Actually running Proguard should only be during development.
+  private final boolean RUN_PROGUARD = false;
+
+  private void test(Class mainClass, Class mentionedClass, boolean forceProguardCompatibility)
       throws Exception {
     String proguardConfig = keepMainProguardConfiguration(mainClass, true, false);
     DexInspector inspector = new DexInspector(
@@ -32,28 +37,24 @@
     assertTrue(inspector.clazz(mainClass.getCanonicalName()).isPresent());
     ClassSubject clazz = inspector.clazz(getJavacGeneratedClassName(mentionedClass));
     assertTrue(clazz.isPresent());
-    if (arrayClass) {
-      MethodSubject defaultInitializer = clazz.method(MethodSignature.initializer(new String[]{}));
-      assertFalse(defaultInitializer.isPresent());
-    } else {
-      MethodSubject defaultInitializer = clazz.method(MethodSignature.initializer(new String[]{}));
-      assertEquals(forceProguardCompatibility, defaultInitializer.isPresent());
-    }
+    MethodSubject defaultInitializer = clazz.method(MethodSignature.initializer(new String[]{}));
+    assertEquals(forceProguardCompatibility, defaultInitializer.isPresent());
   }
 
   @Test
   public void testKeepDefaultInitializer() throws Exception {
-    test(TestMain.class, TestMain.MentionedClass.class, false, true);
-    test(TestMain.class, TestMain.MentionedClass.class, false, false);
+    test(TestMain.class, TestMain.MentionedClass.class, true);
+    test(TestMain.class, TestMain.MentionedClass.class, false);
   }
 
   @Test
   public void testKeepDefaultInitializerArrayType() throws Exception {
-    test(TestMainArrayType.class, TestMainArrayType.MentionedClass.class, true, true);
-    test(TestMainArrayType.class, TestMainArrayType.MentionedClass.class, true, false);
+    test(TestMainArrayType.class, TestMainArrayType.MentionedClass.class, true);
+    test(TestMainArrayType.class, TestMainArrayType.MentionedClass.class, false);
   }
 
-  private void runAnnotationsTest(boolean forceProguardCompatibility, boolean keepAnnotations) throws Exception {
+  private void runAnnotationsTest(boolean forceProguardCompatibility, boolean keepAnnotations)
+      throws Exception {
     R8Command.Builder builder =
         new CompatProguardCommandBuilder(forceProguardCompatibility, false);
     // Add application classes including the annotation class.
@@ -93,4 +94,35 @@
     runAnnotationsTest(false, true);
     runAnnotationsTest(false, false);
   }
+
+  private void runDefaultConstructorTest(boolean forceProguardCompatibility,
+      Class<?> testClass, boolean hasDefaultConstructor) throws Exception {
+    R8Command.Builder builder = new CompatProguardCommandBuilder(forceProguardCompatibility, false);
+    builder.addProgramFiles(ToolHelper.getClassFileForTestClass(testClass));
+    List<String> proguardConfig = ImmutableList.of(
+        "-keep class " + testClass.getCanonicalName() + " {",
+        "  public void method();",
+        "}");
+    builder.addProguardConfiguration(proguardConfig);
+    DexInspector inspector = new DexInspector(ToolHelper.runR8(builder.build()));
+    ClassSubject clazz = inspector.clazz(getJavacGeneratedClassName(testClass));
+    assertTrue(clazz.isPresent());
+    assertEquals(forceProguardCompatibility && hasDefaultConstructor,
+        clazz.init(ImmutableList.of()).isPresent());
+
+    if (RUN_PROGUARD) {
+      Path proguardedJar = File.createTempFile("proguarded", ".jar", temp.getRoot()).toPath();
+      Path proguardConfigFile = File.createTempFile("proguard", ".config", temp.getRoot()).toPath();
+      FileUtils.writeTextFile(proguardConfigFile, proguardConfig);
+      ToolHelper.runProguard(jarTestClasses(testClass), proguardedJar, proguardConfigFile);
+    }
+  }
+
+  @Test
+  public void testDefaultConstructor() throws Exception {
+    runDefaultConstructorTest(true, TestClassWithDefaultConstructor.class, true);
+    runDefaultConstructorTest(true, TestClassWithoutDefaultConstructor.class, false);
+    runDefaultConstructorTest(false, TestClassWithDefaultConstructor.class, true);
+    runDefaultConstructorTest(false, TestClassWithoutDefaultConstructor.class, false);
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/TestClassWithDefaultConstructor.java b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/TestClassWithDefaultConstructor.java
new file mode 100644
index 0000000..fd7909d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/TestClassWithDefaultConstructor.java
@@ -0,0 +1,16 @@
+// Copyright (c) 2017, 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.forceproguardcompatibility;
+
+public class TestClassWithDefaultConstructor {
+  public TestClassWithDefaultConstructor() {
+  }
+
+  public TestClassWithDefaultConstructor(int i) {
+  }
+
+  public void method() {
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/TestClassWithoutDefaultConstructor.java b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/TestClassWithoutDefaultConstructor.java
new file mode 100644
index 0000000..9a5679d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/TestClassWithoutDefaultConstructor.java
@@ -0,0 +1,13 @@
+// Copyright (c) 2017, 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.forceproguardcompatibility;
+
+public class TestClassWithoutDefaultConstructor {
+  public TestClassWithoutDefaultConstructor(int i) {
+  }
+
+  public void method() {
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/IncludeDescriptorClassesTest.java b/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/IncludeDescriptorClassesTest.java
index b09ef9b..8bff0b3 100644
--- a/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/IncludeDescriptorClassesTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/IncludeDescriptorClassesTest.java
@@ -6,13 +6,11 @@
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
 
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.utils.DexInspector;
 import com.google.common.collect.ImmutableList;
-import java.io.File;
 import java.io.IOException;
 import java.nio.file.Path;
 import java.util.ArrayList;
@@ -26,29 +24,6 @@
 
 public class IncludeDescriptorClassesTest extends TestBase {
 
-  private static String PROGUARD = "third_party/proguard/proguard5.2.1/bin/proguard.sh";
-
-  private Path runProguard(Path inJar, Path config) throws IOException {
-    Path outJar = File.createTempFile("junit", ".jar", temp.getRoot()).toPath();
-    List<String> command = new ArrayList<>();
-    command.add(PROGUARD);
-    command.add("-forceprocessing");  // Proguard just checks the creation time on the in/out jars.
-    command.add("-injars");
-    command.add(inJar.toString());
-    command.add("-libraryjars");
-    command.add(ToolHelper.getDefaultAndroidJar());
-    command.add("@" + config);
-    command.add("-outjar");
-    command.add(outJar.toString());
-    command.add("-printmapping");
-    ProcessBuilder builder = new ProcessBuilder(command);
-    ToolHelper.ProcessResult result = ToolHelper.runProcess(builder);
-    if (result.exitCode != 0) {
-      fail("Proguard failed, exit code " + result.exitCode + ", stderr:\n" + result.stderr);
-    }
-    return outJar;
-  }
-
   private Set<String> readJarClasses(Path jar) throws IOException {
     Set<String> result = new HashSet<>();
     try (ZipFile zipFile = new ZipFile(jar.toFile())) {
@@ -115,7 +90,8 @@
     Set<String> classesAfterProguard = null;
     // Actually running Proguard should only be during development.
     if (false) {
-      Path proguardedJar = runProguard(jarTestClasses(classes), proguardConfig);
+      Path proguardedJar = temp.newFile("proguarded.jar").toPath();
+      ToolHelper.runProguard(jarTestClasses(classes), proguardedJar, proguardConfig);
       classesAfterProguard = readJarClasses(proguardedJar);
     }
 
diff --git a/src/test/java/com/android/tools/r8/utils/DexInspector.java b/src/test/java/com/android/tools/r8/utils/DexInspector.java
index 086287c..4b2b6ae 100644
--- a/src/test/java/com/android/tools/r8/utils/DexInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/DexInspector.java
@@ -270,6 +270,10 @@
       return method("void", "<clinit>", ImmutableList.of());
     }
 
+    public MethodSubject init(List<String> parameters) {
+      return method("void", "<init>", parameters);
+    }
+
     public MethodSubject method(MethodSignature signature) {
       return method(signature.type, signature.name, ImmutableList.copyOf(signature.parameters));
     }