Merge "Split edges after transformations that might introduce new critical edges."
diff --git a/build.gradle b/build.gradle
index 6be6012..e9bfe8e 100644
--- a/build.gradle
+++ b/build.gradle
@@ -88,6 +88,12 @@
         }
         output.resourcesDir = 'build/classes/examplesAndroidO'
     }
+    examplesAndroidP {
+        java {
+            srcDirs = ['src/test/examplesAndroidP']
+        }
+        output.resourcesDir = 'build/classes/examplesAndroidP'
+    }
     jctfCommon {
         java {
             srcDirs = [
@@ -145,6 +151,7 @@
     jctfTestsCompile 'junit:junit:4.12'
     jctfTestsCompile sourceSets.jctfCommon.output
     examplesAndroidOCompile group: 'org.ow2.asm', name: 'asm', version: '6.0_BETA'
+    examplesAndroidPCompile group: 'org.ow2.asm', name: 'asm', version: '6.0_BETA'
     examplesCompile 'com.google.protobuf:protobuf-lite:3.0.0'
     examplesRuntime 'com.google.protobuf:protobuf-lite:3.0.0'
     supportLibs 'com.android.support:support-v4:25.4.0'
@@ -887,6 +894,51 @@
     }
 }
 
+task buildExampleAndroidPJars {
+    dependsOn downloadDeps
+    def examplesDir = file("src/test/examplesAndroidP")
+
+    task "compile_examplesAndroidP"(type: JavaCompile) {
+        source = fileTree(dir: examplesDir, include: '**/*.java')
+        destinationDir = file("build/test/examplesAndroidP/classes")
+        classpath = sourceSets.main.compileClasspath
+        sourceCompatibility = JavaVersion.VERSION_1_8
+        targetCompatibility = JavaVersion.VERSION_1_8
+        options.compilerArgs += ["-Xlint:-options"]
+    }
+    examplesDir.eachDir { dir ->
+        def name = dir.getName();
+        def destinationDir = file("build/test/examplesAndroidP/classes");
+        if (file("src/test/examplesAndroidP/" + name + "/TestGenerator.java").isFile()) {
+            task "generate_examplesAndroidP_${name}"(type: JavaExec,
+                    dependsOn: "compile_examplesAndroidP") {
+                main = name + ".TestGenerator"
+                classpath = files(destinationDir, sourceSets.main.compileClasspath)
+                args destinationDir
+            }
+        } else {
+            task "generate_examplesAndroidP_${name}" () {}
+        }
+    }
+    examplesDir.eachDir { dir ->
+        def name = dir.getName();
+        def exampleOutputDir = file("build/test/examplesAndroidP");
+        def jarName = "${name}.jar"
+        dependsOn "jar_examplesAndroidP_${name}"
+        task "jar_examplesAndroidP_${name}"(type: Jar,
+                dependsOn: ["compile_examplesAndroidP",
+                            "generate_examplesAndroidP_${name}"]) {
+            archiveName = jarName
+            destinationDir = exampleOutputDir
+            from "build/test/examplesAndroidP/classes"  // Java 1.8 classes
+            include "**/" + name + "/**/*.class"
+            // Do not include generator into the test runtime jar, it is not useful.
+            // Otherwise, shrinking will need ASM jars.
+            exclude "**/TestGenerator*"
+        }
+    }
+}
+
 task buildExamples {
     if (OperatingSystem.current().isMacOsX() || OperatingSystem.current().isWindows()) {
         logger.lifecycle("WARNING: Testing (including building examples) is only partially supported on your " +
@@ -900,6 +952,7 @@
     dependsOn buildExampleJars
     dependsOn buildExampleAndroidNJars
     dependsOn buildExampleAndroidOJars
+    dependsOn buildExampleAndroidPJars
     def examplesDir = file("src/test/examples")
     def noDexTests = [
         "multidex",
@@ -1462,8 +1515,10 @@
 task javadocD8(type: Javadoc) {
   classpath = sourceSets.main.compileClasspath
   source = sourceSets.main.allJava
+  include '**/com/android/tools/r8/ArchiveClassFileProvider.java'
   include '**/com/android/tools/r8/BaseCommand.java'
   include '**/com/android/tools/r8/BaseOutput.java'
+  include '**/com/android/tools/r8/ClassFileResourceProvider.java'
   include '**/com/android/tools/r8/CompilationException.java'
   include '**/com/android/tools/r8/CompilationMode.java'
   include '**/com/android/tools/r8/D8.java'
diff --git a/src/main/java/com/android/tools/r8/utils/ArchiveClassFileProvider.java b/src/main/java/com/android/tools/r8/ArchiveClassFileProvider.java
similarity index 71%
rename from src/main/java/com/android/tools/r8/utils/ArchiveClassFileProvider.java
rename to src/main/java/com/android/tools/r8/ArchiveClassFileProvider.java
index 2828610..0313cf8 100644
--- a/src/main/java/com/android/tools/r8/utils/ArchiveClassFileProvider.java
+++ b/src/main/java/com/android/tools/r8/ArchiveClassFileProvider.java
@@ -1,18 +1,17 @@
 // 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.utils;
+package com.android.tools.r8;
 
 import static com.android.tools.r8.utils.FileUtils.CLASS_EXTENSION;
 import static com.android.tools.r8.utils.FileUtils.isArchive;
 import static com.android.tools.r8.utils.FileUtils.isClassFile;
 
-import com.android.tools.r8.ClassFileResourceProvider;
-import com.android.tools.r8.Resource;
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.shaking.FilteredClassPath;
+import com.android.tools.r8.utils.DescriptorUtils;
 import com.google.common.io.ByteStreams;
-import java.io.File;
+import java.io.Closeable;
 import java.io.IOException;
 import java.io.InputStream;
 import java.nio.file.Path;
@@ -27,7 +26,7 @@
 /**
  * Lazy Java class file resource provider loading class files form a zip archive.
  */
-public final class ArchiveClassFileProvider implements ClassFileResourceProvider {
+public class ArchiveClassFileProvider implements ClassFileResourceProvider, Closeable {
 
   private final Set<String> descriptors = new HashSet<>();
   private final ZipFile zipFile;
@@ -37,24 +36,8 @@
     return new ArchiveClassFileProvider(archive);
   }
 
-  // Guess class descriptor from location of the class file.
-  static String guessTypeDescriptor(Path name) {
-    return guessTypeDescriptor(name.toString());
-  }
-
-  // Guess class descriptor from location of the class file.
-  private static String guessTypeDescriptor(String name) {
-    assert name != null;
-    assert name.endsWith(CLASS_EXTENSION) :
-        "Name " + name + " must have " + CLASS_EXTENSION + " suffix";
-    String fileName =
-        File.separatorChar == '/' ? name.toString() :
-            name.toString().replace(File.separatorChar, '/');
-    String descriptor = fileName.substring(0, fileName.length() - CLASS_EXTENSION.length());
-    if (descriptor.contains(".")) {
-      throw new CompilationError("Unexpected file name in the archive: " + fileName);
-    }
-    return 'L' + descriptor + ';';
+  protected ArchiveClassFileProvider(Path archive) throws IOException {
+    this(FilteredClassPath.unfiltered(archive));
   }
 
   private ArchiveClassFileProvider(FilteredClassPath archive) throws IOException {
@@ -66,7 +49,7 @@
       String name = entry.getName();
       Path entryPath = Paths.get(name);
       if (isClassFile(entryPath) && archive.matchesFile(entryPath)) {
-        descriptors.add(guessTypeDescriptor(name));
+        descriptors.add(DescriptorUtils.guessTypeDescriptor(name));
       }
     }
   }
@@ -95,7 +78,7 @@
 
   @Override
   protected void finalize() throws Throwable {
-    zipFile.close();
+    close();
     super.finalize();
   }
 
@@ -104,6 +87,11 @@
     return descriptors.size() + " resources from '" + zipFile.getName() +"'";
   }
 
+  @Override
+  public void close() throws IOException {
+    zipFile.close();
+  }
+
   private ZipEntry getZipEntryFromDescriptor(String descriptor) {
     return zipFile.getEntry(descriptor.substring(1, descriptor.length() - 1) + CLASS_EXTENSION);
   }
diff --git a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
index 426197b..e6f099e 100644
--- a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
+++ b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
@@ -73,7 +73,7 @@
     return diagnosticsHandler;
   }
 
-  boolean getEnableDesugaring() {
+  public boolean getEnableDesugaring() {
     return enableDesugaring;
   }
 
@@ -85,6 +85,7 @@
     private CompilationMode mode;
     private int minApiLevel = AndroidApiLevel.getDefault().getLevel();
     private DiagnosticsHandler diagnosticsHandler = new DefaultDiagnosticsHandler();
+    private boolean enableDesugaring = true;
 
     protected Builder(CompilationMode mode) {
       this(AndroidApp.builder(), mode, false);
@@ -160,8 +161,13 @@
       return self();
     }
 
-    protected boolean getEnableDesugaring() {
-      return true;
+    public B setEnableDesugaring(boolean enableDesugaring) {
+      this.enableDesugaring = enableDesugaring;
+      return self();
+    }
+
+    public boolean getEnableDesugaring() {
+      return enableDesugaring;
     }
 
     protected void validate() throws CompilationException {
diff --git a/src/main/java/com/android/tools/r8/GenerateMainDexList.java b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
index 296507f..d4b1ee5 100644
--- a/src/main/java/com/android/tools/r8/GenerateMainDexList.java
+++ b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
@@ -9,7 +9,6 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.shaking.Enqueuer;
 import com.android.tools.r8.shaking.MainDexListBuilder;
-import com.android.tools.r8.shaking.ProguardRuleParserException;
 import com.android.tools.r8.shaking.RootSetBuilder;
 import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
 import com.android.tools.r8.utils.AndroidApp;
@@ -42,7 +41,8 @@
     AppInfoWithSubtyping appInfo = new AppInfoWithSubtyping(application);
     RootSet mainDexRootSet =
         new RootSetBuilder(application, appInfo, options.mainDexKeepRules, options).run(executor);
-    Set<DexType> mainDexBaseClasses = new Enqueuer(appInfo).traceMainDex(mainDexRootSet, timing);
+    Set<DexType> mainDexBaseClasses =
+        new Enqueuer(appInfo, options).traceMainDex(mainDexRootSet, timing);
     Set<DexType> mainDexClasses = new MainDexListBuilder(mainDexBaseClasses, application).run();
 
     List<String> result = mainDexClasses.stream()
@@ -105,7 +105,7 @@
   }
 
   public static void main(String[] args)
-      throws IOException, ProguardRuleParserException, CompilationException, ExecutionException {
+      throws IOException, CompilationException, ExecutionException {
     GenerateMainDexListCommand.Builder builder = GenerateMainDexListCommand.parse(args);
     GenerateMainDexListCommand command = builder.build();
     if (command.isPrintHelp()) {
diff --git a/src/main/java/com/android/tools/r8/GenerateMainDexListCommand.java b/src/main/java/com/android/tools/r8/GenerateMainDexListCommand.java
index dd15f32..5caa704 100644
--- a/src/main/java/com/android/tools/r8/GenerateMainDexListCommand.java
+++ b/src/main/java/com/android/tools/r8/GenerateMainDexListCommand.java
@@ -11,6 +11,7 @@
 import com.android.tools.r8.shaking.ProguardConfigurationSourceStrings;
 import com.android.tools.r8.shaking.ProguardRuleParserException;
 import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DefaultDiagnosticsHandler;
 import com.android.tools.r8.utils.InternalOptions;
 import com.google.common.collect.ImmutableList;
 import java.io.IOException;
@@ -101,7 +102,8 @@
       if (this.mainDexRules.isEmpty()) {
         mainDexKeepRules = ImmutableList.of();
       } else {
-        ProguardConfigurationParser parser = new ProguardConfigurationParser(factory);
+        ProguardConfigurationParser parser =
+            new ProguardConfigurationParser(factory, new DefaultDiagnosticsHandler());
         try {
           parser.parse(mainDexRules);
           mainDexKeepRules = parser.getConfig().getRules();
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 39e0342..f72dd9f 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -32,7 +32,6 @@
 import com.android.tools.r8.shaking.Enqueuer;
 import com.android.tools.r8.shaking.MainDexListBuilder;
 import com.android.tools.r8.shaking.ProguardClassNameList;
-import com.android.tools.r8.shaking.ProguardRuleParserException;
 import com.android.tools.r8.shaking.ProguardTypeMatcher.MatchSpecificType;
 import com.android.tools.r8.shaking.ReasonPrinter;
 import com.android.tools.r8.shaking.RootSetBuilder;
@@ -221,7 +220,7 @@
             new RootSetBuilder(
                     application, appInfo, options.proguardConfiguration.getRules(), options)
                 .run(executorService);
-        Enqueuer enqueuer = new Enqueuer(appInfo);
+        Enqueuer enqueuer = new Enqueuer(appInfo, options);
         enqueuer.addExtension(new ProtoLiteExtension(appInfo));
         appInfo = enqueuer.traceApplication(rootSet, timing);
         if (options.proguardConfiguration.isPrintSeeds()) {
@@ -282,7 +281,7 @@
 
       if (!options.mainDexKeepRules.isEmpty()) {
         appInfo = new AppInfoWithSubtyping(application);
-        Enqueuer enqueuer = new Enqueuer(appInfo);
+        Enqueuer enqueuer = new Enqueuer(appInfo, options);
         // Lets find classes which may have code executed before secondary dex files installation.
         RootSet mainDexRootSet =
             new RootSetBuilder(application, appInfo, options.mainDexKeepRules, options)
@@ -301,7 +300,7 @@
       if (options.useTreeShaking || !options.skipMinification) {
         timing.begin("Post optimization code stripping");
         try {
-          Enqueuer enqueuer = new Enqueuer(appInfo);
+          Enqueuer enqueuer = new Enqueuer(appInfo, options);
           appInfo = enqueuer.traceApplication(rootSet, timing);
           if (options.useTreeShaking) {
             TreePruner pruner = new TreePruner(application, appInfo.withLiveness(), options);
@@ -492,8 +491,7 @@
     return outputApp;
   }
 
-  private static void run(String[] args)
-      throws IOException, ProguardRuleParserException, CompilationException {
+  private static void run(String[] args) throws IOException, CompilationException {
     R8Command.Builder builder = R8Command.parse(args);
     if (builder.getOutputPath() == null) {
       builder.setOutputPath(Paths.get("."));
@@ -521,9 +519,6 @@
     } catch (IOException e) {
       System.err.println("Failed to read or write Android app: " + e.getMessage());
       System.exit(1);
-    } catch (ProguardRuleParserException e) {
-      System.err.println("Failed parsing proguard keep rules: " + e.getMessage());
-      System.exit(1);
     } catch (RuntimeException e) {
       System.err.println("Compilation failed with an internal error.");
       Throwable cause = e.getCause() == null ? e : e.getCause();
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 502dd3c..d8a5be5 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -36,12 +36,18 @@
     private Optional<Boolean> discardedChecker = Optional.empty();
     private Optional<Boolean> minification = Optional.empty();
     private boolean ignoreMissingClasses = false;
+    private boolean forceProguardCompatibility = false;
     private Path proguardMapOutput = null;
 
     private Builder() {
       super(CompilationMode.RELEASE);
     }
 
+    protected Builder(boolean forceProguardCompatibility) {
+      super(CompilationMode.RELEASE);
+      this.forceProguardCompatibility = forceProguardCompatibility;
+    }
+
     private Builder(AndroidApp app) {
       super(app, CompilationMode.RELEASE);
     }
@@ -195,7 +201,8 @@
       if (this.mainDexRules.isEmpty()) {
         mainDexKeepRules = ImmutableList.of();
       } else {
-        ProguardConfigurationParser parser = new ProguardConfigurationParser(factory);
+        ProguardConfigurationParser parser =
+            new ProguardConfigurationParser(factory, getDiagnosticsHandler());
         try {
           parser.parse(mainDexRules);
           mainDexKeepRules = parser.getConfig().getRules();
@@ -207,7 +214,8 @@
       if (proguardConfigs.isEmpty()) {
         configuration = ProguardConfiguration.defaultConfiguration(factory);
       } else {
-        ProguardConfigurationParser parser = new ProguardConfigurationParser(factory);
+        ProguardConfigurationParser parser =
+            new ProguardConfigurationParser(factory, getDiagnosticsHandler());
         try {
           parser.parse(proguardConfigs);
         } catch (ProguardRuleParserException e) {
@@ -243,6 +251,7 @@
           useDiscardedChecker,
           useMinification,
           ignoreMissingClasses,
+          forceProguardCompatibility,
           proguardMapOutput);
     }
   }
@@ -283,6 +292,7 @@
   private final boolean useDiscardedChecker;
   private final boolean useMinification;
   private final boolean ignoreMissingClasses;
+  private final boolean forceProguardCompatibility;
   private final Path proguardMapOutput;
 
   public static Builder builder() {
@@ -400,6 +410,7 @@
       boolean useDiscardedChecker,
       boolean useMinification,
       boolean ignoreMissingClasses,
+      boolean forceProguardCompatibility,
       Path proguardMapOutput) {
     super(inputApp, outputPath, outputMode, mode, minApiLevel, diagnosticsHandler,
         enableDesugaring);
@@ -413,6 +424,7 @@
     this.useDiscardedChecker = useDiscardedChecker;
     this.useMinification = useMinification;
     this.ignoreMissingClasses = ignoreMissingClasses;
+    this.forceProguardCompatibility = forceProguardCompatibility;
     this.proguardMapOutput = proguardMapOutput;
   }
 
@@ -425,6 +437,7 @@
     useDiscardedChecker = false;
     useMinification = false;
     ignoreMissingClasses = false;
+    forceProguardCompatibility = false;
     proguardMapOutput = null;
   }
   public boolean useTreeShaking() {
@@ -477,6 +490,11 @@
       internal.inlineAccessors = false;
     }
     internal.proguardMapOutput = proguardMapOutput;
+
+    // EXPERIMENTAL flags.
+    assert !internal.forceProguardCompatibility;
+    internal.forceProguardCompatibility = forceProguardCompatibility;
+
     return internal;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ReadKeepFile.java b/src/main/java/com/android/tools/r8/ReadKeepFile.java
index 355d42c..86ef5a4 100644
--- a/src/main/java/com/android/tools/r8/ReadKeepFile.java
+++ b/src/main/java/com/android/tools/r8/ReadKeepFile.java
@@ -6,6 +6,7 @@
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.shaking.ProguardConfigurationParser;
 import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.utils.DefaultDiagnosticsHandler;
 import com.android.tools.r8.utils.Timing;
 import java.io.IOException;
 import java.nio.file.Paths;
@@ -23,7 +24,8 @@
     try {
       System.out.println("  - reading " + fileName);
       timing.begin("Reading " + fileName);
-      new ProguardConfigurationParser(new DexItemFactory()).parse(Paths.get(fileName));
+      new ProguardConfigurationParser(new DexItemFactory(), new DefaultDiagnosticsHandler())
+          .parse(Paths.get(fileName));
       timing.end();
     } catch (IOException e) {
       System.err.print("Failed to parse Proguard keep file: " + e.getMessage());
diff --git a/src/main/java/com/android/tools/r8/code/BaseInstructionFactory.java b/src/main/java/com/android/tools/r8/code/BaseInstructionFactory.java
index 8637cca..4e15eb9 100644
--- a/src/main/java/com/android/tools/r8/code/BaseInstructionFactory.java
+++ b/src/main/java/com/android/tools/r8/code/BaseInstructionFactory.java
@@ -454,6 +454,10 @@
         return new InvokeCustom(high, stream, mapping);
       case InvokeCustomRange.OPCODE:
         return new InvokeCustomRange(high, stream, mapping);
+      case ConstMethodHandle.OPCODE:
+        return new ConstMethodHandle(high, stream, mapping);
+      case ConstMethodType.OPCODE:
+        return new ConstMethodType(high, stream, mapping);
       default:
         throw new IllegalArgumentException("Illegal Opcode: 0x" + Integer.toString(opcode, 16));
     }
diff --git a/src/main/java/com/android/tools/r8/code/ConstClass.java b/src/main/java/com/android/tools/r8/code/ConstClass.java
index 04fda4b..a5755e9 100644
--- a/src/main/java/com/android/tools/r8/code/ConstClass.java
+++ b/src/main/java/com/android/tools/r8/code/ConstClass.java
@@ -36,7 +36,7 @@
 
   @Override
   public void registerUse(UseRegistry registry) {
-    registry.registerTypeReference(getType());
+    registry.registerConstClass(getType());
   }
 
   public DexType getType() {
diff --git a/src/main/java/com/android/tools/r8/code/ConstMethodHandle.java b/src/main/java/com/android/tools/r8/code/ConstMethodHandle.java
new file mode 100644
index 0000000..e2c084b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/ConstMethodHandle.java
@@ -0,0 +1,77 @@
+// 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.code;
+
+import com.android.tools.r8.ApiLevelException;
+import com.android.tools.r8.errors.InternalCompilerError;
+import com.android.tools.r8.graph.DexMethodHandle;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.naming.ClassNameMapper;
+import java.nio.ShortBuffer;
+
+public class ConstMethodHandle extends Format21c {
+
+  public static final int OPCODE = 0xfe;
+  public static final String NAME = "ConstMethodHandle";
+  public static final String SMALI_NAME = "const-method-handle";
+
+  ConstMethodHandle(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+    super(high, stream, mapping.getMethodHandleMap());
+  }
+
+  public ConstMethodHandle(int register, DexMethodHandle methodHandle) {
+    super(register, methodHandle);
+  }
+
+  public DexMethodHandle getMethodHandle() {
+    return (DexMethodHandle) BBBB;
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  public String toString(ClassNameMapper naming) {
+    return formatString("v" + AA + ", \"" + BBBB.toString() + "\"");
+  }
+
+  public String toSmaliString(ClassNameMapper naming) {
+    return formatSmaliString("v" + AA + ", \"" + BBBB.toString() + "\"");
+  }
+
+  @Override
+  public void registerUse(UseRegistry registry) {
+    registry.registerMethodHandle(getMethodHandle());
+  }
+
+  @Override
+  public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+    int index = BBBB.getOffset(mapping);
+    if (index != (index & 0xffff)) {
+      throw new InternalCompilerError("MethodHandle-index overflow.");
+    }
+    super.write(dest, mapping);
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) throws ApiLevelException {
+    builder.addConstMethodHandle(AA, (DexMethodHandle) BBBB);
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/ConstMethodType.java b/src/main/java/com/android/tools/r8/code/ConstMethodType.java
new file mode 100644
index 0000000..7e50926
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/ConstMethodType.java
@@ -0,0 +1,72 @@
+// 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.code;
+
+import com.android.tools.r8.ApiLevelException;
+import com.android.tools.r8.errors.InternalCompilerError;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.naming.ClassNameMapper;
+import java.nio.ShortBuffer;
+
+public class ConstMethodType extends Format21c {
+
+  public static final int OPCODE = 0xff;
+  public static final String NAME = "ConstMethodType";
+  public static final String SMALI_NAME = "const-method-type";
+
+  ConstMethodType(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+    super(high, stream, mapping.getProtosMap());
+  }
+
+  public ConstMethodType(int register, DexProto methodType) {
+    super(register, methodType);
+  }
+
+  public DexProto getMethodType() {
+    return (DexProto) BBBB;
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  public String toString(ClassNameMapper naming) {
+    return formatString("v" + AA + ", \"" + BBBB.toString() + "\"");
+  }
+
+  public String toSmaliString(ClassNameMapper naming) {
+    return formatSmaliString("v" + AA + ", \"" + BBBB.toString() + "\"");
+  }
+
+  @Override
+  public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+    int index = BBBB.getOffset(mapping);
+    if (index != (index & 0xffff)) {
+      throw new InternalCompilerError("MethodType-index overflow.");
+    }
+    super.write(dest, mapping);
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) throws ApiLevelException {
+    builder.addConstMethodType(AA, (DexProto) BBBB);
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/InvokeCustom.java b/src/main/java/com/android/tools/r8/code/InvokeCustom.java
index 3c0b67c..c4a16b5 100644
--- a/src/main/java/com/android/tools/r8/code/InvokeCustom.java
+++ b/src/main/java/com/android/tools/r8/code/InvokeCustom.java
@@ -4,8 +4,6 @@
 package com.android.tools.r8.code;
 
 import com.android.tools.r8.graph.DexCallSite;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexMethodHandle;
 import com.android.tools.r8.graph.DexValue;
 import com.android.tools.r8.graph.DexValue.DexValueMethodHandle;
 import com.android.tools.r8.graph.DexValue.DexValueType;
@@ -61,54 +59,15 @@
   }
 
   static void registerCallSite(UseRegistry registry, DexCallSite callSite) {
-    InvokeCustom.registerMethodHandle(registry, callSite.bootstrapMethod);
+    registry.registerMethodHandle(callSite.bootstrapMethod);
 
     // Register bootstrap method arguments, only Type and MethodHandle need to be register.
     for (DexValue arg : callSite.bootstrapArgs) {
       if (arg instanceof DexValueType) {
         registry.registerTypeReference(((DexValueType) arg).value);
       } else if (arg instanceof DexValueMethodHandle) {
-        InvokeCustom.registerMethodHandle(registry, ((DexValueMethodHandle) arg).value);
+        registry.registerMethodHandle(((DexValueMethodHandle) arg).value);
       }
     }
   }
-
-  static void registerMethodHandle(UseRegistry registry, DexMethodHandle methodHandle) {
-    switch (methodHandle.type) {
-      case INSTANCE_GET:
-        registry.registerInstanceFieldRead(methodHandle.asField());
-        break;
-      case INSTANCE_PUT:
-        registry.registerInstanceFieldWrite(methodHandle.asField());
-        break;
-      case STATIC_GET:
-        registry.registerStaticFieldRead(methodHandle.asField());
-        break;
-      case STATIC_PUT:
-        registry.registerStaticFieldWrite(methodHandle.asField());
-        break;
-      case INVOKE_INSTANCE:
-        registry.registerInvokeVirtual(methodHandle.asMethod());
-        break;
-      case INVOKE_STATIC:
-        registry.registerInvokeStatic(methodHandle.asMethod());
-        break;
-      case INVOKE_CONSTRUCTOR:
-        DexMethod method = methodHandle.asMethod();
-        registry.registerNewInstance(method.getHolder());
-        registry.registerInvokeDirect(method);
-        break;
-      case INVOKE_INTERFACE:
-        registry.registerInvokeInterface(methodHandle.asMethod());
-        break;
-      case INVOKE_SUPER:
-        registry.registerInvokeSuper(methodHandle.asMethod());
-        break;
-      case INVOKE_DIRECT:
-        registry.registerInvokeDirect(methodHandle.asMethod());
-        break;
-      default:
-        throw new AssertionError();
-    }
-  }
 }
diff --git a/src/main/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilderCommandBuilder.java b/src/main/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilderCommandBuilder.java
index 5773d7d..dcfd68b 100644
--- a/src/main/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilderCommandBuilder.java
+++ b/src/main/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilderCommandBuilder.java
@@ -8,10 +8,7 @@
 public class CompatDexBuilderCommandBuilder extends D8Command.Builder {
   CompatDexBuilderCommandBuilder() {
     super(true);
+    setEnableDesugaring(false);
   }
 
-  @Override
-  protected boolean getEnableDesugaring() {
-    return false;
-  }
 }
diff --git a/src/main/java/com/android/tools/r8/compatdx/CompatDxCommandBuilder.java b/src/main/java/com/android/tools/r8/compatdx/CompatDxCommandBuilder.java
index 3e8a006..568b7fd 100644
--- a/src/main/java/com/android/tools/r8/compatdx/CompatDxCommandBuilder.java
+++ b/src/main/java/com/android/tools/r8/compatdx/CompatDxCommandBuilder.java
@@ -9,10 +9,6 @@
 public class CompatDxCommandBuilder extends D8Command.Builder {
   CompatDxCommandBuilder() {
     super(true);
-  }
-
-  @Override
-  protected boolean getEnableDesugaring() {
-    return false;
+    setEnableDesugaring(false);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java b/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java
index 1ad589f..9dc5424 100644
--- a/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java
+++ b/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java
@@ -26,17 +26,21 @@
   public static class CompatProguardOptions {
     public final String output;
     public final int minApi;
+    public final boolean forceProguardCompatibility;
     public final List<String> proguardConfig;
 
-    CompatProguardOptions(List<String> proguardConfig, String output, int minApi) {
+    CompatProguardOptions(List<String> proguardConfig, String output, int minApi,
+        boolean forceProguardCompatibility) {
       this.output = output;
       this.minApi = minApi;
+      this.forceProguardCompatibility = forceProguardCompatibility;
       this.proguardConfig = proguardConfig;
     }
 
     public static CompatProguardOptions parse(String[] args) throws CompilationException {
       String output = null;
       int minApi = 1;
+      boolean forceProguardCompatibility = false;
       ImmutableList.Builder<String> builder = ImmutableList.builder();
       if (args.length > 0) {
         StringBuilder currentLine = new StringBuilder(args[0]);
@@ -45,6 +49,8 @@
           if (arg.charAt(0) == '-') {
             if (arg.equals("--min-api")) {
               minApi = Integer.valueOf(args[++i]);
+            } else if (arg.equals("--force-proguard-compatibility")) {
+              forceProguardCompatibility = true;
             } else if (arg.equals("--output")) {
               output = args[++i];
             } else if (arg.equals("-outjars")) {
@@ -60,7 +66,7 @@
         }
         builder.add(currentLine.toString());
       }
-      return new CompatProguardOptions(builder.build(), output, minApi);
+      return new CompatProguardOptions(builder.build(), output, minApi, forceProguardCompatibility);
     }
   }
 
@@ -68,11 +74,12 @@
     System.out.println("CompatProguard " + String.join(" ", args));
     // Run R8 passing all the options from the command line as a Proguard configuration.
     CompatProguardOptions options = CompatProguardOptions.parse(args);
-    R8.run(R8Command.builder()
-        .setOutputPath(Paths.get(options.output))
+    R8Command.Builder builder =
+        new CompatProguardCommandBuilder(options.forceProguardCompatibility);
+    builder.setOutputPath(Paths.get(options.output))
         .addProguardConfiguration(options.proguardConfig)
-        .setMinApiLevel(options.minApi)
-        .build());
+        .setMinApiLevel(options.minApi);
+    R8.run(builder.build());
   }
 
   public static void main(String[] args) throws IOException {
diff --git a/src/main/java/com/android/tools/r8/compatproguard/CompatProguardCommandBuilder.java b/src/main/java/com/android/tools/r8/compatproguard/CompatProguardCommandBuilder.java
new file mode 100644
index 0000000..df2298c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/compatproguard/CompatProguardCommandBuilder.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.compatproguard;
+
+import com.android.tools.r8.R8Command;
+
+public class CompatProguardCommandBuilder extends R8Command.Builder {
+  CompatProguardCommandBuilder(boolean forceProguardCompatibility) {
+    super(forceProguardCompatibility);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index 5de6538..e454d89 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -239,6 +239,19 @@
     return true;
   }
 
+  public boolean hasDefaultInitializer() {
+    return getDefaultInitializer() != null;
+  }
+
+  public DexEncodedMethod getDefaultInitializer() {
+    for (DexEncodedMethod method : directMethods()) {
+      if (method.isDefaultInitializer()) {
+        return method;
+      }
+    }
+    return null;
+  }
+
   public boolean defaultValuesForStaticFieldsMayTriggerAllocation() {
     return Arrays.stream(staticFields())
         .anyMatch(field -> !field.staticValue.mayTriggerAllocation());
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index 7766a94..a541f5a 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -204,6 +204,10 @@
     return accessFlags.isConstructor() && !accessFlags.isStatic();
   }
 
+  public boolean isDefaultInitializer() {
+    return isInstanceInitializer() && method.proto.parameters.isEmpty();
+  }
+
   public boolean isClassInitializer() {
     return accessFlags.isConstructor() && accessFlags.isStatic();
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java b/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
index eeba4e8..d64a893 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
@@ -4,8 +4,10 @@
 package com.android.tools.r8.graph;
 
 import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.naming.NamingLens;
 
-public class DexMethodHandle extends IndexedDexItem {
+public class DexMethodHandle extends IndexedDexItem implements
+    PresortedComparable<DexMethodHandle> {
 
   public enum MethodHandleType {
     STATIC_PUT((short) 0x00),
@@ -190,4 +192,51 @@
     assert isFieldHandle();
     return (DexField) fieldOrMethod;
   }
+
+  @Override
+  public int slowCompareTo(DexMethodHandle other) {
+    int result = type.getValue() - other.type.getValue();
+    if (result == 0) {
+      if (isFieldHandle()) {
+        result = asField().slowCompareTo(other.asField());
+      } else {
+        assert isMethodHandle();
+        result = asMethod().slowCompareTo(other.asMethod());
+      }
+    }
+    return result;
+  }
+
+  @Override
+  public int slowCompareTo(DexMethodHandle other, NamingLens namingLens) {
+    int result = type.getValue() - other.type.getValue();
+    if (result == 0) {
+      if (isFieldHandle()) {
+        result = asField().slowCompareTo(other.asField(), namingLens);
+      } else {
+        assert isMethodHandle();
+        result = asMethod().slowCompareTo(other.asMethod(), namingLens);
+      }
+    }
+    return result;
+  }
+
+  @Override
+  public int layeredCompareTo(DexMethodHandle other, NamingLens namingLens) {
+    int result = type.getValue() - other.type.getValue();
+    if (result == 0) {
+      if (isFieldHandle()) {
+        result = asField().layeredCompareTo(other.asField(), namingLens);
+      } else {
+        assert isMethodHandle();
+        result = asMethod().layeredCompareTo(other.asMethod(), namingLens);
+      }
+    }
+    return result;
+  }
+
+  @Override
+  public int compareTo(DexMethodHandle other) {
+    return sortedCompareTo(other.getSortedIndex());
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/graph/UseRegistry.java b/src/main/java/com/android/tools/r8/graph/UseRegistry.java
index 45824c2..f6736fa 100644
--- a/src/main/java/com/android/tools/r8/graph/UseRegistry.java
+++ b/src/main/java/com/android/tools/r8/graph/UseRegistry.java
@@ -26,4 +26,47 @@
   public abstract boolean registerStaticFieldWrite(DexField field);
 
   public abstract boolean registerTypeReference(DexType type);
+
+  public void registerMethodHandle(DexMethodHandle methodHandle) {
+    switch (methodHandle.type) {
+      case INSTANCE_GET:
+        registerInstanceFieldRead(methodHandle.asField());
+        break;
+      case INSTANCE_PUT:
+        registerInstanceFieldWrite(methodHandle.asField());
+        break;
+      case STATIC_GET:
+        registerStaticFieldRead(methodHandle.asField());
+        break;
+      case STATIC_PUT:
+        registerStaticFieldWrite(methodHandle.asField());
+        break;
+      case INVOKE_INSTANCE:
+        registerInvokeVirtual(methodHandle.asMethod());
+        break;
+      case INVOKE_STATIC:
+        registerInvokeStatic(methodHandle.asMethod());
+        break;
+      case INVOKE_CONSTRUCTOR:
+        DexMethod method = methodHandle.asMethod();
+        registerNewInstance(method.getHolder());
+        registerInvokeDirect(method);
+        break;
+      case INVOKE_INTERFACE:
+        registerInvokeInterface(methodHandle.asMethod());
+        break;
+      case INVOKE_SUPER:
+        registerInvokeSuper(methodHandle.asMethod());
+        break;
+      case INVOKE_DIRECT:
+        registerInvokeDirect(methodHandle.asMethod());
+        break;
+      default:
+        throw new AssertionError();
+    }
+  }
+
+  public boolean registerConstClass(DexType type) {
+    return registerTypeReference(type);
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstMethodHandle.java b/src/main/java/com/android/tools/r8/ir/code/ConstMethodHandle.java
new file mode 100644
index 0000000..108eb32
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstMethodHandle.java
@@ -0,0 +1,80 @@
+// 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.ir.code;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.DexMethodHandle;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+
+public class ConstMethodHandle extends ConstInstruction {
+
+  private final DexMethodHandle methodHandle;
+
+  public ConstMethodHandle(Value dest, DexMethodHandle methodHandle) {
+    super(dest);
+    dest.markNeverNull();
+    this.methodHandle = methodHandle;
+  }
+
+  public Value dest() {
+    return outValue;
+  }
+
+  public DexMethodHandle getValue() {
+    return methodHandle;
+  }
+
+  @Override
+  public void buildDex(DexBuilder builder) {
+    int dest = builder.allocatedRegister(dest(), getNumber());
+    builder.add(this, new com.android.tools.r8.code.ConstMethodHandle(dest, methodHandle));
+  }
+
+  @Override
+  public boolean identicalNonValueParts(Instruction other) {
+    return other.asConstMethodHandle().methodHandle == methodHandle;
+  }
+
+  @Override
+  public int compareNonValueParts(Instruction other) {
+    return methodHandle.slowCompareTo(other.asConstMethodHandle().methodHandle);
+  }
+
+  @Override
+  public int maxInValueRegister() {
+    assert false : "ConstMethodHandle has no register arguments.";
+    return 0;
+  }
+
+  @Override
+  public int maxOutValueRegister() {
+    return Constants.U8BIT_MAX;
+  }
+
+  @Override
+  public String toString() {
+    return super.toString() + " \"" + methodHandle + "\"";
+  }
+
+  @Override
+  public boolean instructionTypeCanThrow() {
+    return true;
+  }
+
+  @Override
+  public boolean isOutConstant() {
+    return true;
+  }
+
+  @Override
+  public boolean isConstMethodHandle() {
+    return true;
+  }
+
+  @Override
+  public ConstMethodHandle asConstMethodHandle() {
+    return this;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstMethodType.java b/src/main/java/com/android/tools/r8/ir/code/ConstMethodType.java
new file mode 100644
index 0000000..3e32bb6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstMethodType.java
@@ -0,0 +1,80 @@
+// 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.ir.code;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+
+public class ConstMethodType extends ConstInstruction {
+
+  private final DexProto methodType;
+
+  public ConstMethodType(Value dest, DexProto methodType) {
+    super(dest);
+    dest.markNeverNull();
+    this.methodType = methodType;
+  }
+
+  public Value dest() {
+    return outValue;
+  }
+
+  public DexProto getValue() {
+    return methodType;
+  }
+
+  @Override
+  public void buildDex(DexBuilder builder) {
+    int dest = builder.allocatedRegister(dest(), getNumber());
+    builder.add(this, new com.android.tools.r8.code.ConstMethodType(dest, methodType));
+  }
+
+  @Override
+  public boolean identicalNonValueParts(Instruction other) {
+    return other.asConstMethodType().methodType == methodType;
+  }
+
+  @Override
+  public int compareNonValueParts(Instruction other) {
+    return methodType.slowCompareTo(other.asConstMethodType().methodType);
+  }
+
+  @Override
+  public int maxInValueRegister() {
+    assert false : "ConstMethodType has no register arguments.";
+    return 0;
+  }
+
+  @Override
+  public int maxOutValueRegister() {
+    return Constants.U8BIT_MAX;
+  }
+
+  @Override
+  public String toString() {
+    return super.toString() + " \"" + methodType + "\"";
+  }
+
+  @Override
+  public boolean instructionTypeCanThrow() {
+    return true;
+  }
+
+  @Override
+  public boolean isOutConstant() {
+    return true;
+  }
+
+  @Override
+  public boolean isConstString() {
+    return true;
+  }
+
+  @Override
+  public ConstMethodType asConstMethodType() {
+    return this;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Instruction.java b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
index 21c2abf..5fd2ff1 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
@@ -450,6 +450,22 @@
     return null;
   }
 
+  public boolean isConstMethodHandle() {
+    return false;
+  }
+
+  public ConstMethodHandle asConstMethodHandle() {
+    return null;
+  }
+
+  public boolean isConstMethodType() {
+    return false;
+  }
+
+  public ConstMethodType asConstMethodType() {
+    return null;
+  }
+
   public boolean isConstString() {
     return false;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index 08bc75e..37db998 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -32,6 +32,8 @@
 import com.android.tools.r8.ir.code.Cmp;
 import com.android.tools.r8.ir.code.Cmp.Bias;
 import com.android.tools.r8.ir.code.ConstClass;
+import com.android.tools.r8.ir.code.ConstMethodHandle;
+import com.android.tools.r8.ir.code.ConstMethodType;
 import com.android.tools.r8.ir.code.ConstNumber;
 import com.android.tools.r8.ir.code.ConstString;
 import com.android.tools.r8.ir.code.ConstType;
@@ -799,6 +801,32 @@
     add(instruction);
   }
 
+  public void addConstMethodHandle(int dest, DexMethodHandle methodHandle)
+      throws ApiLevelException {
+    if (!options.canUseConstantMethodHandle()) {
+      throw new ApiLevelException(
+          AndroidApiLevel.P,
+          "Const-method-handle",
+          null /* sourceString */);
+    }
+    Value out = writeRegister(dest, MoveType.OBJECT, ThrowingInfo.CAN_THROW);
+    ConstMethodHandle instruction = new ConstMethodHandle(out, methodHandle);
+    add(instruction);
+  }
+
+  public void addConstMethodType(int dest, DexProto methodType)
+      throws ApiLevelException {
+    if (!options.canUseConstantMethodType()) {
+      throw new ApiLevelException(
+          AndroidApiLevel.P,
+          "Const-method-type",
+          null /* sourceString */);
+    }
+    Value out = writeRegister(dest, MoveType.OBJECT, ThrowingInfo.CAN_THROW);
+    ConstMethodType instruction = new ConstMethodType(out, methodType);
+    add(instruction);
+  }
+
   public void addConstString(int dest, DexString string) {
     Value out = writeRegister(dest, MoveType.OBJECT, ThrowingInfo.CAN_THROW);
     ConstString instruction = new ConstString(out, string);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
index e44ee80..9f3d3a4 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
@@ -142,6 +142,8 @@
   static final Type STRING_TYPE = Type.getObjectType("java/lang/String");
   static final Type INT_ARRAY_TYPE = Type.getObjectType(INT_ARRAY_DESC);
   static final Type THROWABLE_TYPE = Type.getObjectType("java/lang/Throwable");
+  static final Type METHOD_HANDLE_TYPE = Type.getObjectType("java/lang/invoke/MethodHandle");
+  static final Type METHOD_TYPE_TYPE = Type.getObjectType("java/lang/invoke/MethodType");
 
   private static final int[] NO_TARGETS = {};
 
@@ -619,7 +621,7 @@
       case Opcodes.LDC: {
         // const-class and const-string* may throw in dex.
         LdcInsnNode ldc = (LdcInsnNode) insn;
-        return ldc.cst instanceof String || ldc.cst instanceof Type;
+        return ldc.cst instanceof String || ldc.cst instanceof Type || ldc.cst instanceof Handle;
       }
       default:
         return false;
@@ -1727,6 +1729,8 @@
       state.push(Type.INT_TYPE);
     } else if (insn.cst instanceof Float) {
       state.push(Type.FLOAT_TYPE);
+    } else if (insn.cst instanceof Handle) {
+      state.push(METHOD_HANDLE_TYPE);
     } else {
       throw new CompilationError("Unsupported constant: " + insn.cst.toString());
     }
@@ -2724,11 +2728,16 @@
     }
   }
 
-  private void build(LdcInsnNode insn, IRBuilder builder) {
+  private void build(LdcInsnNode insn, IRBuilder builder) throws ApiLevelException {
     if (insn.cst instanceof Type) {
       Type type = (Type) insn.cst;
-      int dest = state.push(type);
-      builder.addConstClass(dest, application.getTypeFromDescriptor(type.getDescriptor()));
+      if (type.getSort() == Type.METHOD) {
+        int dest = state.push(METHOD_TYPE_TYPE);
+        builder.addConstMethodType(dest, application.getProto(type.getDescriptor()));
+      } else {
+        int dest = state.push(type);
+        builder.addConstClass(dest, application.getTypeFromDescriptor(type.getDescriptor()));
+      }
     } else if (insn.cst instanceof String) {
       int dest = state.push(STRING_TYPE);
       builder.addConstString(dest, application.getString((String) insn.cst));
@@ -2744,6 +2753,10 @@
     } else if (insn.cst instanceof Float) {
       int dest = state.push(Type.FLOAT_TYPE);
       builder.addFloatConst(dest, Float.floatToRawIntBits((Float) insn.cst));
+    } else if (insn.cst instanceof Handle) {
+      Handle handle = (Handle) insn.cst;
+      int dest = state.push(METHOD_HANDLE_TYPE);
+      builder.addConstMethodHandle(dest, getMethodHandle(application, handle));
     } else {
       throw new CompilationError("Unsupported constant: " + insn.cst.toString());
     }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/JarState.java b/src/main/java/com/android/tools/r8/ir/conversion/JarState.java
index f11af49..6faba19 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/JarState.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/JarState.java
@@ -122,7 +122,7 @@
 
       // Catch all matching.
       if (other == REFERENCE_TYPE) {
-        return sort == Type.OBJECT || sort == Type.ARRAY;
+        return sort == Type.OBJECT || sort == Type.ARRAY || sort == Type.METHOD;
       }
       if (other == OBJECT_TYPE) {
         return sort == Type.OBJECT;
diff --git a/src/main/java/com/android/tools/r8/jar/JarRegisterEffectsVisitor.java b/src/main/java/com/android/tools/r8/jar/JarRegisterEffectsVisitor.java
index 2d38bf0..46bcbd5 100644
--- a/src/main/java/com/android/tools/r8/jar/JarRegisterEffectsVisitor.java
+++ b/src/main/java/com/android/tools/r8/jar/JarRegisterEffectsVisitor.java
@@ -48,7 +48,13 @@
   @Override
   public void visitLdcInsn(Object cst) {
     if (cst instanceof Type) {
-      registry.registerTypeReference(application.getType((Type) cst));
+      // Nothing to register for method type, it represents only a prototype not associated with a
+      // method name.
+      if (((Type) cst).getSort() != Type.METHOD) {
+        registry.registerConstClass(application.getType((Type) cst));
+      }
+    } else if (cst instanceof Handle) {
+      registerMethodHandleType((Handle) cst);
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/naming/MemberNaming.java b/src/main/java/com/android/tools/r8/naming/MemberNaming.java
index 1537f05..762bdb6 100644
--- a/src/main/java/com/android/tools/r8/naming/MemberNaming.java
+++ b/src/main/java/com/android/tools/r8/naming/MemberNaming.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.naming;
 
+import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
@@ -280,6 +281,10 @@
           method.proto.returnType.toSourceString(), paramNames);
     }
 
+    public static MethodSignature initializer(String[] parameters) {
+      return new MethodSignature(Constants.INSTANCE_INITIALIZER_NAME, "void", parameters);
+    }
+
     @Override
     Signature asRenamed(String renamedName) {
       return new MethodSignature(renamedName, type, parameters);
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 ff8adee..358c1d3 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -28,6 +28,7 @@
 import com.android.tools.r8.graph.PresortedComparable;
 import com.android.tools.r8.logging.Log;
 import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
+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.Wrapper;
@@ -69,6 +70,7 @@
 public class Enqueuer {
 
   private final AppInfoWithSubtyping appInfo;
+  private final InternalOptions options;
   private RootSet rootSet;
 
   private Map<DexType, Set<DexMethod>> virtualInvokes = Maps.newIdentityHashMap();
@@ -156,8 +158,9 @@
    */
   private final Map<DexType, Set<DexAnnotation>> deferredAnnotations = new IdentityHashMap<>();
 
-  public Enqueuer(AppInfoWithSubtyping appInfo) {
+  public Enqueuer(AppInfoWithSubtyping appInfo, InternalOptions options) {
     this.appInfo = appInfo;
+    this.options = options;
   }
 
   public void addExtension(SemanticsProvider extension) {
@@ -332,6 +335,30 @@
       }
       return false;
     }
+
+    @Override
+    public boolean registerConstClass(DexType type) {
+      boolean result = registerTypeReference(type);
+      // For Proguard compatibility mark default initializer for live type as live.
+      if (options.forceProguardCompatibility) {
+        if (type.isArrayType()) {
+          return result;
+        }
+        assert type.isClassType();
+        DexClass holder = appInfo.definitionFor(type);
+        if (holder == null) {
+          // Don't call reportMissingClass(type) here. That already happened in the call to
+          // registerTypeReference(type).
+          return result;
+        }
+        if (holder.hasDefaultInitializer()) {
+          registerNewInstance(type);
+          DexEncodedMethod init = holder.getDefaultInitializer();
+          markDirectStaticOrConstructorMethodAsLive(init, KeepReason.reachableFromLiveType(type));
+        }
+      }
+      return result;
+    }
   }
 
   //
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
index a6e5703..cd56ef5 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8.shaking;
 
 import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.DiagnosticsHandler;
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.graph.DexAccessFlags;
 import com.android.tools.r8.graph.DexField;
@@ -17,6 +18,7 @@
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.InternalOptions.PackageObfuscationMode;
 import com.android.tools.r8.utils.LongInterval;
+import com.android.tools.r8.utils.StringDiagnostic;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 import java.io.File;
@@ -36,6 +38,8 @@
 
   private final DexItemFactory dexItemFactory;
 
+  private final DiagnosticsHandler diagnosticsHandler;
+
   private static final List<String> ignoredSingleArgOptions = ImmutableList
       .of("protomapping",
           "target");
@@ -68,9 +72,11 @@
   private static final List<String> unsupportedFlagOptions = ImmutableList
       .of("skipnonpubliclibraryclasses");
 
-  public ProguardConfigurationParser(DexItemFactory dexItemFactory) {
+  public ProguardConfigurationParser(
+      DexItemFactory dexItemFactory, DiagnosticsHandler diagnosticsHandler) {
     this.dexItemFactory = dexItemFactory;
     configurationBuilder = ProguardConfiguration.builder(dexItemFactory);
+    this.diagnosticsHandler = diagnosticsHandler;
   }
 
   public ProguardConfiguration.Builder getConfigurationBuilder() {
@@ -101,7 +107,7 @@
   public void parse(List<ProguardConfigurationSource> sources)
       throws ProguardRuleParserException, IOException {
     for (ProguardConfigurationSource source : sources) {
-      new ProguardFileParser(source).parse();
+      new ProguardFileParser(source, diagnosticsHandler).parse();
     }
   }
 
@@ -111,7 +117,8 @@
     private int position = 0;
     private Path baseDirectory;
 
-    ProguardFileParser(ProguardConfigurationSource source) throws IOException {
+    ProguardFileParser(ProguardConfigurationSource source, DiagnosticsHandler diagnosticsHandler)
+        throws IOException {
       contents = source.get();
       baseDirectory = source.getBaseDirectory();
       name = source.getName();
@@ -123,7 +130,8 @@
       } while (parseOption());
     }
 
-    private boolean parseOption() throws ProguardRuleParserException {
+    private boolean parseOption()
+        throws ProguardRuleParserException {
       if (eof()) {
         return false;
       }
@@ -270,6 +278,14 @@
         configurationBuilder.addRule(rule);
       } else if (acceptString("useuniqueclassmembernames")) {
         configurationBuilder.setUseUniqueClassMemberNames(true);
+      } else if (acceptString("adaptclassstrings")) {
+        if (isOptionalArgumentGiven()) {
+          configurationBuilder.addRule(parseAdaptClassStrings());
+        } else {
+          configurationBuilder.addRule(ProguardIdentifierNameStringRule.defaultAllRule());
+        }
+        // TODO(b/36799092): warn until it is fully implemented.
+        warnIgnoringOptions("adaptclassstrings");
       } else if (acceptString("identifiernamestring")) {
         configurationBuilder.addRule(parseIdentifierNameStringRule());
         // TODO(b/36799092): warn until it is fully implemented.
@@ -281,17 +297,19 @@
     }
 
     private void warnIgnoringOptions(String optionName) {
-      System.out.println("WARNING: Ignoring option: -" + optionName);
+      diagnosticsHandler.warning(new StringDiagnostic("Ignoring option: -" + optionName));
     }
 
     private void warnOverridingOptions(String optionName, String victim) {
-      System.out.println("WARNING: option -" + optionName + " overrides -" + victim);
+      diagnosticsHandler.warning(
+          new StringDiagnostic("Option -" + optionName + " overrides -" + victim));
     }
 
     private void parseInclude() throws ProguardRuleParserException {
       Path included = parseFileName();
       try {
-        new ProguardFileParser(new ProguardConfigurationSourceFile(included)).parse();
+        new ProguardFileParser(new ProguardConfigurationSourceFile(included), diagnosticsHandler)
+            .parse();
       } catch (FileNotFoundException | NoSuchFileException e) {
         throw parseError("Included file '" + included.toString() + "' not found", e);
       } catch (IOException e) {
@@ -446,6 +464,14 @@
       return keepRuleBuilder.build();
     }
 
+    private ProguardIdentifierNameStringRule parseAdaptClassStrings()
+        throws ProguardRuleParserException {
+      ProguardIdentifierNameStringRule.Builder keepRuleBuilder =
+          ProguardIdentifierNameStringRule.builder();
+      keepRuleBuilder.setClassNames(parseClassNames());
+      return keepRuleBuilder.build();
+    }
+
     private ProguardIdentifierNameStringRule parseIdentifierNameStringRule()
         throws ProguardRuleParserException {
       ProguardIdentifierNameStringRule.Builder keepRuleBuilder =
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardIdentifierNameStringRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardIdentifierNameStringRule.java
index 034bd13..44e7078 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardIdentifierNameStringRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardIdentifierNameStringRule.java
@@ -43,4 +43,11 @@
     return "identifiernamestring";
   }
 
+  public static ProguardIdentifierNameStringRule defaultAllRule() {
+    ProguardIdentifierNameStringRule.Builder builder = ProguardIdentifierNameStringRule.builder();
+    builder.setClassNames(
+        ProguardClassNameList.singletonList(ProguardTypeMatcher.defaultAllMatcher()));
+    return builder.build();
+  }
+
 }
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
index 5c0d869..3b5cdbc 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -202,7 +202,7 @@
         markMatchingFields(clazz, memberKeepRules, rule, null);
       } else {
         assert rule instanceof ProguardIdentifierNameStringRule;
-        // TODO(b/36799092): collect string literals while marking matching methods/fields.
+        // TODO(b/36799092): collect string literals while marking class and matching members.
       }
     }
   }
diff --git a/src/main/java/com/android/tools/r8/shaking/TreePruner.java b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
index 947abe3..10716bd 100644
--- a/src/main/java/com/android/tools/r8/shaking/TreePruner.java
+++ b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
@@ -76,7 +76,7 @@
       } else {
         newClasses.add(clazz);
         if (!appInfo.instantiatedTypes.contains(clazz.type) &&
-            (!options.debugKeepRules || !hasDefaultConstructor(clazz))) {
+            (!options.debugKeepRules || !clazz.hasDefaultInitializer())) {
           // The class is only needed as a type but never instantiated. Make it abstract to reflect
           // this.
           if (clazz.accessFlags.isFinal()) {
@@ -102,15 +102,6 @@
     return newClasses;
   }
 
-  private boolean hasDefaultConstructor(DexProgramClass clazz) {
-    for (DexEncodedMethod method : clazz.directMethods()) {
-      if (isDefaultConstructor(method)) {
-        return true;
-      }
-    }
-    return false;
-  }
-
   private <S extends PresortedComparable<S>, T extends KeyedDexItem<S>> int firstUnreachableIndex(
       T[] items, Set<S> live) {
     for (int i = 0; i < items.length; i++) {
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApp.java b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
index 6064b64..80a2393 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApp.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
@@ -8,6 +8,7 @@
 import static com.android.tools.r8.utils.FileUtils.isDexFile;
 import static com.android.tools.r8.utils.FileUtils.isVDexFile;
 
+import com.android.tools.r8.ArchiveClassFileProvider;
 import com.android.tools.r8.ClassFileResourceProvider;
 import com.android.tools.r8.Resource;
 import com.android.tools.r8.Resource.Kind;
diff --git a/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java b/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
index 84beae5..ee3c845 100644
--- a/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
@@ -4,9 +4,14 @@
 
 package com.android.tools.r8.utils;
 
+import static com.android.tools.r8.utils.FileUtils.CLASS_EXTENSION;
+
+import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.naming.ClassNameMapper;
 import com.google.common.collect.ImmutableMap;
+import java.io.File;
+import java.nio.file.Path;
 import java.util.Map;
 
 public class DescriptorUtils {
@@ -249,4 +254,23 @@
     }
   }
 
+  // Guess class descriptor from location of the class file.
+  public static String guessTypeDescriptor(Path name) {
+    return guessTypeDescriptor(name.toString());
+  }
+
+  // Guess class descriptor from location of the class file.
+  public static String guessTypeDescriptor(String name) {
+    assert name != null;
+    assert name.endsWith(CLASS_EXTENSION) :
+        "Name " + name + " must have " + CLASS_EXTENSION + " suffix";
+    String fileName =
+        File.separatorChar == '/' ? name.toString() :
+            name.toString().replace(File.separatorChar, '/');
+    String descriptor = fileName.substring(0, fileName.length() - CLASS_EXTENSION.length());
+    if (descriptor.contains(".")) {
+      throw new CompilationError("Unexpected class file name: " + fileName);
+    }
+    return 'L' + descriptor + ';';
+  }
 }
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 c8308a9..7f6fe45 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -103,6 +103,8 @@
   public String printCfgFile;
   public Path printMainDexListFile;
   public boolean ignoreMissingClasses = false;
+  // EXPERIMENTAL flag to get behaviour as close to Proguard as possible.
+  public boolean forceProguardCompatibility = false;
   public boolean skipMinification = false;
   public boolean disableAssertions = true;
   public boolean debugKeepRules = false;
@@ -348,6 +350,14 @@
     return minApiLevel >= AndroidApiLevel.O.getLevel();
   }
 
+  public boolean canUseConstantMethodHandle() {
+    return minApiLevel >= AndroidApiLevel.P.getLevel();
+  }
+
+  public boolean canUseConstantMethodType() {
+    return minApiLevel >= AndroidApiLevel.P.getLevel();
+  }
+
   public boolean canUseInvokeCustom() {
     return minApiLevel >= AndroidApiLevel.O.getLevel();
   }
diff --git a/src/main/java/com/android/tools/r8/utils/ProgramFileArchiveReader.java b/src/main/java/com/android/tools/r8/utils/ProgramFileArchiveReader.java
index fa6d5fb..f33bf77 100644
--- a/src/main/java/com/android/tools/r8/utils/ProgramFileArchiveReader.java
+++ b/src/main/java/com/android/tools/r8/utils/ProgramFileArchiveReader.java
@@ -55,7 +55,7 @@
                 dexResources.add(resource);
               }
             } else if (isClassFile(name)) {
-              String descriptor = ArchiveClassFileProvider.guessTypeDescriptor(name);
+              String descriptor = DescriptorUtils.guessTypeDescriptor(name);
               Resource resource = new OneShotByteResource(Resource.Kind.CLASSFILE,
                   ByteStreams.toByteArray(stream), Collections.singleton(descriptor));
               classResources.add(resource);
diff --git a/src/test/examplesAndroidP/invokecustom/InvokeCustom.java b/src/test/examplesAndroidP/invokecustom/InvokeCustom.java
new file mode 100644
index 0000000..e8447a3
--- /dev/null
+++ b/src/test/examplesAndroidP/invokecustom/InvokeCustom.java
@@ -0,0 +1,38 @@
+// 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 invokecustom;
+
+import java.lang.invoke.CallSite;
+import java.lang.invoke.ConstantCallSite;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+
+
+public class InvokeCustom {
+
+  private static String staticField1 = "StaticField1";
+
+  private static void targetMethodTest1() {
+    System.out.println("Hello World!");
+  }
+
+  private static void targetMethodTest2(MethodHandle mhInvokeStatic, MethodHandle mhGetStatic)
+      throws Throwable {
+    mhInvokeStatic.invokeExact();
+    System.out.println(mhGetStatic.invoke());
+  }
+
+  private static void targetMethodTest3(MethodType mt)
+      throws Throwable {
+    System.out.println("MethodType: " + mt.toString());
+  }
+
+  public static CallSite bsmLookupStatic(MethodHandles.Lookup caller, String name, MethodType type)
+      throws NoSuchMethodException, IllegalAccessException {
+    final MethodHandles.Lookup lookup = MethodHandles.lookup();
+    final MethodHandle targetMH = lookup.findStatic(lookup.lookupClass(), name, type);
+    return new ConstantCallSite(targetMH.asType(type));
+  }
+}
diff --git a/src/test/examplesAndroidP/invokecustom/TestGenerator.java b/src/test/examplesAndroidP/invokecustom/TestGenerator.java
new file mode 100644
index 0000000..375dc34
--- /dev/null
+++ b/src/test/examplesAndroidP/invokecustom/TestGenerator.java
@@ -0,0 +1,105 @@
+// 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 invokecustom;
+
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.lang.invoke.CallSite;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Handle;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+public class TestGenerator {
+
+  private final Path classNamePath;
+
+  public static void main(String[] args) throws IOException {
+    assert args.length == 1;
+    TestGenerator testGenerator = new TestGenerator(Paths.get(args[0],
+        TestGenerator.class.getPackage().getName(), InvokeCustom.class.getSimpleName() + ".class"));
+    testGenerator.generateTests();
+  }
+
+  public TestGenerator(Path classNamePath) {
+    this.classNamePath = classNamePath;
+  }
+
+  private void generateTests() throws IOException {
+    ClassReader cr = new ClassReader(new FileInputStream(classNamePath.toFile()));
+    ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
+    cr.accept(
+        new ClassVisitor(Opcodes.ASM6, cw) {
+          @Override
+          public void visitEnd() {
+            generateMethodTest1(cw);
+            generateMethodTest2(cw);
+            generateMethodMain(cw);
+            super.visitEnd();
+          }
+        }, 0);
+    new FileOutputStream(classNamePath.toFile()).write(cw.toByteArray());
+  }
+
+  /* Generate main method that only call all test methods. */
+  private void generateMethodMain(ClassVisitor cv) {
+    MethodVisitor mv = cv.visitMethod(
+            Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
+    mv.visitMethodInsn(
+        Opcodes.INVOKESTATIC, Type.getInternalName(InvokeCustom.class), "test1", "()V", false);
+    mv.visitMethodInsn(
+        Opcodes.INVOKESTATIC, Type.getInternalName(InvokeCustom.class), "test2", "()V", false);
+    mv.visitInsn(Opcodes.RETURN);
+    mv.visitMaxs(-1, -1);
+  }
+
+  /**
+   *  Generate test with an invokedynamic, a static bootstrap method without extra args and
+   *  args to the target method.
+   */
+  private void generateMethodTest1(ClassVisitor cv) {
+    MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test1", "()V",
+        null, null);
+    MethodType mt = MethodType.methodType(
+            CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class);
+    Handle bootstrap = new Handle( Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
+        "bsmLookupStatic", mt.toMethodDescriptorString(), false);
+    mv.visitLdcInsn(new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
+        "targetMethodTest1", "()V", false));
+    mv.visitLdcInsn(new Handle(Opcodes.H_GETSTATIC, Type.getInternalName(InvokeCustom.class),
+        "staticField1", "Ljava/lang/String;", false));
+    mv.visitInvokeDynamicInsn("targetMethodTest2",
+        "(Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;)V",
+        bootstrap);
+    mv.visitInsn(Opcodes.RETURN);
+    mv.visitMaxs(-1, -1);
+  }
+
+  /**
+   *  Generate test with an invokedynamic, a static bootstrap method without extra args and
+   *  args to the target method.
+   */
+  private void generateMethodTest2(ClassVisitor cv) {
+    MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test2", "()V",
+        null, null);
+    MethodType mt = MethodType.methodType(
+        CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class);
+    Handle bootstrap = new Handle( Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
+        "bsmLookupStatic", mt.toMethodDescriptorString(), false);
+    mv.visitLdcInsn(Type.getMethodType("(ZBSCIFJDLjava/lang/String;)Ljava/lang/Object;"));
+    mv.visitInvokeDynamicInsn("targetMethodTest3", "(Ljava/lang/invoke/MethodType;)V",
+        bootstrap);
+    mv.visitInsn(Opcodes.RETURN);
+    mv.visitMaxs(-1, -1);
+  }
+}
diff --git a/src/test/examplesAndroidP/invokecustom/keep-rules.txt b/src/test/examplesAndroidP/invokecustom/keep-rules.txt
new file mode 100644
index 0000000..dbc21fc
--- /dev/null
+++ b/src/test/examplesAndroidP/invokecustom/keep-rules.txt
@@ -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.
+
+# Keep the application entry point and the target methods of invoke-custom because these methods
+# can not be known at compile time. Get rid of everything that is not reachable from there.
+-keep public class invokecustom.InvokeCustom {
+  public static void main(...);
+}
+
+-keepclasseswithmembers class * {
+  *** targetMethodTest*(...);
+}
+
+# allow access modification to enable minification
+-allowaccessmodification
diff --git a/src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java
index 7de4f7f..45939db 100644
--- a/src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java
@@ -11,11 +11,9 @@
 import com.android.tools.r8.shaking.FilteredClassPath;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.ArchiveClassFileProvider;
 import com.android.tools.r8.utils.DirectoryClassFileProvider;
 import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.OffOrAuto;
-import com.android.tools.r8.utils.PreloadedClassFileProvider;
 import com.android.tools.r8.utils.ZipUtils;
 import java.io.IOException;
 import java.nio.file.Files;
diff --git a/src/test/java/com/android/tools/r8/D8RunExamplesAndroidPTest.java b/src/test/java/com/android/tools/r8/D8RunExamplesAndroidPTest.java
new file mode 100644
index 0000000..dde8a9c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/D8RunExamplesAndroidPTest.java
@@ -0,0 +1,86 @@
+// 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;
+
+import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
+import static com.android.tools.r8.utils.FileUtils.ZIP_EXTENSION;
+
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.errors.InternalCompilerError;
+import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.OffOrAuto;
+import com.android.tools.r8.utils.OutputMode;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.function.UnaryOperator;
+import org.hamcrest.core.CombinableMatcher;
+import org.hamcrest.core.IsInstanceOf;
+import org.hamcrest.core.StringContains;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.internal.matchers.ThrowableMessageMatcher;
+
+public class D8RunExamplesAndroidPTest extends RunExamplesAndroidPTest<D8Command.Builder> {
+
+  class D8TestRunner extends TestRunner<D8TestRunner> {
+
+    D8TestRunner(String testName, String packageName, String mainClass) {
+      super(testName, packageName, mainClass);
+    }
+
+    @Override
+    D8TestRunner withMinApiLevel(int minApiLevel) {
+      return withBuilderTransformation(builder -> builder.setMinApiLevel(minApiLevel));
+    }
+
+    D8TestRunner withClasspath(Path... classpath) {
+      return withBuilderTransformation(b -> {
+        try {
+          return b.addClasspathFiles(classpath);
+        } catch (IOException e) {
+          throw new AssertionError(e);
+        }
+      });
+    }
+
+
+    @Override
+    void build(Path inputFile, Path out) throws Throwable {
+      D8Command.Builder builder = D8Command.builder();
+      for (UnaryOperator<D8Command.Builder> transformation : builderTransformations) {
+        builder = transformation.apply(builder);
+      }
+      // TODO(mikaelpeltier) Add new android.jar build from aosp and use it
+      builder.addLibraryFiles(Paths.get(ToolHelper.getAndroidJar(AndroidApiLevel.O.getLevel())));
+      D8Command command = builder.addProgramFiles(inputFile).setOutputPath(out).build();
+      try {
+        ToolHelper.runD8(command, this::combinedOptionConsumer);
+      } catch (Unimplemented | CompilationError | InternalCompilerError re) {
+        throw re;
+      } catch (RuntimeException re) {
+        throw re.getCause() == null ? re : re.getCause();
+      }
+    }
+
+    D8TestRunner withIntermediate(boolean intermediate) {
+      return withBuilderTransformation(builder -> builder.setIntermediate(intermediate));
+    }
+
+    @Override
+    D8TestRunner self() {
+      return this;
+    }
+  }
+
+  @Override
+  D8TestRunner test(String testName, String packageName, String mainClass) {
+    return new D8TestRunner(testName, packageName, mainClass);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/R8CodeCanonicalizationTest.java b/src/test/java/com/android/tools/r8/R8CodeCanonicalizationTest.java
index 3513290..239e4fd 100644
--- a/src/test/java/com/android/tools/r8/R8CodeCanonicalizationTest.java
+++ b/src/test/java/com/android/tools/r8/R8CodeCanonicalizationTest.java
@@ -35,8 +35,7 @@
   public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
 
   @Test
-  public void testNumberOfCodeItemsUnchanged()
-      throws IOException, ExecutionException, ProguardRuleParserException, CompilationException {
+  public void testNumberOfCodeItemsUnchanged() throws Exception {
     int numberOfCodes = readNumberOfCodes(Paths.get(ToolHelper.EXAMPLES_BUILD_DIR + SOURCE_DEX));
     ToolHelper.runR8(ToolHelper.EXAMPLES_BUILD_DIR + SOURCE_DEX, temp.getRoot().getCanonicalPath());
     int newNumberOfCodes = readNumberOfCodes(
diff --git a/src/test/java/com/android/tools/r8/R8EntryPointTests.java b/src/test/java/com/android/tools/r8/R8EntryPointTests.java
index c83cea6..a03ae3d 100644
--- a/src/test/java/com/android/tools/r8/R8EntryPointTests.java
+++ b/src/test/java/com/android/tools/r8/R8EntryPointTests.java
@@ -5,7 +5,6 @@
 package com.android.tools.r8;
 
 import com.android.tools.r8.ToolHelper.ProcessResult;
-import com.android.tools.r8.shaking.ProguardRuleParserException;
 import com.android.tools.r8.utils.FileUtils;
 import com.google.common.collect.ImmutableList;
 import java.io.IOException;
@@ -38,7 +37,7 @@
   }
 
   @Test
-  public void testRun1Dir() throws IOException, CompilationException, ProguardRuleParserException {
+  public void testRun1Dir() throws Exception {
     Path out = temp.newFolder("outdex").toPath();
     R8.run(getCommand(out));
     Assert.assertTrue(Files.isRegularFile(out.resolve(FileUtils.DEFAULT_DEX_FILENAME)));
@@ -47,7 +46,7 @@
   }
 
   @Test
-  public void testRun1Zip() throws IOException, CompilationException, ProguardRuleParserException {
+  public void testRun1Zip() throws Exception {
     Path out = temp.newFolder("outdex").toPath().resolve("dex.zip");
     R8.run(getCommand(out));
     Assert.assertTrue(Files.isRegularFile(out));
@@ -56,7 +55,7 @@
   }
 
   @Test
-  public void testRun2Dir() throws IOException, CompilationException, ProguardRuleParserException {
+  public void testRun2Dir() throws Exception {
     Path out = temp.newFolder("outdex").toPath();
     ExecutorService executor = Executors.newWorkStealingPool(2);
     try {
@@ -70,7 +69,7 @@
   }
 
   @Test
-  public void testRun2Zip() throws IOException, CompilationException, ProguardRuleParserException {
+  public void testRun2Zip() throws Exception {
     Path out = temp.newFolder("outdex").toPath().resolve("dex.zip");
     ExecutorService executor = Executors.newWorkStealingPool(2);
     try {
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
index 1444b2b..1fb3175 100644
--- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -110,7 +110,7 @@
           .put("957-methodhandle-transforms", AndroidApiLevel.O.getLevel())
           .put("958-methodhandle-stackframe", AndroidApiLevel.O.getLevel())
           .put("959-invoke-polymorphic-accessors", AndroidApiLevel.O.getLevel())
-          .put("979-const-method-handle", AndroidApiLevel.O.getLevel())
+          .put("979-const-method-handle", AndroidApiLevel.P.getLevel())
           .put("990-method-handle-and-mr", AndroidApiLevel.O.getLevel())
           // Test intentionally asserts presence of bridge default methods desugar removes.
           .put("044-proxy", AndroidApiLevel.N.getLevel())
@@ -680,6 +680,11 @@
           TestCondition.runtimes(
               DexVm.Version.V4_4_4, DexVm.Version.V5_1_1, DexVm.Version.V6_0_1,
               DexVm.Version.V7_0_0));
+  private static final TestCondition beforeAndroidP =
+      TestCondition.match(
+          TestCondition.runtimes(
+              DexVm.Version.V4_4_4, DexVm.Version.V5_1_1, DexVm.Version.V6_0_1,
+              DexVm.Version.V7_0_0));
 
   // TODO(ager): Could we test that these fail in the way that we expect?
   private static final Multimap<String, TestCondition> expectedToFailRunWithArt =
@@ -803,6 +808,7 @@
               TestCondition.match(TestCondition.tools(DexTool.JACK, DexTool.DX),
                   TestCondition
                       .runtimes(DexVm.Version.V4_4_4, DexVm.Version.V5_1_1, DexVm.Version.V6_0_1)))
+          .put("979-const-method-handle", beforeAndroidP)
           .build();
 
   // Tests where code generation fails.
@@ -848,6 +854,51 @@
           .put("972-iface-super-multidex", TestCondition.match(TestCondition.R8_COMPILER))
           .build();
 
+  // Tests that does not have dex input for some toolchains.
+  private static Multimap<String, TestCondition> noInputDex =
+      new ImmutableListMultimap.Builder<String, TestCondition>()
+          .put("914-hello-obsolescence", TestCondition.match(
+              TestCondition.tools(DexTool.NONE, DexTool.DX),
+              LEGACY_RUNTIME))
+          .put("915-obsolete-2", TestCondition.match(
+              TestCondition.tools(DexTool.NONE, DexTool.DX),
+              LEGACY_RUNTIME))
+          .put("916-obsolete-jit", TestCondition.match(
+              TestCondition.tools(DexTool.NONE, DexTool.DX),
+              LEGACY_RUNTIME))
+          .put("919-obsolete-fields", TestCondition.match(
+              TestCondition.tools(DexTool.NONE, DexTool.DX),
+              LEGACY_RUNTIME))
+          .put("926-multi-obsolescence", TestCondition.match(
+              TestCondition.tools(DexTool.NONE, DexTool.DX),
+              LEGACY_RUNTIME))
+          .put("941-recurive-obsolete-jit", TestCondition.match(
+              TestCondition.tools(DexTool.NONE, DexTool.DX),
+              LEGACY_RUNTIME))
+          .put("940-recursive-obsolete", TestCondition.match(
+              TestCondition.tools(DexTool.NONE, DexTool.DX),
+              LEGACY_RUNTIME))
+          .put("942-private-recursive", TestCondition.match(
+              TestCondition.tools(DexTool.NONE, DexTool.DX),
+              LEGACY_RUNTIME))
+          .put("943-private-recursive-jit", TestCondition.match(
+              TestCondition.tools(DexTool.NONE, DexTool.DX),
+              LEGACY_RUNTIME))
+          .put("945-obsolete-native", TestCondition.match(
+              TestCondition.tools(DexTool.NONE, DexTool.DX),
+              LEGACY_RUNTIME))
+          .put("948-change-annotations", TestCondition.match(
+              TestCondition.tools(DexTool.NONE, DexTool.DX),
+              LEGACY_RUNTIME))
+          .put("952-invoke-custom", TestCondition.match(
+              TestCondition.tools(DexTool.NONE, DexTool.DX),
+              LEGACY_RUNTIME))
+          .put("952-invoke-custom-kinds",
+              TestCondition.match(TestCondition.tools(DexTool.DX)))
+          .put("979-const-method-handle",
+              TestCondition.match(TestCondition.tools(DexTool.DX)))
+          .build();
+
   // Tests that does not have valid input for us to be compatible with jack/dx running.
   private static List<String> noInputJar = ImmutableList.of(
       "097-duplicate-method", // No input class files.
@@ -895,14 +946,8 @@
           // the output when run on Art is not as expected. b/65233869
           .put("162-method-resolution",
               TestCondition.match(
-                  TestCondition.tools(DexTool.DX, DexTool.JACK), TestCondition.R8_COMPILER))
-          // Old runtimes used the legacy test directory which does not contain input for tools
-          // NONE and DX.
-          .put("952-invoke-custom", TestCondition.match(
-              TestCondition.tools(DexTool.NONE, DexTool.DX),
-              LEGACY_RUNTIME))
-          // No input dex files for DX
-          .put("952-invoke-custom-kinds", TestCondition.match(TestCondition.tools(DexTool.DX)))
+                  TestCondition.tools(DexTool.DX, DexTool.JACK, DexTool.NONE),
+                  TestCondition.R8_COMPILER))
           .build();
 
   public static List<String> requireInliningToBeDisabled = ImmutableList.of(
@@ -931,9 +976,6 @@
   );
 
   private static List<String> failuresToTriage = ImmutableList.of(
-      // const-method-handle and const-method-type
-      "979-const-method-handle",
-
       // Dex file input into a jar file, not yet supported by the test framework.
       "663-odd-dex-size",
       "663-odd-dex-size2",
@@ -1103,13 +1145,13 @@
     // Collect tests where running Art is skipped (we still run R8/D8 on these).
     Set<String> skipArt = new HashSet<>(customRun);
 
-    Set<String> skipTest = Sets.newHashSet(skipAltogether);
-    skipTest.addAll(usesNativeAgentCode);
-    skipTest.addAll(failuresToTriage);
-
     // Collect the tests requiring the native library.
     Set<String> useNativeLibrary = Sets.newHashSet(useJNI);
     for (DexTool dexTool : DexTool.values()) {
+      Set<String> skipTest = Sets.newHashSet(skipAltogether);
+      skipTest.addAll(usesNativeAgentCode);
+      skipTest.addAll(failuresToTriage);
+
       File artTestDir =
           dexTool == DexTool.JACK || LEGACY_RUNTIME.set.contains(version) ? legacyArtTestDir
               : defaultArtTestDir;
@@ -1127,6 +1169,10 @@
         skipTest.addAll(noInputJar);
       }
 
+      // Collect the test that we should skip in this configuration because there is no dex input
+      skipTest.addAll(collectTestsMatchingConditions(
+          dexTool, compilerUnderTest, version, compilationMode, noInputDex));
+
       // Collect the test that we should skip in this configuration
       skipTest.addAll(collectTestsMatchingConditions(
           dexTool, compilerUnderTest, version, compilationMode, testToSkip));
@@ -1618,6 +1664,7 @@
           specification.directory.listFiles((File file) ->
               file.getName().endsWith(".dex") && !file.getName().startsWith("jasmin"));
     }
+
     List<String> fileNames = new ArrayList<>();
     for (File file : inputFiles) {
       fileNames.add(file.getCanonicalPath());
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidNTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidNTest.java
index 13c38ca..ed02330 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidNTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidNTest.java
@@ -5,7 +5,6 @@
 package com.android.tools.r8;
 
 import java.nio.file.Path;
-import java.util.concurrent.ExecutionException;
 import java.util.function.UnaryOperator;
 
 public class R8RunExamplesAndroidNTest extends RunExamplesAndroidNTest<R8Command.Builder> {
@@ -23,16 +22,12 @@
 
     @Override
     void build(Path inputFile, Path out) throws Throwable {
-      try {
-        R8Command.Builder builder = R8Command.builder();
-        for (UnaryOperator<R8Command.Builder> transformation : builderTransformations) {
-          builder = transformation.apply(builder);
-        }
-        R8Command command = builder.addProgramFiles(inputFile).setOutputPath(out).build();
-        ToolHelper.runR8(command, this::combinedOptionConsumer);
-      } catch (ExecutionException e) {
-        throw e.getCause();
+      R8Command.Builder builder = R8Command.builder();
+      for (UnaryOperator<R8Command.Builder> transformation : builderTransformations) {
+        builder = transformation.apply(builder);
       }
+      R8Command command = builder.addProgramFiles(inputFile).setOutputPath(out).build();
+      ToolHelper.runR8(command, this::combinedOptionConsumer);
     }
   }
 
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
index ebf4749..a7354e2 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
@@ -6,7 +6,6 @@
 
 import com.android.tools.r8.ToolHelper.DexVm;
 import com.android.tools.r8.ToolHelper.DexVm.Version;
-import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
@@ -14,7 +13,6 @@
 import java.nio.file.Paths;
 import java.util.List;
 import java.util.Map;
-import java.util.concurrent.ExecutionException;
 import java.util.function.UnaryOperator;
 import org.junit.Test;
 
@@ -61,18 +59,14 @@
 
     @Override
     void build(Path inputFile, Path out) throws Throwable {
-      try {
-        R8Command.Builder builder = R8Command.builder();
-        for (UnaryOperator<R8Command.Builder> transformation : builderTransformations) {
-          builder = transformation.apply(builder);
-        }
-        builder.addLibraryFiles(Paths.get(ToolHelper.getAndroidJar(
-            androidJarVersion == null ? builder.getMinApiLevel() : androidJarVersion)));
-        R8Command command = builder.addProgramFiles(inputFile).setOutputPath(out).build();
-        ToolHelper.runR8(command, this::combinedOptionConsumer);
-      } catch (ExecutionException e) {
-        throw e.getCause();
+      R8Command.Builder builder = R8Command.builder();
+      for (UnaryOperator<R8Command.Builder> transformation : builderTransformations) {
+        builder = transformation.apply(builder);
       }
+      builder.addLibraryFiles(Paths.get(ToolHelper.getAndroidJar(
+          androidJarVersion == null ? builder.getMinApiLevel() : androidJarVersion)));
+      R8Command command = builder.addProgramFiles(inputFile).setOutputPath(out).build();
+      ToolHelper.runR8(command, this::combinedOptionConsumer);
     }
 
     @Override
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidPTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidPTest.java
new file mode 100644
index 0000000..eca0be8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidPTest.java
@@ -0,0 +1,86 @@
+// 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;
+
+import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.Map;
+import java.util.function.UnaryOperator;
+import org.junit.Test;
+
+public class R8RunExamplesAndroidPTest extends RunExamplesAndroidPTest<R8Command.Builder> {
+
+  private static Map<DexVm.Version, List<String>> alsoFailsOn =
+      ImmutableMap.of(
+          DexVm.Version.V4_4_4, ImmutableList.of(
+              "invokecustom-with-shrinking"
+          ),
+          DexVm.Version.V5_1_1, ImmutableList.of(
+              "invokecustom-with-shrinking"
+          ),
+          DexVm.Version.V6_0_1, ImmutableList.of(
+              "invokecustom-with-shrinking"
+          ),
+          DexVm.Version.V7_0_0, ImmutableList.of(
+              "invokecustom-with-shrinking"
+          ),
+          DexVm.Version.DEFAULT, ImmutableList.of(
+          )
+      );
+
+  @Test
+  public void invokeCustomWithShrinking() throws Throwable {
+    test("invokecustom-with-shrinking", "invokecustom", "InvokeCustom")
+        .withMinApiLevel(AndroidApiLevel.P.getLevel())
+        .withBuilderTransformation(builder ->
+            builder.addProguardConfigurationFiles(
+                Paths.get(ToolHelper.EXAMPLES_ANDROID_P_DIR, "invokecustom/keep-rules.txt")))
+        .run();
+  }
+
+  class R8TestRunner extends TestRunner<R8TestRunner> {
+
+    R8TestRunner(String testName, String packageName, String mainClass) {
+      super(testName, packageName, mainClass);
+    }
+
+    @Override
+    R8TestRunner withMinApiLevel(int minApiLevel) {
+      return withBuilderTransformation(builder -> builder.setMinApiLevel(minApiLevel));
+    }
+
+    @Override
+    void build(Path inputFile, Path out) throws Throwable {
+      R8Command.Builder builder = R8Command.builder();
+      for (UnaryOperator<R8Command.Builder> transformation : builderTransformations) {
+        builder = transformation.apply(builder);
+      }
+      // TODO(mikaelpeltier) Add new android.jar build from aosp and use it
+      builder.addLibraryFiles(Paths.get(ToolHelper.getAndroidJar(AndroidApiLevel.O.getLevel())));
+      R8Command command = builder.addProgramFiles(inputFile).setOutputPath(out).build();
+      ToolHelper.runR8(command, this::combinedOptionConsumer);
+    }
+
+    @Override
+    R8TestRunner self() {
+      return this;
+    }
+  }
+
+  @Override
+  R8TestRunner test(String testName, String packageName, String mainClass) {
+    return new R8TestRunner(testName, packageName, mainClass);
+  }
+
+  @Override
+  boolean expectedToFail(String name) {
+    return super.expectedToFail(name) || failsOn(alsoFailsOn, name);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/R8RunSmaliTestsTest.java b/src/test/java/com/android/tools/r8/R8RunSmaliTestsTest.java
index 1a8f68a..3258b87 100644
--- a/src/test/java/com/android/tools/r8/R8RunSmaliTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunSmaliTestsTest.java
@@ -138,8 +138,7 @@
   }
 
   @Test
-  public void SmaliTest()
-      throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
+  public void SmaliTest() throws Exception {
     File originalDexFile = Paths.get(SMALI_DIR, directoryName, dexFileName).toFile();
     String outputPath = temp.getRoot().getCanonicalPath();
     ToolHelper.runR8(originalDexFile.getCanonicalPath(), outputPath);
diff --git a/src/test/java/com/android/tools/r8/RunExamplesAndroidPTest.java b/src/test/java/com/android/tools/r8/RunExamplesAndroidPTest.java
new file mode 100644
index 0000000..06916a8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/RunExamplesAndroidPTest.java
@@ -0,0 +1,273 @@
+// 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;
+
+import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
+import static com.android.tools.r8.utils.FileUtils.ZIP_EXTENSION;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.DexInspector.FoundClassSubject;
+import com.android.tools.r8.utils.DexInspector.FoundMethodSubject;
+import com.android.tools.r8.utils.DexInspector.InstructionSubject;
+import com.android.tools.r8.utils.DexInspector.InvokeInstructionSubject;
+import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.OffOrAuto;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.io.ByteStreams;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+import java.util.function.UnaryOperator;
+import java.util.stream.Collectors;
+import java.util.zip.ZipException;
+import java.util.zip.ZipFile;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+
+public abstract class RunExamplesAndroidPTest
+      <B extends BaseCommand.Builder<? extends BaseCommand, B>> {
+  static final String EXAMPLE_DIR = ToolHelper.EXAMPLES_ANDROID_P_BUILD_DIR;
+
+  abstract class TestRunner<C extends TestRunner<C>> {
+    final String testName;
+    final String packageName;
+    final String mainClass;
+
+    Integer androidJarVersion = null;
+
+    final List<Consumer<InternalOptions>> optionConsumers = new ArrayList<>();
+    final List<Consumer<DexInspector>> dexInspectorChecks = new ArrayList<>();
+    final List<UnaryOperator<B>> builderTransformations = new ArrayList<>();
+
+    TestRunner(String testName, String packageName, String mainClass) {
+      this.testName = testName;
+      this.packageName = packageName;
+      this.mainClass = mainClass;
+    }
+
+    abstract C self();
+
+    C withDexCheck(Consumer<DexInspector> check) {
+      dexInspectorChecks.add(check);
+      return self();
+    }
+
+    C withClassCheck(Consumer<FoundClassSubject> check) {
+      return withDexCheck(inspector -> inspector.forAllClasses(check));
+    }
+
+    C withMethodCheck(Consumer<FoundMethodSubject> check) {
+      return withClassCheck(clazz -> clazz.forAllMethods(check));
+    }
+
+    <T extends InstructionSubject> C withInstructionCheck(
+        Predicate<InstructionSubject> filter, Consumer<T> check) {
+      return withMethodCheck(method -> {
+        if (method.isAbstract()) {
+          return;
+        }
+        Iterator<T> iterator = method.iterateInstructions(filter);
+        while (iterator.hasNext()) {
+          check.accept(iterator.next());
+        }
+      });
+    }
+
+    C withOptionConsumer(Consumer<InternalOptions> consumer) {
+      optionConsumers.add(consumer);
+      return self();
+    }
+
+    C withMainDexClass(String... classes) {
+      return withBuilderTransformation(builder -> builder.addMainDexClasses(classes));
+    }
+
+    C withInterfaceMethodDesugaring(OffOrAuto behavior) {
+      return withOptionConsumer(o -> o.interfaceMethodDesugaring = behavior);
+    }
+
+    C withTryWithResourcesDesugaring(OffOrAuto behavior) {
+      return withOptionConsumer(o -> o.tryWithResourcesDesugaring = behavior);
+    }
+
+    C withBuilderTransformation(UnaryOperator<B> builderTransformation) {
+      builderTransformations.add(builderTransformation);
+      return self();
+    }
+
+    void combinedOptionConsumer(InternalOptions options) {
+      for (Consumer<InternalOptions> consumer : optionConsumers) {
+        consumer.accept(options);
+      }
+    }
+
+    Path build() throws Throwable {
+      Path inputFile = getInputJar();
+      Path out = temp.getRoot().toPath().resolve(testName + ZIP_EXTENSION);
+
+      build(inputFile, out);
+      return out;
+    }
+
+    Path getInputJar() {
+      return Paths.get(EXAMPLE_DIR, packageName + JAR_EXTENSION);
+    }
+
+    void run() throws Throwable {
+      if (minSdkErrorExpected(testName)) {
+        thrown.expect(ApiLevelException.class);
+      }
+
+      String qualifiedMainClass = packageName + "." + mainClass;
+      Path inputFile = getInputJar();
+      Path out = temp.getRoot().toPath().resolve(testName + ZIP_EXTENSION);
+
+      build(inputFile, out);
+
+      if (!ToolHelper.artSupported()) {
+        return;
+      }
+
+      if (!dexInspectorChecks.isEmpty()) {
+        DexInspector inspector = new DexInspector(out);
+        for (Consumer<DexInspector> check : dexInspectorChecks) {
+          check.accept(inspector);
+        }
+      }
+
+      execute(testName, qualifiedMainClass, new Path[]{inputFile}, new Path[]{out});
+    }
+
+    abstract C withMinApiLevel(int minApiLevel);
+
+    C withAndroidJar(int androidJarVersion) {
+      assert this.androidJarVersion == null;
+      this.androidJarVersion = androidJarVersion;
+      return self();
+    }
+
+    abstract void build(Path inputFile, Path out) throws Throwable;
+  }
+
+  private static List<String> minSdkErrorExpected =
+      ImmutableList.of(
+          "invokepolymorphic-error-due-to-min-sdk", "invokecustom-error-due-to-min-sdk");
+
+  private static Map<DexVm.Version, List<String>> failsOn =
+      ImmutableMap.of(
+          DexVm.Version.V4_4_4, ImmutableList.of(
+              // Dex version not supported
+              "invokecustom"
+          ),
+          DexVm.Version.V5_1_1, ImmutableList.of(
+              // Dex version not supported
+              "invokecustom"
+          ),
+          DexVm.Version.V6_0_1, ImmutableList.of(
+              // Dex version not supported
+              "invokecustom"
+          ),
+          DexVm.Version.V7_0_0, ImmutableList.of(
+              // Dex version not supported
+              "invokecustom"
+          ),
+          DexVm.Version.DEFAULT, ImmutableList.of()
+      );
+
+  @Rule
+  public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+
+  @Rule
+  public ExpectedException thrown = ExpectedException.none();
+
+  boolean failsOn(Map<DexVm.Version, List<String>> failsOn, String name) {
+    DexVm.Version vmVersion = ToolHelper.getDexVm().getVersion();
+    return failsOn.containsKey(vmVersion)
+        && failsOn.get(vmVersion).contains(name);
+  }
+
+  boolean expectedToFail(String name) {
+    return failsOn(failsOn, name);
+  }
+
+  boolean minSdkErrorExpected(String testName) {
+    return minSdkErrorExpected.contains(testName);
+  }
+
+  @Test
+  public void invokeCustom() throws Throwable {
+    test("invokecustom", "invokecustom", "InvokeCustom")
+        .withMinApiLevel(AndroidApiLevel.P.getLevel())
+        .run();
+  }
+
+  @Test
+  public void invokeCustomErrorDueToMinSdk() throws Throwable {
+    test("invokecustom-error-due-to-min-sdk", "invokecustom", "InvokeCustom")
+        .withMinApiLevel(AndroidApiLevel.O.getLevel())
+        .run();
+  }
+
+  abstract RunExamplesAndroidPTest<B>.TestRunner<?> test(String testName, String packageName,
+      String mainClass);
+
+  void execute(
+      String testName,
+      String qualifiedMainClass, Path[] jars, Path[] dexes)
+      throws IOException {
+
+    boolean expectedToFail = expectedToFail(testName);
+    if (expectedToFail) {
+      thrown.expect(Throwable.class);
+    }
+    String output = ToolHelper.runArtNoVerificationErrors(
+        Arrays.stream(dexes).map(path -> path.toString()).collect(Collectors.toList()),
+        qualifiedMainClass,
+        null);
+    if (!expectedToFail) {
+      ToolHelper.ProcessResult javaResult =
+          ToolHelper.runJava(
+              Arrays.stream(jars).map(path -> path.toString()).collect(Collectors.toList()),
+              qualifiedMainClass);
+      assertEquals("JVM run failed", javaResult.exitCode, 0);
+      assertTrue(
+          "JVM output does not match art output.\n\tjvm: "
+              + javaResult.stdout
+              + "\n\tart: "
+              + output.replace("\r", ""),
+          output.equals(javaResult.stdout.replace("\r", "")));
+    }
+  }
+
+  protected DexInspector getMainDexInspector(Path zip)
+      throws ZipException, IOException, ExecutionException {
+    try (ZipFile zipFile = new ZipFile(zip.toFile())) {
+      try (InputStream in =
+          zipFile.getInputStream(zipFile.getEntry(FileUtils.DEFAULT_DEX_FILENAME))) {
+        return new DexInspector(AndroidApp.fromDexProgramData(ByteStreams.toByteArray(in)));
+      }
+    }
+  }
+
+}
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index e46ff48..dde13ae 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -18,6 +18,7 @@
 import com.android.tools.r8.utils.OutputMode;
 import com.android.tools.r8.utils.Timing;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
 import com.google.common.io.ByteStreams;
 import java.io.File;
 import java.io.FileInputStream;
@@ -92,6 +93,20 @@
   }
 
   /**
+   * Get the class name generated by javac.
+   */
+  protected String getJavacGeneratedClassName(Class clazz) {
+    List<String> parts = Lists.newArrayList(clazz.getCanonicalName().split("\\."));
+    Class enclosing = clazz;
+    while (enclosing.getEnclosingClass() != null) {
+      parts.set(parts.size() - 2, parts.get(parts.size() - 2) + "$" + parts.get(parts.size() - 1));
+      parts.remove(parts.size() - 1);
+      enclosing = clazz.getEnclosingClass();
+    }
+    return String.join(".", parts);
+  }
+
+  /**
    * Compile an application with D8.
    */
   protected AndroidApp compileWithD8(AndroidApp app)
@@ -101,6 +116,15 @@
   }
 
   /**
+   * Compile an application with D8.
+   */
+  protected AndroidApp compileWithD8(AndroidApp app, Consumer<InternalOptions> optionsConsumer)
+      throws CompilationException, ExecutionException, IOException {
+    D8Command command = ToolHelper.prepareD8CommandBuilder(app).build();
+    return ToolHelper.runD8(command, optionsConsumer);
+  }
+
+  /**
    * Compile an application with R8.
    */
   protected AndroidApp compileWithR8(Class... classes)
@@ -249,11 +273,24 @@
   /**
    * Run application on Art with the specified main class.
    */
-  protected String runOnArt(AndroidApp app, Class mainClass) throws IOException {
+  protected ProcessResult runOnArtRaw(AndroidApp app, String mainClass) throws IOException {
     Path out = File.createTempFile("junit", ".zip", temp.getRoot()).toPath();
     app.writeToZip(out, OutputMode.Indexed);
-    return ToolHelper.runArtNoVerificationErrors(
-        ImmutableList.of(out.toString()), mainClass.getCanonicalName(), null);
+    return ToolHelper.runArtRaw(ImmutableList.of(out.toString()), mainClass, null);
+  }
+
+  /**
+   * Run application on Art with the specified main class.
+   */
+  protected ProcessResult runOnArtRaw(AndroidApp app, Class mainClass) throws IOException {
+    return runOnArtRaw(app, mainClass.getCanonicalName());
+  }
+
+  /**
+   * Run application on Art with the specified main class.
+   */
+  protected String runOnArt(AndroidApp app, Class mainClass) throws IOException {
+    return runOnArtRaw(app, mainClass).stdout;
   }
 
   /**
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index a5d0322..fdd2fa6 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -10,7 +10,6 @@
 import com.android.tools.r8.DeviceRunner.DeviceRunnerConfigurationException;
 import com.android.tools.r8.ToolHelper.DexVm.Kind;
 import com.android.tools.r8.dex.ApplicationReader;
-import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.graph.AppInfoWithSubtyping;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexItemFactory;
@@ -20,6 +19,7 @@
 import com.android.tools.r8.shaking.ProguardRuleParserException;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DefaultDiagnosticsHandler;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.StringUtils;
@@ -59,9 +59,11 @@
   public static final String BUILD_DIR = "build/";
   public static final String EXAMPLES_DIR = "src/test/examples/";
   public static final String EXAMPLES_ANDROID_O_DIR = "src/test/examplesAndroidO/";
+  public static final String EXAMPLES_ANDROID_P_DIR = "src/test/examplesAndroidP/";
   public static final String EXAMPLES_BUILD_DIR = BUILD_DIR + "test/examples/";
   public static final String EXAMPLES_ANDROID_N_BUILD_DIR = BUILD_DIR + "test/examplesAndroidN/";
   public static final String EXAMPLES_ANDROID_O_BUILD_DIR = BUILD_DIR + "test/examplesAndroidO/";
+  public static final String EXAMPLES_ANDROID_P_BUILD_DIR = BUILD_DIR + "test/examplesAndroidP/";
   public static final String SMALI_BUILD_DIR = BUILD_DIR + "test/smali/";
 
   public static final String LINE_SEPARATOR = StringUtils.LINE_SEPARATOR;
@@ -426,6 +428,10 @@
   }
 
   public static String getAndroidJar(int minSdkVersion) {
+    if (minSdkVersion == AndroidApiLevel.P.getLevel()) {
+      // TODO(mikaelpeltier) Android P does not yet have his android.jar use the O version
+      minSdkVersion = AndroidApiLevel.O.getLevel();
+    }
     return String.format(
         ANDROID_JAR_PATTERN,
         minSdkVersion == AndroidApiLevel.getDefault().getLevel() ? DEFAULT_MIN_SDK : minSdkVersion);
@@ -590,7 +596,8 @@
     if (configPaths.isEmpty()) {
       return ProguardConfiguration.defaultConfiguration(factory);
     }
-    ProguardConfigurationParser parser = new ProguardConfigurationParser(factory);
+    ProguardConfigurationParser parser =
+        new ProguardConfigurationParser(factory, new DefaultDiagnosticsHandler());
     for (Path configPath : configPaths) {
       parser.parse(configPath);
     }
@@ -605,35 +612,33 @@
     return R8Command.builder(app);
   }
 
-  public static AndroidApp runR8(AndroidApp app)
-      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+  public static AndroidApp runR8(AndroidApp app) throws IOException, CompilationException {
     return runR8(R8Command.builder(app).build());
   }
 
   public static AndroidApp runR8(AndroidApp app, Path output)
-      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+      throws IOException, CompilationException {
     assert output != null;
     return runR8(R8Command.builder(app).setOutputPath(output).build());
   }
 
   public static AndroidApp runR8(AndroidApp app, Consumer<InternalOptions> optionsConsumer)
-      throws ProguardRuleParserException, ExecutionException, IOException, CompilationException {
+      throws IOException, CompilationException {
     return runR8(R8Command.builder(app).build(), optionsConsumer);
   }
 
-  public static AndroidApp runR8(R8Command command)
-      throws ProguardRuleParserException, ExecutionException, IOException, CompilationException {
+  public static AndroidApp runR8(R8Command command) throws IOException, CompilationException {
     return runR8(command, null);
   }
 
   public static AndroidApp runR8(R8Command command, Consumer<InternalOptions> optionsConsumer)
-      throws ProguardRuleParserException, ExecutionException, IOException, CompilationException {
+      throws IOException, CompilationException {
     return runR8WithFullResult(command, optionsConsumer).androidApp;
   }
 
   public static CompilationResult runR8WithFullResult(
       R8Command command, Consumer<InternalOptions> optionsConsumer)
-      throws ProguardRuleParserException, ExecutionException, IOException, CompilationException {
+      throws IOException, CompilationException {
     // TODO(zerny): Should we really be adding the android library in ToolHelper?
     AndroidApp app = command.getInputApp();
     if (app.getLibraryResourceProviders().isEmpty()) {
@@ -655,12 +660,12 @@
   }
 
   public static AndroidApp runR8(String fileName, String out)
-      throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
+      throws IOException, CompilationException {
     return runR8(Collections.singletonList(fileName), out);
   }
 
   public static AndroidApp runR8(Collection<String> fileNames, String out)
-      throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
+      throws IOException, CompilationException {
     return R8.run(
         R8Command.builder()
             .addProgramFiles(ListUtils.map(fileNames, Paths::get))
@@ -780,9 +785,54 @@
     return Paths.get(System.getProperty("java.home"), "bin", "java").toString();
   }
 
+  public static ProcessResult runArtRaw(String file, String mainClass)
+      throws IOException {
+    return runArtRaw(Collections.singletonList(file), mainClass, null);
+  }
+
+  public static ProcessResult runArtRaw(List<String> files, String mainClass,
+      Consumer<ArtCommandBuilder> extras)
+      throws IOException {
+    return runArtRaw(files, mainClass, extras, null);
+  }
+
+  public static ProcessResult runArtRaw(List<String> files, String mainClass,
+      Consumer<ArtCommandBuilder> extras, DexVm version)
+      throws IOException {
+    ArtCommandBuilder builder =
+        version != null ? new ArtCommandBuilder(version) : new ArtCommandBuilder();
+    files.forEach(builder::appendClasspath);
+    builder.setMainClass(mainClass);
+    if (extras != null) {
+      extras.accept(builder);
+    }
+    return runArtProcessRaw(builder);
+  }
+
+  public static ProcessResult runArtNoVerificationErrorsRaw(String file, String mainClass)
+      throws IOException {
+    return runArtNoVerificationErrorsRaw(Collections.singletonList(file), mainClass, null);
+  }
+
+  public static ProcessResult runArtNoVerificationErrorsRaw(List<String> files, String mainClass,
+      Consumer<ArtCommandBuilder> extras)
+      throws IOException {
+    return runArtNoVerificationErrorsRaw(files, mainClass, extras, null);
+  }
+
+  public static ProcessResult runArtNoVerificationErrorsRaw(List<String> files, String mainClass,
+      Consumer<ArtCommandBuilder> extras,
+      DexVm version)
+      throws IOException {
+    ProcessResult result = runArtRaw(files, mainClass, extras, version);
+    failOnProcessFailure(result);
+    failOnVerificationErrors(result);
+    return result;
+  }
+
   public static String runArtNoVerificationErrors(String file, String mainClass)
       throws IOException {
-    return runArtNoVerificationErrors(Collections.singletonList(file), mainClass, null);
+    return runArtNoVerificationErrorsRaw(file, mainClass).stdout;
   }
 
   public static String runArtNoVerificationErrors(List<String> files, String mainClass,
@@ -795,25 +845,22 @@
       Consumer<ArtCommandBuilder> extras,
       DexVm version)
       throws IOException {
-    ArtCommandBuilder builder =
-        version != null ? new ArtCommandBuilder(version) : new ArtCommandBuilder();
-    files.forEach(builder::appendClasspath);
-    builder.setMainClass(mainClass);
-    if (extras != null) {
-      extras.accept(builder);
-    }
-    return runArtNoVerificationErrors(builder);
+    return runArtNoVerificationErrorsRaw(files, mainClass, extras, version).stdout;
   }
 
-  public static String runArtNoVerificationErrors(ArtCommandBuilder builder) throws IOException {
-    ProcessResult result = runArtProcess(builder);
+  private static void failOnProcessFailure(ProcessResult result) {
+    if (result.exitCode != 0) {
+      fail("Unexpected art failure: '" + result.stderr + "'\n" + result.stdout);
+    }
+  }
+
+  private static void failOnVerificationErrors(ProcessResult result) {
     if (result.stderr.contains("Verification error")) {
       fail("Verification error: \n" + result.stderr);
     }
-    return result.stdout;
   }
 
-  private static ProcessResult runArtProcess(ArtCommandBuilder builder) throws IOException {
+  private static ProcessResult runArtProcessRaw(ArtCommandBuilder builder) throws IOException {
     Assume.assumeTrue(ToolHelper.artSupported());
     ProcessResult result;
     if (builder.isForDevice()) {
@@ -825,14 +872,12 @@
     } else {
       result = runProcess(builder.asProcessBuilder());
     }
-    if (result.exitCode != 0) {
-      fail("Unexpected art failure: '" + result.stderr + "'\n" + result.stdout);
-    }
     return result;
   }
 
   public static String runArt(ArtCommandBuilder builder) throws IOException {
-    ProcessResult result = runArtProcess(builder);
+    ProcessResult result = runArtProcessRaw(builder);
+    failOnProcessFailure(result);
     return result.stdout;
   }
 
diff --git a/src/test/java/com/android/tools/r8/internal/D8Framework14082017DesugaredVerificationTest.java b/src/test/java/com/android/tools/r8/internal/D8Framework14082017DesugaredVerificationTest.java
index 4cc5818..026918e 100644
--- a/src/test/java/com/android/tools/r8/internal/D8Framework14082017DesugaredVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/D8Framework14082017DesugaredVerificationTest.java
@@ -3,13 +3,9 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.internal;
 
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.D8Command;
-import com.android.tools.r8.shaking.ProguardRuleParserException;
-import java.io.IOException;
 import java.nio.file.Paths;
-import java.util.concurrent.ExecutionException;
 import org.junit.Test;
 
 public class D8Framework14082017DesugaredVerificationTest extends CompilationTestBase {
@@ -17,8 +13,7 @@
   private static final String JAR = "third_party/framework/framework_14082017_desugared.jar";
 
   @Test
-  public void verifyDebugBuild()
-      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+  public void verifyDebugBuild() throws Exception {
     runAndCheckVerification(
         D8Command.builder()
             .addProgramFiles(Paths.get(JAR))
@@ -29,8 +24,7 @@
   }
 
   @Test
-  public void verifyReleaseBuild()
-      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+  public void verifyReleaseBuild() throws Exception {
     runAndCheckVerification(
         D8Command.builder()
             .addProgramFiles(Paths.get(JAR))
diff --git a/src/test/java/com/android/tools/r8/internal/D8Framework14082017VerificationTest.java b/src/test/java/com/android/tools/r8/internal/D8Framework14082017VerificationTest.java
index 87786df..d62de88 100644
--- a/src/test/java/com/android/tools/r8/internal/D8Framework14082017VerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/D8Framework14082017VerificationTest.java
@@ -3,13 +3,9 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.internal;
 
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.D8Command;
-import com.android.tools.r8.shaking.ProguardRuleParserException;
-import java.io.IOException;
 import java.nio.file.Paths;
-import java.util.concurrent.ExecutionException;
 import org.junit.Test;
 
 public class D8Framework14082017VerificationTest extends CompilationTestBase {
@@ -17,8 +13,7 @@
   private static final String JAR = "third_party/framework/framework_14082017.jar";
 
   @Test
-  public void verifyDebugBuild()
-      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+  public void verifyDebugBuild() throws Exception {
     runAndCheckVerification(
         D8Command.builder()
             .addProgramFiles(Paths.get(JAR))
@@ -29,8 +24,7 @@
   }
 
   @Test
-  public void verifyReleaseBuild()
-      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+  public void verifyReleaseBuild() throws Exception {
     runAndCheckVerification(
         D8Command.builder()
             .addProgramFiles(Paths.get(JAR))
diff --git a/src/test/java/com/android/tools/r8/internal/D8FrameworkDeterministicTest.java b/src/test/java/com/android/tools/r8/internal/D8FrameworkDeterministicTest.java
index 2cbd60a..12bf52f 100644
--- a/src/test/java/com/android/tools/r8/internal/D8FrameworkDeterministicTest.java
+++ b/src/test/java/com/android/tools/r8/internal/D8FrameworkDeterministicTest.java
@@ -7,11 +7,9 @@
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.D8Command;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.shaking.ProguardRuleParserException;
 import com.android.tools.r8.utils.AndroidApp;
 import java.io.IOException;
 import java.nio.file.Paths;
-import java.util.concurrent.ExecutionException;
 import org.junit.Test;
 
 public class D8FrameworkDeterministicTest extends CompilationTestBase {
@@ -23,8 +21,7 @@
   }
 
   @Test
-  public void verifyDebugBuild()
-      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+  public void verifyDebugBuild() throws Exception {
     D8Command command = D8Command.builder()
         .addProgramFiles(Paths.get(JAR))
         .setMode(CompilationMode.DEBUG)
@@ -36,8 +33,7 @@
   }
 
   @Test
-  public void verifyReleaseBuild()
-      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+  public void verifyReleaseBuild() throws Exception {
     D8Command command = D8Command.builder()
         .addProgramFiles(Paths.get(JAR))
         .setMode(CompilationMode.RELEASE)
diff --git a/src/test/java/com/android/tools/r8/internal/D8FrameworkVerificationTest.java b/src/test/java/com/android/tools/r8/internal/D8FrameworkVerificationTest.java
index a4f33d5..8cd9409 100644
--- a/src/test/java/com/android/tools/r8/internal/D8FrameworkVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/D8FrameworkVerificationTest.java
@@ -3,13 +3,9 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.internal;
 
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.D8Command;
-import com.android.tools.r8.shaking.ProguardRuleParserException;
-import java.io.IOException;
 import java.nio.file.Paths;
-import java.util.concurrent.ExecutionException;
 import org.junit.Test;
 
 public class D8FrameworkVerificationTest extends CompilationTestBase {
@@ -17,8 +13,7 @@
   private static final String JAR = "third_party/framework/framework_160115954.jar";
 
   @Test
-  public void verifyDebugBuild()
-      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+  public void verifyDebugBuild() throws Exception {
     runAndCheckVerification(
         D8Command.builder()
             .addProgramFiles(Paths.get(JAR))
@@ -29,8 +24,7 @@
   }
 
   @Test
-  public void verifyReleaseBuild()
-      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+  public void verifyReleaseBuild() throws Exception {
     runAndCheckVerification(
         D8Command.builder()
             .addProgramFiles(Paths.get(JAR))
diff --git a/src/test/java/com/android/tools/r8/internal/R8DisassemblerTest.java b/src/test/java/com/android/tools/r8/internal/R8DisassemblerTest.java
index 0d2c5af..ecff473 100644
--- a/src/test/java/com/android/tools/r8/internal/R8DisassemblerTest.java
+++ b/src/test/java/com/android/tools/r8/internal/R8DisassemblerTest.java
@@ -5,17 +5,13 @@
 
 import static com.android.tools.r8.utils.AndroidApp.DEFAULT_PROGUARD_MAP_FILE;
 
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.Disassemble;
-import com.android.tools.r8.shaking.ProguardRuleParserException;
 import com.android.tools.r8.utils.FileUtils;
-import java.io.IOException;
 import java.io.OutputStream;
 import java.io.PrintStream;
 import java.nio.file.Files;
 import java.nio.file.Paths;
 import java.util.Arrays;
-import java.util.concurrent.ExecutionException;
 import java.util.stream.Collectors;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -42,8 +38,7 @@
   public boolean smali;
 
   @Test
-  public void testDisassemble()
-      throws IOException, ExecutionException, ProguardRuleParserException, CompilationException {
+  public void testDisassemble() throws Exception {
     // This test only ensures that we do not break disassembling of dex code. It does not
     // check the generated code. To make it fast, we get rid of the output.
     PrintStream originalOut = System.out;
diff --git a/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java b/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java
index 8cf6e9b..ad625cc 100644
--- a/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java
+++ b/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java
@@ -39,6 +39,7 @@
 import java.util.List;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executors;
+import java.util.function.Consumer;
 import org.junit.Rule;
 import org.junit.rules.TemporaryFolder;
 
@@ -80,6 +81,11 @@
     return ToolHelper.runD8(builder.build());
   }
 
+  protected AndroidApp compileWithD8(
+      JasminBuilder builder, Consumer<InternalOptions> optionsConsumer) throws Exception {
+    return ToolHelper.runD8(builder.build(), optionsConsumer);
+  }
+
   protected String runOnArtD8(JasminBuilder builder, String main) throws Exception {
     return runOnArt(compileWithD8(builder), main);
   }
@@ -132,10 +138,16 @@
     return ToolHelper.runArtNoVerificationErrors(dex.toString(), main);
   }
 
+  protected ProcessResult runOnArtRaw(AndroidApp app, String main) throws IOException {
+    Path out = temp.getRoot().toPath().resolve("out.zip");
+    app.writeToZip(out, OutputMode.Indexed);
+    return ToolHelper.runArtRaw(out.toString(), main);
+  }
+
   protected String runOnArt(AndroidApp app, String main) throws IOException {
     Path out = temp.getRoot().toPath().resolve("out.zip");
     app.writeToZip(out, OutputMode.Indexed);
-    return ToolHelper.runArtNoVerificationErrors(ImmutableList.of(out.toString()), main, null);
+    return ToolHelper.runArtNoVerificationErrors(out.toString(), main);
   }
 
   protected static DexApplication process(DexApplication app, InternalOptions options)
diff --git a/src/test/java/com/android/tools/r8/jsr45/JSR45Tests.java b/src/test/java/com/android/tools/r8/jsr45/JSR45Tests.java
index b260304..c15b716 100644
--- a/src/test/java/com/android/tools/r8/jsr45/JSR45Tests.java
+++ b/src/test/java/com/android/tools/r8/jsr45/JSR45Tests.java
@@ -9,10 +9,8 @@
 import com.android.tools.r8.R8;
 import com.android.tools.r8.R8Command;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.graph.DexAnnotationElement;
 import com.android.tools.r8.graph.DexValue.DexValueString;
-import com.android.tools.r8.shaking.ProguardRuleParserException;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.DexInspector;
@@ -60,7 +58,7 @@
   }
 
   void compileWithR8(Path inputPath, Path outputPath, Path keepRulesPath)
-      throws IOException, CompilationException, ProguardRuleParserException {
+      throws IOException, CompilationException {
     AndroidApp androidApp =
         R8.run(
             R8Command.builder()
@@ -105,7 +103,7 @@
    */
   @Test
   public void testSourceDebugExtensionWithShriking1()
-      throws IOException, CompilationException, ExecutionException, ProguardRuleParserException {
+      throws IOException, CompilationException, ExecutionException {
     Path outputPath = tmpOutputDir.newFolder().toPath();
     compileWithR8(INPUT_PATH, outputPath, DONT_SHRINK_DONT_OBFUSCATE_CONFIG);
     checkAnnotationContent(INPUT_PATH, outputPath);
@@ -116,7 +114,7 @@
    */
   @Test
   public void testSourceDebugExtensionWithShrinking2()
-      throws IOException, CompilationException, ExecutionException, ProguardRuleParserException {
+      throws IOException, CompilationException, ExecutionException {
     Path outputPath = tmpOutputDir.newFolder().toPath();
     compileWithR8(INPUT_PATH, outputPath, DONT_SHRINK_CONFIG);
     checkAnnotationContent(INPUT_PATH, outputPath);
@@ -127,7 +125,7 @@
    */
   @Test
   public void testSourceDebugExtensionWithShrinking3()
-      throws IOException, CompilationException, ExecutionException, ProguardRuleParserException {
+      throws IOException, CompilationException, ExecutionException {
     Path outputPath = tmpOutputDir.newFolder().toPath();
 
     compileWithR8(INPUT_PATH, outputPath, SHRINK_KEEP_CONFIG);
@@ -141,7 +139,7 @@
    */
   @Test
   public void testSourceDebugExtensionWithShrinking4()
-      throws IOException, CompilationException, ExecutionException, ProguardRuleParserException {
+      throws IOException, CompilationException, ExecutionException {
     Path outputPath = tmpOutputDir.newFolder().toPath();
 
     compileWithR8(INPUT_PATH, outputPath, SHRINK_NO_KEEP_CONFIG);
diff --git a/src/test/java/com/android/tools/r8/naming/NamingTestBase.java b/src/test/java/com/android/tools/r8/naming/NamingTestBase.java
index 3d65e0d..53f0793 100644
--- a/src/test/java/com/android/tools/r8/naming/NamingTestBase.java
+++ b/src/test/java/com/android/tools/r8/naming/NamingTestBase.java
@@ -73,7 +73,7 @@
 
     RootSet rootSet = new RootSetBuilder(program, appInfo, configuration.getRules(), options)
         .run(ThreadUtils.getExecutorService(options));
-    Enqueuer enqueuer = new Enqueuer(appInfo);
+    Enqueuer enqueuer = new Enqueuer(appInfo, options);
     appInfo = enqueuer.traceApplication(rootSet, timing);
     return new Minifier(appInfo.withLiveness(), rootSet, options).run(timing);
   }
diff --git a/src/test/java/com/android/tools/r8/naming/RenameSourceFileSmaliTest.java b/src/test/java/com/android/tools/r8/naming/RenameSourceFileSmaliTest.java
index 4506310..acb0a51 100644
--- a/src/test/java/com/android/tools/r8/naming/RenameSourceFileSmaliTest.java
+++ b/src/test/java/com/android/tools/r8/naming/RenameSourceFileSmaliTest.java
@@ -1,3 +1,6 @@
+// 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.naming;
 
 import static org.junit.Assert.assertEquals;
diff --git a/src/test/java/com/android/tools/r8/rewrite/staticvalues/B67468748.java b/src/test/java/com/android/tools/r8/rewrite/staticvalues/B67468748.java
new file mode 100644
index 0000000..6d9f582
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/staticvalues/B67468748.java
@@ -0,0 +1,59 @@
+// 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.rewrite.staticvalues;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.jasmin.JasminBuilder;
+import com.android.tools.r8.jasmin.JasminTestBase;
+import com.android.tools.r8.utils.AndroidApp;
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+
+public class B67468748 extends JasminTestBase {
+
+  @Test
+  public void jarInput() throws Exception {
+    final String CLASS_NAME = "Test";
+
+    JasminBuilder builder = new JasminBuilder();
+    JasminBuilder.ClassBuilder clazz = builder.addClass(CLASS_NAME);
+
+    // The static field Test/intField is not defined even though it is written in the
+    // <clinit> code. This class cannot load, but we can still process it to dex, where Art also
+    // cannot load the class.
+    clazz.addStaticMethod("<clinit>", ImmutableList.of(), "V",
+        ".limit stack 1",
+        ".limit locals 1",
+        "iconst_1",
+        "putstatic Test/intField I",
+        "return");
+
+    clazz.addMainMethod(
+        ".limit stack 2",
+        ".limit locals 1",
+        "getstatic java/lang/System/out Ljava/io/PrintStream;",
+        "getstatic Test/intField I",
+        "invokevirtual java/io/PrintStream/print(I)V",
+        "return");
+
+    // The code does not run on the Java VM, as there is a missing field.
+    ProcessResult result = runOnJavaRaw(builder, CLASS_NAME);
+    assertEquals(1, result.exitCode);
+    assertTrue(result.stderr.contains("java.lang.NoSuchFieldError"));
+
+    // Run in release mode to turn on initializer defaults rewriting.
+    AndroidApp application = compileWithD8(builder, options -> options.debug = false);
+
+    // The code does not run on Art, as there is a missing field.
+    result = runOnArtRaw(application, CLASS_NAME);
+    assertEquals(1, result.exitCode);
+    assertTrue(result.stderr.contains("java.lang.NoSuchFieldError"));
+  }
+
+  // Test with dex input is in StaticValuesTest.
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/staticvalues/StaticValuesTest.java b/src/test/java/com/android/tools/r8/rewrite/staticvalues/StaticValuesTest.java
index 9e5a839..1d26039 100644
--- a/src/test/java/com/android/tools/r8/rewrite/staticvalues/StaticValuesTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/staticvalues/StaticValuesTest.java
@@ -7,7 +7,7 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
-import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
 import com.android.tools.r8.code.Instruction;
 import com.android.tools.r8.code.Sput;
 import com.android.tools.r8.code.SputObject;
@@ -24,6 +24,7 @@
 import com.android.tools.r8.graph.DexValue.DexValueShort;
 import com.android.tools.r8.graph.DexValue.DexValueString;
 import com.android.tools.r8.smali.SmaliTestBase;
+import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.DexInspector;
 import com.android.tools.r8.utils.DexInspector.MethodSubject;
 import com.android.tools.r8.utils.InternalOptions;
@@ -572,4 +573,43 @@
     assertEquals(StringUtils.lines("2"), result);
   }
 
+  @Test
+  public void b67468748() throws Exception {
+    final String CLASS_NAME = "Test";
+
+    SmaliBuilder builder = new SmaliBuilder(CLASS_NAME);
+
+    // The static field LTest;->intField:I is not defined even though it is written in the
+    // <clinit> code. This class cannot load, but we can still process it to output which still
+    // cannot load.
+    builder.addStaticInitializer(
+        2,
+        "const               v0, 3",
+        "sput                v0, LTest;->intField:I",
+        "return-void"
+    );
+    builder.addMainMethod(
+        3,
+        "sget-object         v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+        "sget                v1, LTest;->intField:I",
+        "invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->println(I)V",
+        "return-void"
+    );
+
+    AndroidApp application = builder.build();
+
+    // The code does not run on Art, as there is a missing field.
+    ProcessResult result = runOnArtRaw(application, CLASS_NAME);
+    assertEquals(1, result.exitCode);
+    assertTrue(result.stderr.contains("java.lang.NoSuchFieldError"));
+
+    // Run in release mode to turn on initializer defaults rewriting.
+    application = compileWithD8(application, options -> options.debug = false);
+
+    // The code does still not run on Art, as there is still a missing field.
+    result = runOnArtRaw(application, CLASS_NAME);
+    assertEquals(1, result.exitCode);
+    assertTrue(result.stderr.contains("java.lang.NoSuchFieldError"));
+  }
+
 }
diff --git a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
index fe0b6f6..cb76b5d 100644
--- a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
@@ -10,10 +10,12 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import com.android.tools.r8.DiagnosticsHandler;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.graph.DexAccessFlags;
 import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.utils.DefaultDiagnosticsHandler;
 import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.InternalOptions.KeepAttributeOptions;
 import com.android.tools.r8.utils.InternalOptions.PackageObfuscationMode;
@@ -104,19 +106,21 @@
   private static final String TARGET =
       VALID_PROGUARD_DIR + "target.flags";
 
+  private static final DiagnosticsHandler diagnosticsHandler = new DefaultDiagnosticsHandler();
+
   @Test
   public void parse() throws Exception {
     ProguardConfigurationParser parser;
 
     // Parse from file.
-    parser = new ProguardConfigurationParser(new DexItemFactory());
+    parser = new ProguardConfigurationParser(new DexItemFactory(), diagnosticsHandler);
     parser.parse(Paths.get(PROGUARD_SPEC_FILE));
     List<ProguardConfigurationRule> rules = parser.getConfig().getRules();
     assertEquals(24, rules.size());
     assertEquals(1, rules.get(0).getMemberRules().size());
 
     // Parse from strings.
-    parser = new ProguardConfigurationParser(new DexItemFactory());
+    parser = new ProguardConfigurationParser(new DexItemFactory(), diagnosticsHandler);
     List<String> lines = FileUtils.readTextFile(Paths.get(PROGUARD_SPEC_FILE));
     parser.parse(new ProguardConfigurationSourceStrings(lines));
     rules = parser.getConfig().getRules();
@@ -126,7 +130,8 @@
 
   @Test
   public void parseMultipleNamePatterns() throws Exception {
-    ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+    ProguardConfigurationParser parser =
+        new ProguardConfigurationParser(new DexItemFactory(), diagnosticsHandler);
     parser.parse(Paths.get(MULTIPLE_NAME_PATTERNS_FILE));
     List<ProguardConfigurationRule> rules = parser.getConfig().getRules();
     assertEquals(1, rules.size());
@@ -147,7 +152,8 @@
   @Test
   public void testDontWarn() throws Exception {
     DexItemFactory dexItemFactory = new DexItemFactory();
-    ProguardConfigurationParser parser = new ProguardConfigurationParser(dexItemFactory);
+    ProguardConfigurationParser parser =
+        new ProguardConfigurationParser(dexItemFactory, diagnosticsHandler);
     String dontwarn = "-dontwarn !foobar,*bar";
     parser.parse(new ProguardConfigurationSourceStrings(ImmutableList.of(dontwarn)));
     ProguardConfiguration config = parser.getConfig();
@@ -162,7 +168,8 @@
   @Test
   public void testDontWarnAllExplicitly() throws Exception {
     DexItemFactory dexItemFactory = new DexItemFactory();
-    ProguardConfigurationParser parser = new ProguardConfigurationParser(dexItemFactory);
+    ProguardConfigurationParser parser =
+        new ProguardConfigurationParser(dexItemFactory, diagnosticsHandler);
     String dontwarnAll = "-dontwarn *";
     parser.parse(new ProguardConfigurationSourceStrings(ImmutableList.of(dontwarnAll)));
     ProguardConfiguration config = parser.getConfig();
@@ -177,7 +184,8 @@
   @Test
   public void testDontWarnAllImplicitly() throws Exception {
     DexItemFactory dexItemFactory = new DexItemFactory();
-    ProguardConfigurationParser parser = new ProguardConfigurationParser(dexItemFactory);
+    ProguardConfigurationParser parser =
+        new ProguardConfigurationParser(dexItemFactory, diagnosticsHandler);
     String dontwarnAll = "-dontwarn";
     parser.parse(new ProguardConfigurationSourceStrings(ImmutableList.of(dontwarnAll)));
     ProguardConfiguration config = parser.getConfig();
@@ -191,7 +199,8 @@
 
   @Test
   public void parseAccessFlags() throws Exception {
-    ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+    ProguardConfigurationParser parser =
+        new ProguardConfigurationParser(new DexItemFactory(), diagnosticsHandler);
     parser.parse(Paths.get(ACCESS_FLAGS_FILE));
     List<ProguardConfigurationRule> rules = parser.getConfig().getRules();
     assertEquals(1, rules.size());
@@ -229,7 +238,8 @@
 
   @Test
   public void parseWhyAreYouKeeping() throws Exception {
-    ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+    ProguardConfigurationParser parser =
+        new ProguardConfigurationParser(new DexItemFactory(), diagnosticsHandler);
     parser.parse(Paths.get(WHY_ARE_YOU_KEEPING_FILE));
     List<ProguardConfigurationRule> rules = parser.getConfig().getRules();
     assertEquals(1, rules.size());
@@ -242,7 +252,8 @@
 
   @Test
   public void parseAssumeNoSideEffects() throws Exception {
-    ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+    ProguardConfigurationParser parser =
+        new ProguardConfigurationParser(new DexItemFactory(), diagnosticsHandler);
     parser.parse(Paths.get(ASSUME_NO_SIDE_EFFECTS));
     List<ProguardConfigurationRule> assumeNoSideEffects = parser.getConfig().getRules();
     assertEquals(1, assumeNoSideEffects.size());
@@ -253,7 +264,8 @@
 
   @Test
   public void parseAssumeNoSideEffectsWithReturnValue() throws Exception {
-    ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+    ProguardConfigurationParser parser =
+        new ProguardConfigurationParser(new DexItemFactory(), diagnosticsHandler);
     parser.parse(Paths.get(ASSUME_NO_SIDE_EFFECTS_WITH_RETURN_VALUE));
     List<ProguardConfigurationRule> assumeNoSideEffects = parser.getConfig().getRules();
     assertEquals(1, assumeNoSideEffects.size());
@@ -292,7 +304,8 @@
 
   @Test
   public void parseAssumeValuesWithReturnValue() throws Exception {
-    ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+    ProguardConfigurationParser parser =
+        new ProguardConfigurationParser(new DexItemFactory(), diagnosticsHandler);
     parser.parse(Paths.get(ASSUME_VALUES_WITH_RETURN_VALUE));
     List<ProguardConfigurationRule> assumeValues = parser.getConfig().getRules();
     assertEquals(1, assumeValues.size());
@@ -330,8 +343,76 @@
   }
 
   @Test
+  public void testAdaptClassStrings() throws Exception {
+    DexItemFactory dexItemFactory = new DexItemFactory();
+    ProguardConfigurationParser parser =
+        new ProguardConfigurationParser(dexItemFactory, diagnosticsHandler);
+    String config1 = "-adaptclassstrings !foobar,*bar";
+    String config2 = "-adaptclassstrings !a.b.c.nope,a.b.**";
+    parser.parse( new ProguardConfigurationSourceStrings(ImmutableList.of(config1, config2)));
+    ProguardConfiguration config = parser.getConfig();
+    List<ProguardConfigurationRule> rules = config.getRules();
+    assertEquals(2, rules.size());
+    assertEquals(0, rules.get(0).getMemberRules().size());
+    assertEquals(2, rules.get(0).getClassNames().size());
+    assertFalse(
+        rules.get(0).getClassNames().matches(dexItemFactory.createType("Lboobaz;")));
+    assertTrue(
+        rules.get(0).getClassNames().matches(dexItemFactory.createType("Lboobar;")));
+    assertFalse(
+        rules.get(0).getClassNames().matches(dexItemFactory.createType("Lfoobar;")));
+    assertEquals(0, rules.get(1).getMemberRules().size());
+    assertEquals(2, rules.get(1).getClassNames().size());
+    assertFalse(
+        rules.get(1).getClassNames().matches(dexItemFactory.createType("Lx/y/z;")));
+    assertTrue(
+        rules.get(1).getClassNames().matches(dexItemFactory.createType("La/b/c/d;")));
+    assertTrue(
+        rules.get(1).getClassNames().matches(dexItemFactory.createType("La/b/p/q;")));
+    assertFalse(
+        rules.get(1).getClassNames().matches(dexItemFactory.createType("La/b/c/nope;")));
+  }
+
+  @Test
+  public void testAdaptClassStringsAllExplicitly() throws Exception {
+    DexItemFactory dexItemFactory = new DexItemFactory();
+    ProguardConfigurationParser parser =
+        new ProguardConfigurationParser(dexItemFactory, diagnosticsHandler);
+    String adaptAll = "-adaptclassstrings *";
+    parser.parse(new ProguardConfigurationSourceStrings(ImmutableList.of(adaptAll)));
+    ProguardConfiguration config = parser.getConfig();
+    List<ProguardConfigurationRule> rules = config.getRules();
+    assertEquals(1, rules.size());
+    assertTrue(
+        rules.get(0).getClassNames().matches(dexItemFactory.createType("Lboobaz;")));
+    assertTrue(
+        rules.get(0).getClassNames().matches(dexItemFactory.createType("Lboobaz;")));
+    assertTrue(
+        rules.get(0).getClassNames().matches(dexItemFactory.createType("Lfoobar;")));
+  }
+
+  @Test
+  public void testAdaptClassStringsAllImplicitly() throws Exception {
+    DexItemFactory dexItemFactory = new DexItemFactory();
+    ProguardConfigurationParser parser =
+        new ProguardConfigurationParser(dexItemFactory, diagnosticsHandler);
+    String adaptAll = "-adaptclassstrings";
+    parser.parse(new ProguardConfigurationSourceStrings(ImmutableList.of(adaptAll)));
+    ProguardConfiguration config = parser.getConfig();
+    List<ProguardConfigurationRule> rules = config.getRules();
+    assertEquals(1, rules.size());
+    assertTrue(
+        rules.get(0).getClassNames().matches(dexItemFactory.createType("Lboobaz;")));
+    assertTrue(
+        rules.get(0).getClassNames().matches(dexItemFactory.createType("Lboobaz;")));
+    assertTrue(
+        rules.get(0).getClassNames().matches(dexItemFactory.createType("Lfoobar;")));
+  }
+
+  @Test
   public void testIdentifierNameString() throws Exception {
-    ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+    ProguardConfigurationParser parser =
+        new ProguardConfigurationParser(new DexItemFactory(), diagnosticsHandler);
     String config1 =
         "-identifiernamestring class a.b.c.*GeneratedClass {\n"
         + "  static java.lang.String CONTAINING_TYPE_*;\n"
@@ -375,7 +456,8 @@
 
   @Test
   public void parseDontobfuscate() throws Exception {
-    ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+    ProguardConfigurationParser parser =
+        new ProguardConfigurationParser(new DexItemFactory(), diagnosticsHandler);
     parser.parse(Paths.get(DONT_OBFUSCATE));
     ProguardConfiguration config = parser.getConfig();
     assertFalse(config.isObfuscating());
@@ -383,7 +465,8 @@
 
   @Test
   public void parseRepackageClassesEmpty() throws Exception {
-    ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+    ProguardConfigurationParser parser =
+        new ProguardConfigurationParser(new DexItemFactory(), diagnosticsHandler);
     parser.parse(Paths.get(PACKAGE_OBFUSCATION_1));
     ProguardConfiguration config = parser.getConfig();
     assertEquals(PackageObfuscationMode.REPACKAGE, config.getPackageObfuscationMode());
@@ -393,7 +476,8 @@
 
   @Test
   public void parseRepackageClassesNonEmpty() throws Exception {
-    ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+    ProguardConfigurationParser parser =
+        new ProguardConfigurationParser(new DexItemFactory(), diagnosticsHandler);
     parser.parse(Paths.get(PACKAGE_OBFUSCATION_2));
     ProguardConfiguration config = parser.getConfig();
     assertEquals(PackageObfuscationMode.REPACKAGE, config.getPackageObfuscationMode());
@@ -403,7 +487,8 @@
 
   @Test
   public void parseFlattenPackageHierarchyEmpty() throws Exception {
-    ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+    ProguardConfigurationParser parser =
+        new ProguardConfigurationParser(new DexItemFactory(), diagnosticsHandler);
     parser.parse(Paths.get(PACKAGE_OBFUSCATION_3));
     ProguardConfiguration config = parser.getConfig();
     assertEquals(PackageObfuscationMode.FLATTEN, config.getPackageObfuscationMode());
@@ -413,7 +498,8 @@
 
   @Test
   public void parseFlattenPackageHierarchyNonEmpty() throws Exception {
-    ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+    ProguardConfigurationParser parser =
+        new ProguardConfigurationParser(new DexItemFactory(), diagnosticsHandler);
     parser.parse(Paths.get(PACKAGE_OBFUSCATION_4));
     ProguardConfiguration config = parser.getConfig();
     assertEquals(PackageObfuscationMode.FLATTEN, config.getPackageObfuscationMode());
@@ -424,7 +510,8 @@
   @Test
   public void flattenPackageHierarchyCannotOverrideRepackageClasses()
       throws Exception {
-    ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+    ProguardConfigurationParser parser =
+        new ProguardConfigurationParser(new DexItemFactory(), diagnosticsHandler);
     parser.parse(Paths.get(PACKAGE_OBFUSCATION_5));
     ProguardConfiguration config = parser.getConfig();
     assertEquals(PackageObfuscationMode.REPACKAGE, config.getPackageObfuscationMode());
@@ -435,7 +522,8 @@
   @Test
   public void repackageClassesOverridesFlattenPackageHierarchy()
       throws Exception {
-    ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+    ProguardConfigurationParser parser =
+        new ProguardConfigurationParser(new DexItemFactory(), diagnosticsHandler);
     parser.parse(Paths.get(PACKAGE_OBFUSCATION_6));
     ProguardConfiguration config = parser.getConfig();
     assertEquals(PackageObfuscationMode.REPACKAGE, config.getPackageObfuscationMode());
@@ -445,7 +533,8 @@
 
   @Test
   public void parseApplyMapping() throws Exception {
-    ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+    ProguardConfigurationParser parser =
+        new ProguardConfigurationParser(new DexItemFactory(), diagnosticsHandler);
     parser.parse(Paths.get(APPLY_MAPPING));
     ProguardConfiguration config = parser.getConfig();
     assertTrue(config.hasApplyMappingFile());
@@ -454,7 +543,8 @@
   @Test
   public void parseApplyMappingWithoutFile() throws Exception {
     try {
-      ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+      ProguardConfigurationParser parser =
+          new ProguardConfigurationParser(new DexItemFactory(), diagnosticsHandler);
       parser.parse(Paths.get(APPLY_MAPPING_WITHOUT_FILE));
       fail("Expect to fail due to the lack of file name.");
     } catch (ProguardRuleParserException e) {
@@ -464,13 +554,15 @@
 
   @Test
   public void parseIncluding() throws Exception {
-    new ProguardConfigurationParser(new DexItemFactory()).parse(Paths.get(INCLUDING));
+    new ProguardConfigurationParser(new DexItemFactory(), diagnosticsHandler)
+        .parse(Paths.get(INCLUDING));
   }
 
   @Test
   public void parseInvalidIncluding1() throws IOException {
     try {
-      new ProguardConfigurationParser(new DexItemFactory()).parse(Paths.get(INVALID_INCLUDING_1));
+      new ProguardConfigurationParser(new DexItemFactory(), diagnosticsHandler)
+          .parse(Paths.get(INVALID_INCLUDING_1));
       fail();
     } catch (ProguardRuleParserException e) {
       assertTrue(e.getMessage().contains("6")); // line
@@ -482,7 +574,8 @@
   @Test
   public void parseInvalidIncluding2() throws IOException {
     try {
-      new ProguardConfigurationParser(new DexItemFactory()).parse(Paths.get(INVALID_INCLUDING_2));
+      new ProguardConfigurationParser(new DexItemFactory(), diagnosticsHandler)
+          .parse(Paths.get(INVALID_INCLUDING_2));
       fail();
     } catch (ProguardRuleParserException e) {
       String message = e.getMessage();
@@ -494,7 +587,8 @@
 
   @Test
   public void parseLibraryJars() throws Exception {
-    ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+    ProguardConfigurationParser parser =
+        new ProguardConfigurationParser(new DexItemFactory(), diagnosticsHandler);
     if (!ToolHelper.isLinux() && !ToolHelper.isMac()) {
       parser.parse(Paths.get(LIBRARY_JARS_WIN));
     } else {
@@ -506,7 +600,8 @@
   @Test
   public void parseInvalidFilePattern() throws IOException {
     try {
-      ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+      ProguardConfigurationParser parser =
+          new ProguardConfigurationParser(new DexItemFactory(), diagnosticsHandler);
       parser.parse(new ProguardConfigurationSourceStrings(
           Collections.singletonList("-injars abc.jar(*.zip;*.class)")));
     } catch (ProguardRuleParserException e) {
@@ -517,7 +612,8 @@
 
   @Test
   public void parseSeeds() throws Exception {
-    ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+    ProguardConfigurationParser parser =
+        new ProguardConfigurationParser(new DexItemFactory(), diagnosticsHandler);
     parser.parse(Paths.get(SEEDS));
     ProguardConfiguration config = parser.getConfig();
     assertTrue(config.isPrintSeeds());
@@ -526,7 +622,8 @@
 
   @Test
   public void parseSeeds2() throws Exception {
-    ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+    ProguardConfigurationParser parser =
+        new ProguardConfigurationParser(new DexItemFactory(), diagnosticsHandler);
     parser.parse(Paths.get(SEEDS_2));
     ProguardConfiguration config = parser.getConfig();
     assertTrue(config.isPrintSeeds());
@@ -535,7 +632,8 @@
 
   @Test
   public void parseVerbose() throws Exception {
-    ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+    ProguardConfigurationParser parser =
+        new ProguardConfigurationParser(new DexItemFactory(), diagnosticsHandler);
     parser.parse(Paths.get(VERBOSE));
     ProguardConfiguration config = parser.getConfig();
     assertTrue(config.isVerbose());
@@ -543,13 +641,15 @@
 
   @Test
   public void parseKeepdirectories() throws Exception {
-    ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+    ProguardConfigurationParser parser =
+        new ProguardConfigurationParser(new DexItemFactory(), diagnosticsHandler);
     parser.parse(Paths.get(KEEPDIRECTORIES));
   }
 
   @Test
   public void parseDontshrink() throws Exception {
-    ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+    ProguardConfigurationParser parser =
+        new ProguardConfigurationParser(new DexItemFactory(), diagnosticsHandler);
     parser.parse(Paths.get(DONT_SHRINK));
     ProguardConfiguration config = parser.getConfig();
     assertFalse(config.isShrinking());
@@ -557,39 +657,45 @@
 
   @Test
   public void parseDontSkipNonPublicLibraryClasses() throws Exception {
-    ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+    ProguardConfigurationParser parser =
+        new ProguardConfigurationParser(new DexItemFactory(), diagnosticsHandler);
     parser.parse(Paths.get(DONT_SKIP_NON_PUBLIC_LIBRARY_CLASSES));
   }
 
   @Test
   public void parseDontskipnonpubliclibraryclassmembers() throws Exception {
-    ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+    ProguardConfigurationParser parser =
+        new ProguardConfigurationParser(new DexItemFactory(), diagnosticsHandler);
     parser.parse(Paths.get(DONT_SKIP_NON_PUBLIC_LIBRARY_CLASS_MEMBERS));
   }
 
   @Test
   public void parseOverloadAggressively() throws Exception {
-    ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+    ProguardConfigurationParser parser =
+        new ProguardConfigurationParser(new DexItemFactory(), diagnosticsHandler);
     parser.parse(Paths.get(OVERLOAD_AGGRESIVELY));
   }
 
   @Test
   public void parseDontOptimize() throws Exception {
-    ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+    ProguardConfigurationParser parser =
+        new ProguardConfigurationParser(new DexItemFactory(), diagnosticsHandler);
     parser.parse(Paths.get(DONT_OPTIMIZE));
     ProguardConfiguration config = parser.getConfig();
   }
 
   @Test
   public void parseDontOptimizeOverridesPasses() throws Exception {
-    ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+    ProguardConfigurationParser parser =
+        new ProguardConfigurationParser(new DexItemFactory(), diagnosticsHandler);
     parser.parse(Paths.get(DONT_OPTIMIZE_OVERRIDES_PASSES));
     ProguardConfiguration config = parser.getConfig();
   }
 
   @Test
   public void parseOptimizationPasses() throws Exception {
-    ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+    ProguardConfigurationParser parser =
+        new ProguardConfigurationParser(new DexItemFactory(), diagnosticsHandler);
     parser.parse(Paths.get(OPTIMIZATION_PASSES));
     ProguardConfiguration config = parser.getConfig();
   }
@@ -597,7 +703,8 @@
   @Test
   public void parseOptimizationPassesError() throws Exception {
     try {
-      ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+      ProguardConfigurationParser parser =
+          new ProguardConfigurationParser(new DexItemFactory(), diagnosticsHandler);
       parser.parse(Paths.get(OPTIMIZATION_PASSES_WITHOUT_N));
       fail();
     } catch (ProguardRuleParserException e) {
@@ -608,7 +715,8 @@
   @Test
   public void parseSkipNonPublicLibraryClasses() throws IOException {
     try {
-      ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+      ProguardConfigurationParser parser =
+          new ProguardConfigurationParser(new DexItemFactory(), diagnosticsHandler);
       parser.parse(Paths.get(SKIP_NON_PUBLIC_LIBRARY_CLASSES));
       fail();
     } catch (ProguardRuleParserException e) {
@@ -618,13 +726,15 @@
 
   @Test
   public void parseAndskipSingleArgument() throws Exception {
-    ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+    ProguardConfigurationParser parser =
+        new ProguardConfigurationParser(new DexItemFactory(), diagnosticsHandler);
     parser.parse(Paths.get(PARSE_AND_SKIP_SINGLE_ARGUMENT));
   }
 
   @Test
   public void parsePrintUsage() throws Exception {
-    ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+    ProguardConfigurationParser parser =
+        new ProguardConfigurationParser(new DexItemFactory(), diagnosticsHandler);
     parser.parse(Paths.get(PRINT_USAGE));
     ProguardConfiguration config = parser.getConfig();
     assertTrue(config.isPrintUsage());
@@ -633,7 +743,8 @@
 
   @Test
   public void parsePrintUsageToFile() throws Exception {
-    ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+    ProguardConfigurationParser parser =
+        new ProguardConfigurationParser(new DexItemFactory(), diagnosticsHandler);
     parser.parse(Paths.get(PRINT_USAGE_TO_FILE));
     ProguardConfiguration config = parser.getConfig();
     assertTrue(config.isPrintUsage());
@@ -642,14 +753,16 @@
 
   @Test
   public void parseTarget() throws Exception {
-    ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+    ProguardConfigurationParser parser =
+        new ProguardConfigurationParser(new DexItemFactory(), diagnosticsHandler);
     parser.parse(Paths.get(TARGET));
   }
 
   @Test
   public void parseInvalidKeepClassOption() throws Exception {
     try {
-      ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+      ProguardConfigurationParser parser =
+          new ProguardConfigurationParser(new DexItemFactory(), diagnosticsHandler);
       Path proguardConfig = writeTextToTempFile(
           "-keepclassx public class * {  ",
           "  native <methods>;           ",
@@ -664,7 +777,8 @@
 
   @Test
   public void parseCustomFlags() throws Exception {
-    ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+    ProguardConfigurationParser parser =
+        new ProguardConfigurationParser(new DexItemFactory(), diagnosticsHandler);
     // Custom Proguard flags -runtype and -laststageoutput are ignored.
     Path proguardConfig = writeTextToTempFile(
         "-runtype FINAL                    ",
@@ -675,7 +789,8 @@
 
   @Test
   public void testRenameSourceFileAttribute() throws Exception {
-    ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+    ProguardConfigurationParser parser =
+        new ProguardConfigurationParser(new DexItemFactory(), diagnosticsHandler);
     String config1 = "-renamesourcefileattribute PG\n";
     String config2 = "-keepattributes SourceFile,SourceDir\n";
     parser.parse(new ProguardConfigurationSourceStrings(ImmutableList.of(config1, config2)));
@@ -687,7 +802,8 @@
 
   @Test
   public void testRenameSourceFileAttributeEmpty() throws Exception {
-    ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+    ProguardConfigurationParser parser =
+        new ProguardConfigurationParser(new DexItemFactory(), diagnosticsHandler);
     String config1 = "-renamesourcefileattribute\n";
     String config2 = "-keepattributes SourceFile\n";
     parser.parse(new ProguardConfigurationSourceStrings(ImmutableList.of(config1, config2)));
@@ -697,7 +813,8 @@
   }
 
   private void testKeepattributes(List<String> expected, String config) throws Exception {
-    ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+    ProguardConfigurationParser parser =
+        new ProguardConfigurationParser(new DexItemFactory(), diagnosticsHandler);
     parser.parse(new ProguardConfigurationSourceStrings(ImmutableList.of(config)));
     assertEquals(expected, parser.getConfig().getKeepAttributesPatterns());
   }
@@ -721,7 +838,8 @@
   @Test
   public void parseInvalidKeepattributes() throws Exception {
     try {
-      ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+      ProguardConfigurationParser parser =
+          new ProguardConfigurationParser(new DexItemFactory(), diagnosticsHandler);
       parser.parse(new ProguardConfigurationSourceStrings(ImmutableList.of("-keepattributes xxx,")));
       fail();
     } catch (ProguardRuleParserException e) {
@@ -731,7 +849,8 @@
 
   @Test
   public void parseUseUniqueClassMemberNames() throws Exception {
-    ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+    ProguardConfigurationParser parser =
+        new ProguardConfigurationParser(new DexItemFactory(), diagnosticsHandler);
     parser.parse(new ProguardConfigurationSourceStrings(ImmutableList.of(
         "-useuniqueclassmembernames"
     )));
@@ -742,7 +861,8 @@
   @Test
   public void parseKeepParameterNames() throws Exception {
     try {
-      ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+      ProguardConfigurationParser parser =
+          new ProguardConfigurationParser(new DexItemFactory(), diagnosticsHandler);
       parser.parse(new ProguardConfigurationSourceStrings(ImmutableList.of(
           "-keepparameternames"
       )));
@@ -756,7 +876,8 @@
 
   @Test
   public void parseKeepParameterNamesWithoutMinification() throws Exception {
-    ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+    ProguardConfigurationParser parser =
+        new ProguardConfigurationParser(new DexItemFactory(), diagnosticsHandler);
     parser.parse(new ProguardConfigurationSourceStrings(ImmutableList.of(
         "-keepparameternames",
         "-dontobfuscate"
@@ -764,7 +885,7 @@
     ProguardConfiguration config = parser.getConfig();
     assertTrue(config.isKeepParameterNames());
 
-    parser = new ProguardConfigurationParser(new DexItemFactory());
+    parser = new ProguardConfigurationParser(new DexItemFactory(), diagnosticsHandler);
     parser.parse(new ProguardConfigurationSourceStrings(ImmutableList.of(
         "-keepparameternames"
     )));
diff --git a/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java b/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java
index 795c020..9b6ba29 100644
--- a/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java
@@ -6,20 +6,17 @@
 import static com.android.tools.r8.ToolHelper.EXAMPLES_BUILD_DIR;
 import static com.android.tools.r8.ToolHelper.EXAMPLES_DIR;
 
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.R8;
 import com.android.tools.r8.R8Command;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.errors.CompilationError;
 import java.io.BufferedReader;
-import java.io.IOException;
 import java.io.PrintStream;
 import java.io.StringReader;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
-import java.util.concurrent.ExecutionException;
 import java.util.stream.Collectors;
 import org.junit.Assert;
 import org.junit.Rule;
@@ -37,8 +34,7 @@
   public ExpectedException thrown = ExpectedException.none();
 
   @Test
-  public void testIgnoreWarnings()
-      throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
+  public void testIgnoreWarnings() throws Exception {
     // Generate R8 processed version without library option.
     Path out = temp.getRoot().toPath();
     String test = "shaking2";
@@ -54,8 +50,7 @@
   }
 
   @Test
-  public void testMissingLibrary()
-      throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
+  public void testMissingLibrary() throws Exception {
     // Generate R8 processed version without library option.
     Path out = temp.getRoot().toPath();
     String test = "shaking2";
@@ -72,8 +67,7 @@
   }
 
   @Test
-  public void testPrintMapping()
-      throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
+  public void testPrintMapping() throws Exception {
     // Generate R8 processed version without library option.
     String test = "shaking1";
     Path out = temp.getRoot().toPath();
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
new file mode 100644
index 0000000..4d59f63
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
@@ -0,0 +1,52 @@
+// 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;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+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.google.common.collect.ImmutableList;
+import org.junit.Test;
+
+public class ForceProguardCompatibilityTest extends TestBase {
+  private void test(Class mainClass, Class mentionedClass, boolean arrayClass,
+      boolean forceProguardCompatibility)
+      throws Exception {
+    String proguardConfig = keepMainProguardConfiguration(mainClass, true, false);
+    DexInspector inspector = new DexInspector(
+        compileWithR8(
+            ImmutableList.of(mainClass, mentionedClass),
+            proguardConfig,
+            options -> options.forceProguardCompatibility = forceProguardCompatibility));
+    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());
+    }
+  }
+
+  @Test
+  public void testKeepDefaultInitializer() throws Exception {
+    test(TestMain.class, TestMain.MentionedClass.class, false, true);
+    test(TestMain.class, TestMain.MentionedClass.class, false, false);
+  }
+
+  @Test
+  public void testKeepDefaultInitializerArrayType() throws Exception {
+    test(TestMainArrayType.class, TestMainArrayType.MentionedClass.class, true, true);
+    test(TestMainArrayType.class, TestMainArrayType.MentionedClass.class, true, false);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/TestMain.java b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/TestMain.java
new file mode 100644
index 0000000..94a9d0a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/TestMain.java
@@ -0,0 +1,17 @@
+// 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 TestMain {
+
+  public static class MentionedClass {
+    public MentionedClass() {
+    }
+  }
+
+  public static void main(String[] args) {
+    System.out.println(MentionedClass.class.getCanonicalName());
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/TestMainArrayType.java b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/TestMainArrayType.java
new file mode 100644
index 0000000..8b1acaf
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/TestMainArrayType.java
@@ -0,0 +1,17 @@
+// 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 TestMainArrayType {
+
+  public static class MentionedClass {
+    public MentionedClass() {
+    }
+  }
+
+  public static void main(String[] args) {
+    System.out.println(MentionedClass[].class.getCanonicalName());
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java b/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
index f2f1220..afe0e2a 100644
--- a/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
+++ b/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
@@ -361,7 +361,7 @@
       getSource(currentClassName).add(builder.toString());
     }
 
-    public List<String> build() {
+    public List<String> buildSource() {
       List<String> result = new ArrayList<>(classes.size());
       for (String clazz : classes.keySet()) {
         Builder classBuilder = classes.get(clazz);
@@ -372,12 +372,18 @@
 
     public byte[] compile()
         throws IOException, RecognitionException, DexOverflowException, ExecutionException {
-      return Smali.compile(build());
+      return Smali.compile(buildSource());
     }
 
+    public AndroidApp build()
+        throws IOException, RecognitionException, DexOverflowException, ExecutionException {
+      return AndroidApp.fromDexProgramData(compile());
+    }
+
+
     @Override
     public String toString() {
-      return String.join("\n\n", build());
+      return String.join("\n\n", buildSource());
     }
   }
 
@@ -499,8 +505,7 @@
               .build();
       ToolHelper.runR8WithFullResult(command, optionsConsumer);
       return dexOutputDir.resolve("classes.dex");
-    } catch (CompilationException | IOException | RecognitionException | ExecutionException
-        | ProguardRuleParserException e) {
+    } catch (CompilationException | IOException | RecognitionException | ExecutionException e) {
       throw new RuntimeException(e);
     }
   }