Merge "Replace clinit static puts with initial values for static fields"
diff --git a/build.gradle b/build.gradle
index e2c21b6..722106c 100644
--- a/build.gradle
+++ b/build.gradle
@@ -6,11 +6,15 @@
 
 apply plugin: 'java'
 apply plugin: 'idea'
-apply plugin: 'jacoco'
 apply plugin: 'com.google.protobuf'
 
 apply from: 'copyAdditionalJctfCommonFiles.gradle'
 
+
+if (project.hasProperty('with_code_coverage')) {
+    apply plugin: 'jacoco'
+}
+
 repositories {
     maven { url 'https://maven.google.com' }
     mavenCentral()
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 2f5fb79..4c8cdd5 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -172,7 +172,6 @@
 
       // Disable global optimizations.
       options.skipMinification = true;
-      options.allowAccessModification = false;
       options.inlineAccessors = false;
       options.outline.enabled = false;
 
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index 2937205..64a1a0e 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -215,8 +215,6 @@
     internal.useTreeShaking = false;
     assert internal.interfaceMethodDesugaring == OffOrAuto.Off;
     assert internal.tryWithResourcesDesugaring == OffOrAuto.Off;
-    assert internal.allowAccessModification;
-    internal.allowAccessModification = false;
     assert internal.inlineAccessors;
     internal.inlineAccessors = false;
     assert internal.removeSwitchMaps;
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 84bcee7..acbbe0a 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -244,7 +244,8 @@
       timing.begin("Strip unused code");
       try {
         Set<DexType> missingClasses = appInfo.getMissingClasses();
-        missingClasses = filterMissingClasses(missingClasses, options.dontWarnPatterns);
+        missingClasses = filterMissingClasses(
+            missingClasses, options.proguardConfiguration.getDontWarnPatterns());
         if (!missingClasses.isEmpty()) {
           System.err.println();
           System.err.println("WARNING, some classes are missing:");
@@ -256,11 +257,12 @@
                 "Shrinking can't be performed because some library classes are missing.");
           }
         }
-        rootSet = new RootSetBuilder(application, appInfo, options.keepRules).run(executorService);
+        rootSet = new RootSetBuilder(application, appInfo, options.proguardConfiguration.getRules())
+            .run(executorService);
         Enqueuer enqueuer = new Enqueuer(appInfo);
         enqueuer.addExtension(new ProtoLiteExtension(appInfo));
         appInfo = enqueuer.traceApplication(rootSet, timing);
-        if (options.printSeeds) {
+        if (options.proguardConfiguration.isPrintSeeds()) {
           ByteArrayOutputStream bytes = new ByteArrayOutputStream();
           PrintStream out = new PrintStream(bytes);
           RootSetBuilder.writeSeeds(appInfo.withLiveness().pinnedItems, out);
@@ -278,7 +280,7 @@
         timing.end();
       }
 
-      if (options.allowAccessModification) {
+      if (options.proguardConfiguration.isAccessModificationAllowed()) {
         ClassAndMemberPublicizer.run(application);
         // We can now remove visibility bridges. Note that we do not need to update the
         // invoke-targets here, as the existing invokes will simply dispatch to the now
@@ -424,23 +426,23 @@
       outputApp.write(command.getOutputPath(), options.outputMode);
     }
 
-    if (options.printMapping && !options.skipMinification) {
+    if (options.proguardConfiguration.isPrintMapping() && !options.skipMinification) {
       assert outputApp.hasProguardMap();
       try (Closer closer = Closer.create()) {
         OutputStream mapOut = FileUtils.openPathWithDefault(
             closer,
-            options.printMappingFile,
+            options.proguardConfiguration.getPrintMappingFile(),
             System.out,
             StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
         outputApp.writeProguardMap(closer, mapOut);
       }
     }
-    if (options.printSeeds) {
+    if (options.proguardConfiguration.isPrintSeeds()) {
       assert outputApp.hasProguardSeeds();
       try (Closer closer = Closer.create()) {
         OutputStream seedsOut = FileUtils.openPathWithDefault(
             closer,
-            options.seedsFile,
+            options.proguardConfiguration.getSeedFile(),
             System.out,
             StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
         outputApp.writeProguardSeeds(closer, seedsOut);
@@ -457,11 +459,11 @@
         outputApp.writeMainDexList(closer, mainDexOut);
       }
     }
-    if (options.printUsage && outputApp.hasDeadCode()) {
+    if (options.proguardConfiguration.isPrintUsage() && outputApp.hasDeadCode()) {
       try (Closer closer = Closer.create()) {
         OutputStream deadCodeOut = FileUtils.openPathWithDefault(
             closer,
-            options.printUsageFile,
+            options.proguardConfiguration.getPrintUsageFile(),
             System.out,
             StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
         outputApp.writeDeadCode(closer, deadCodeOut);
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 86844a3..928b28f 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -5,13 +5,13 @@
 
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.shaking.ProguardConfiguration;
+import com.android.tools.r8.shaking.ProguardConfiguration.Builder;
 import com.android.tools.r8.shaking.ProguardConfigurationParser;
 import com.android.tools.r8.shaking.ProguardConfigurationRule;
 import com.android.tools.r8.shaking.ProguardRuleParserException;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.InternalOptions.PackageObfuscationMode;
 import com.android.tools.r8.utils.OutputMode;
 import com.google.common.collect.ImmutableList;
 import java.io.IOException;
@@ -21,6 +21,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Optional;
+import java.util.function.Consumer;
 
 public class R8Command extends BaseCommand {
 
@@ -28,6 +29,7 @@
 
     private final List<Path> mainDexRules = new ArrayList<>();
     private Path mainDexListOutput = null;
+    private Consumer<ProguardConfiguration.Builder> proguardConfigurationConsumer = null;
     private final List<Path> proguardConfigFiles = new ArrayList<>();
     private Optional<Boolean> treeShaking = Optional.empty();
     private Optional<Boolean> minification = Optional.empty();
@@ -35,10 +37,14 @@
 
     private Builder() {
       super(CompilationMode.RELEASE);
+      // TODO(b/62048823): Minifier should not depend on -allowaccessmodification.
+      proguardConfigurationConsumer = builder -> builder.setAllowAccessModification(true);
     }
 
     private Builder(AndroidApp app) {
       super(app, CompilationMode.RELEASE);
+      // TODO(b/62048823): Minifier should not depend on -allowaccessmodification.
+      proguardConfigurationConsumer = builder -> builder.setAllowAccessModification(true);
     }
 
     @Override
@@ -100,6 +106,20 @@
     }
 
     /**
+     * Add and/or chain proguard configuration consumer(s) for testing.
+     */
+    public Builder addProguardConfigurationConsumer(Consumer<ProguardConfiguration.Builder> c) {
+      Consumer<ProguardConfiguration.Builder> oldConsumer = proguardConfigurationConsumer;
+      proguardConfigurationConsumer = builder -> {
+        if (oldConsumer != null) {
+          oldConsumer.accept(builder);
+        }
+        c.accept(builder);
+      };
+      return self();
+    }
+
+    /**
      * Set a proguard mapping file resource.
      */
     public Builder setProguardMapFile(Path path) {
@@ -164,7 +184,11 @@
         } catch (ProguardRuleParserException e) {
           throw new CompilationException(e.getMessage(), e.getCause());
         }
-        configuration = parser.getConfig();
+        ProguardConfiguration.Builder configurationBuilder = parser.getConfigurationBuilder();
+        if (proguardConfigurationConsumer != null) {
+          proguardConfigurationConsumer.accept(configurationBuilder);
+        }
+        configuration = configurationBuilder.build();
         addProgramFiles(configuration.getInjars());
         addLibraryFiles(configuration.getLibraryjars());
       }
@@ -364,55 +388,27 @@
 
   @Override
   InternalOptions getInternalOptions() {
-    InternalOptions internal = new InternalOptions(proguardConfiguration.getDexItemFactory());
+    InternalOptions internal = new InternalOptions(proguardConfiguration);
     assert !internal.debug;
     internal.debug = getMode() == CompilationMode.DEBUG;
     internal.minApiLevel = getMinApiLevel();
     assert !internal.skipMinification;
-    internal.skipMinification = !useMinification();
+    internal.skipMinification = !useMinification() || !proguardConfiguration.isObfuscating();
     assert internal.useTreeShaking;
     internal.useTreeShaking = useTreeShaking();
-    assert !internal.printUsage;
-    internal.printUsage = proguardConfiguration.isPrintUsage();
-    internal.printUsageFile = proguardConfiguration.getPrintUsageFile();
     assert !internal.ignoreMissingClasses;
     internal.ignoreMissingClasses = ignoreMissingClasses;
-
-    // TODO(zerny): Consider which other proguard options should be given flags.
-    assert internal.packagePrefix.length() == 0;
-    internal.packageObfuscationMode = proguardConfiguration.getPackageObfuscationMode();
-    internal.packagePrefix = proguardConfiguration.getPackagePrefix();
-    assert internal.allowAccessModification;
-    internal.allowAccessModification = proguardConfiguration.getAllowAccessModification();
     for (String pattern : proguardConfiguration.getAttributesRemovalPatterns()) {
       internal.attributeRemoval.applyPattern(pattern);
     }
-    if (proguardConfiguration.isIgnoreWarnings()) {
-      internal.ignoreMissingClasses = true;
-    }
-    assert internal.seedsFile == null;
-    if (proguardConfiguration.getSeedFile() != null) {
-      internal.seedsFile = proguardConfiguration.getSeedFile();
-    }
+    internal.ignoreMissingClasses |= proguardConfiguration.isIgnoreWarnings();
     assert !internal.verbose;
-    if (proguardConfiguration.isVerbose()) {
-      internal.verbose = true;
-    }
-    if (!proguardConfiguration.isObfuscating()) {
-      internal.skipMinification = true;
-    }
-    internal.printSeeds |= proguardConfiguration.getPrintSeeds();
-    internal.printMapping |= proguardConfiguration.isPrintingMapping();
-    internal.printMappingFile = proguardConfiguration.getPrintMappingOutput();
-    internal.classObfuscationDictionary = proguardConfiguration.getClassObfuscationDictionary();
-    internal.obfuscationDictionary = proguardConfiguration.getObfuscationDictionary();
+    internal.verbose |= proguardConfiguration.isVerbose();
     internal.mainDexKeepRules = mainDexKeepRules;
     internal.minimalMainDex = internal.debug;
     if (mainDexListOutput != null) {
       internal.printMainDexListFile = mainDexListOutput;
     }
-    internal.keepRules = proguardConfiguration.getRules();
-    internal.dontWarnPatterns = proguardConfiguration.getDontWarnPatterns();
     internal.outputMode = getOutputMode();
     if (internal.debug) {
       // TODO(zerny): Should we support removeSwitchMaps in debug mode? b/62936642
diff --git a/src/main/java/com/android/tools/r8/code/Const.java b/src/main/java/com/android/tools/r8/code/Const.java
index 301f982..2f63777 100644
--- a/src/main/java/com/android/tools/r8/code/Const.java
+++ b/src/main/java/com/android/tools/r8/code/Const.java
@@ -41,12 +41,12 @@
   }
 
   public String toString(ClassNameMapper naming) {
-    return formatString("v" + AA + ", 0x" + StringUtils.hexString(decodedValue(), 8) +
+    return formatString("v" + AA + ", " + StringUtils.hexString(decodedValue(), 8) +
         " (" + decodedValue() + ")");
   }
 
   public String toSmaliString(ClassNameMapper naming) {
-    return formatSmaliString("v" + AA + ", 0x" + StringUtils.hexString(decodedValue(), 8) +
+    return formatSmaliString("v" + AA + ", " + StringUtils.hexString(decodedValue(), 8) +
         "  # " + decodedValue());
   }
 
diff --git a/src/main/java/com/android/tools/r8/code/Const16.java b/src/main/java/com/android/tools/r8/code/Const16.java
index aa00341..baf2ca7 100644
--- a/src/main/java/com/android/tools/r8/code/Const16.java
+++ b/src/main/java/com/android/tools/r8/code/Const16.java
@@ -42,7 +42,7 @@
 
   @Override
   public String toString(ClassNameMapper naming) {
-    return formatString("v" + AA + ", 0x" + StringUtils.hexString(decodedValue(), 4) +
+    return formatString("v" + AA + ", " + StringUtils.hexString(decodedValue(), 4) +
         " (" + decodedValue() + ")");
   }
 
diff --git a/src/main/java/com/android/tools/r8/code/Const4.java b/src/main/java/com/android/tools/r8/code/Const4.java
index 26da02a..b907e78 100644
--- a/src/main/java/com/android/tools/r8/code/Const4.java
+++ b/src/main/java/com/android/tools/r8/code/Const4.java
@@ -41,12 +41,12 @@
   }
 
   public String toString(ClassNameMapper naming) {
-    return formatString("v" + A + ", 0x" + StringUtils.hexString(decodedValue(), 1) +
+    return formatString("v" + A + ", " + StringUtils.hexString(decodedValue(), 1) +
         " (" + decodedValue() + ")");
   }
 
   public String toSmaliString(ClassNameMapper naming) {
-    return formatSmaliString("v" + A + ", 0x" + StringUtils.hexString(decodedValue(), 2) +
+    return formatSmaliString("v" + A + ", " + StringUtils.hexString(decodedValue(), 2) +
         "  # " + decodedValue());
   }
 
diff --git a/src/main/java/com/android/tools/r8/code/ConstHigh16.java b/src/main/java/com/android/tools/r8/code/ConstHigh16.java
index 3245fa9..d3a4264 100644
--- a/src/main/java/com/android/tools/r8/code/ConstHigh16.java
+++ b/src/main/java/com/android/tools/r8/code/ConstHigh16.java
@@ -41,12 +41,12 @@
   }
 
   public String toString(ClassNameMapper naming) {
-    return formatString("v" + AA + ", 0x" + StringUtils.hexString(decodedValue(), 8) +
+    return formatString("v" + AA + ", " + StringUtils.hexString(decodedValue(), 8) +
         " (" + decodedValue() + ")");
   }
 
   public String toSmaliString(ClassNameMapper naming) {
-    return formatSmaliString("v" + AA + ", 0x" + StringUtils.hexString(decodedValue(), 8) +
+    return formatSmaliString("v" + AA + ", " + StringUtils.hexString(decodedValue(), 8) +
         "  # " + decodedValue());
   }
 
diff --git a/src/main/java/com/android/tools/r8/code/ConstWide.java b/src/main/java/com/android/tools/r8/code/ConstWide.java
index 7323f9b..002a377 100644
--- a/src/main/java/com/android/tools/r8/code/ConstWide.java
+++ b/src/main/java/com/android/tools/r8/code/ConstWide.java
@@ -41,12 +41,12 @@
   }
 
   public String toString(ClassNameMapper naming) {
-    return formatString("v" + AA + ", 0x" + StringUtils.hexString(decodedValue(), 16) +
+    return formatString("v" + AA + ", " + StringUtils.hexString(decodedValue(), 16) +
         " (" + decodedValue() + ")");
   }
 
   public String toSmaliString(ClassNameMapper naming) {
-    return formatSmaliString("v" + AA + ", 0x" + StringUtils.hexString(decodedValue(), 16) +
+    return formatSmaliString("v" + AA + ", " + StringUtils.hexString(decodedValue(), 16) +
         "L  # " + decodedValue());
   }
 
diff --git a/src/main/java/com/android/tools/r8/code/ConstWide16.java b/src/main/java/com/android/tools/r8/code/ConstWide16.java
index edd93fc..b593491 100644
--- a/src/main/java/com/android/tools/r8/code/ConstWide16.java
+++ b/src/main/java/com/android/tools/r8/code/ConstWide16.java
@@ -41,13 +41,13 @@
   }
 
   public String toString(ClassNameMapper naming) {
-    return formatString("v" + AA + ", 0x" + StringUtils.hexString(decodedValue(), 16) +
+    return formatString("v" + AA + ", " + StringUtils.hexString(decodedValue(), 16) +
         " (" + decodedValue() + ")");
   }
 
   public String toSmaliString(ClassNameMapper naming) {
     return formatSmaliString(
-        "v" + AA + ", 0x" + StringUtils.hexString(decodedValue(), 16) + "L  # " + decodedValue());
+        "v" + AA + ", " + StringUtils.hexString(decodedValue(), 16) + "L  # " + decodedValue());
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/code/ConstWide32.java b/src/main/java/com/android/tools/r8/code/ConstWide32.java
index 540e840..5c4fc7b 100644
--- a/src/main/java/com/android/tools/r8/code/ConstWide32.java
+++ b/src/main/java/com/android/tools/r8/code/ConstWide32.java
@@ -41,13 +41,13 @@
   }
 
   public String toString(ClassNameMapper naming) {
-    return formatString("v" + AA + ", 0x" + StringUtils.hexString(decodedValue(), 16) +
+    return formatString("v" + AA + ", " + StringUtils.hexString(decodedValue(), 16) +
         " (" + decodedValue() + ")");
   }
 
   public String toSmaliString(ClassNameMapper naming) {
     return formatSmaliString(
-        "v" + AA + ", 0x" + StringUtils.hexString(decodedValue(), 16) + "  # " + decodedValue());
+        "v" + AA + ", " + StringUtils.hexString(decodedValue(), 16) + "  # " + decodedValue());
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/code/ConstWideHigh16.java b/src/main/java/com/android/tools/r8/code/ConstWideHigh16.java
index 864a079..182b39a 100644
--- a/src/main/java/com/android/tools/r8/code/ConstWideHigh16.java
+++ b/src/main/java/com/android/tools/r8/code/ConstWideHigh16.java
@@ -40,12 +40,12 @@
   }
 
   public String toString(ClassNameMapper naming) {
-    return formatString("v" + AA + ", 0x" + StringUtils.hexString(decodedValue(), 16) +
+    return formatString("v" + AA + ", " + StringUtils.hexString(decodedValue(), 16) +
         " (" + decodedValue() + ")");
   }
 
   public String toSmaliString(ClassNameMapper naming) {
-    return formatSmaliString("v" + AA + ", 0x" + StringUtils.hexString(decodedValue(), 16) +
+    return formatSmaliString("v" + AA + ", " + StringUtils.hexString(decodedValue(), 16) +
         "L  # " + decodedValue());
   }
 
diff --git a/src/main/java/com/android/tools/r8/code/FillArrayDataPayload.java b/src/main/java/com/android/tools/r8/code/FillArrayDataPayload.java
index 1f69d9c..885a8af 100644
--- a/src/main/java/com/android/tools/r8/code/FillArrayDataPayload.java
+++ b/src/main/java/com/android/tools/r8/code/FillArrayDataPayload.java
@@ -80,8 +80,7 @@
   public String toSmaliString(ClassNameMapper naming) {
     StringBuilder builder = new StringBuilder();
     builder.append("    ");
-    builder.append(".array-data");
-    builder.append(" 0x");
+    builder.append(".array-data ");
     builder.append(StringUtils.hexString(element_width, 1));
     builder.append("  # ");
     builder.append(element_width);
@@ -93,7 +92,6 @@
           int value = (data[i] >> j * 8) & 0xff;
           if (i * 2 + j < size) {
             builder.append("      ");
-            builder.append("0x");
             builder.append(StringUtils.hexString(value, 2));
             builder.append("  # ");
             builder.append(value);
@@ -109,7 +107,6 @@
         value = (Short.toUnsignedLong(data[i]) << (16 * (i % (element_width / 2)))) | value;
         if ((((i + 1) * 2) % element_width) == 0) {
           builder.append("      ");
-          builder.append("0x");
           builder.append(StringUtils.hexString(value, element_width * 2));
           builder.append("  # ");
           builder.append(value);
diff --git a/src/main/java/com/android/tools/r8/code/Format10t.java b/src/main/java/com/android/tools/r8/code/Format10t.java
index e5d2952..8d65d2b 100644
--- a/src/main/java/com/android/tools/r8/code/Format10t.java
+++ b/src/main/java/com/android/tools/r8/code/Format10t.java
@@ -40,8 +40,7 @@
   }
 
   public String toString(ClassNameMapper naming) {
-    String relative = AA >= 0 ? ("+" + AA) : Integer.toString(AA);
-    return formatString(relative + " (" + (getOffset() + AA) + ")");
+    return formatString(formatRelativeOffset(AA));
   }
 
   public String toSmaliString(ClassNameMapper naming) {
diff --git a/src/main/java/com/android/tools/r8/code/Format20t.java b/src/main/java/com/android/tools/r8/code/Format20t.java
index b3e3627..75721a3 100644
--- a/src/main/java/com/android/tools/r8/code/Format20t.java
+++ b/src/main/java/com/android/tools/r8/code/Format20t.java
@@ -40,7 +40,7 @@
   }
 
   public String toString(ClassNameMapper naming) {
-    return formatString("" + AAAA + " (" + (getOffset() + AAAA) + ")");
+    return formatString("" + AAAA + " " + formatRelativeOffset(AAAA));
   }
 
   public String toSmaliString(ClassNameMapper naming) {
diff --git a/src/main/java/com/android/tools/r8/code/Format21s.java b/src/main/java/com/android/tools/r8/code/Format21s.java
index db148bd..cc5d6c0 100644
--- a/src/main/java/com/android/tools/r8/code/Format21s.java
+++ b/src/main/java/com/android/tools/r8/code/Format21s.java
@@ -51,7 +51,7 @@
   }
 
   public String toSmaliString(ClassNameMapper naming) {
-    return formatSmaliString("v" + AA + ", 0x" + StringUtils.hexString(BBBB, 4) + "  # " + BBBB);
+    return formatSmaliString("v" + AA + ", " + StringUtils.hexString(BBBB, 4) + "  # " + BBBB);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/code/Format21t.java b/src/main/java/com/android/tools/r8/code/Format21t.java
index 26f7650..6e91554 100644
--- a/src/main/java/com/android/tools/r8/code/Format21t.java
+++ b/src/main/java/com/android/tools/r8/code/Format21t.java
@@ -62,7 +62,7 @@
   }
 
   public String toString(ClassNameMapper naming) {
-    return formatString("v" + AA + ", +" + BBBB + " (" + (getOffset() + BBBB) + ")");
+    return formatString("v" + AA + ", " + formatRelativeOffset(BBBB));
   }
 
   public String toSmaliString(ClassNameMapper naming) {
diff --git a/src/main/java/com/android/tools/r8/code/Format22b.java b/src/main/java/com/android/tools/r8/code/Format22b.java
index 8aeae42..1164b03 100644
--- a/src/main/java/com/android/tools/r8/code/Format22b.java
+++ b/src/main/java/com/android/tools/r8/code/Format22b.java
@@ -56,7 +56,7 @@
 
   public String toSmaliString(ClassNameMapper naming) {
     return formatSmaliString(
-        "v" + AA + ", v" + BB + ", 0x" + StringUtils.hexString(CC, 2) + "  # " + CC);
+        "v" + AA + ", v" + BB + ", " + StringUtils.hexString(CC, 2) + "  # " + CC);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/code/Format22s.java b/src/main/java/com/android/tools/r8/code/Format22s.java
index eef5fa5..85fbeab 100644
--- a/src/main/java/com/android/tools/r8/code/Format22s.java
+++ b/src/main/java/com/android/tools/r8/code/Format22s.java
@@ -56,7 +56,7 @@
 
   public String toSmaliString(ClassNameMapper naming) {
     return formatSmaliString(
-        "v" + A + ", v" + B + ", 0x" + StringUtils.hexString(CCCC, 4) + "  # " + CCCC);
+        "v" + A + ", v" + B + ", " + StringUtils.hexString(CCCC, 4) + "  # " + CCCC);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/code/Format22t.java b/src/main/java/com/android/tools/r8/code/Format22t.java
index 3ff5190..0a4d664 100644
--- a/src/main/java/com/android/tools/r8/code/Format22t.java
+++ b/src/main/java/com/android/tools/r8/code/Format22t.java
@@ -66,7 +66,7 @@
   }
 
   public String toString(ClassNameMapper naming) {
-    return formatString("v" + A + ", v" + B + ", +" + CCCC + " (" + (getOffset() + CCCC) + ")");
+    return formatString("v" + A + ", v" + B + ", " + formatRelativeOffset(CCCC));
   }
 
   public String toSmaliString(ClassNameMapper naming) {
diff --git a/src/main/java/com/android/tools/r8/code/Format30t.java b/src/main/java/com/android/tools/r8/code/Format30t.java
index 77cb49d..098863c 100644
--- a/src/main/java/com/android/tools/r8/code/Format30t.java
+++ b/src/main/java/com/android/tools/r8/code/Format30t.java
@@ -39,7 +39,7 @@
   }
 
   public String toString(ClassNameMapper naming) {
-    return formatString("" + AAAAAAAA);
+    return formatString(formatOffset(AAAAAAAA));
   }
 
   public String toSmaliString(ClassNameMapper naming) {
diff --git a/src/main/java/com/android/tools/r8/code/Format31t.java b/src/main/java/com/android/tools/r8/code/Format31t.java
index ec41ed4..87106f0 100644
--- a/src/main/java/com/android/tools/r8/code/Format31t.java
+++ b/src/main/java/com/android/tools/r8/code/Format31t.java
@@ -58,7 +58,7 @@
   }
 
   public String toString(ClassNameMapper naming) {
-    return formatString("v" + AA + ", +" + BBBBBBBB + " (" + (getOffset() + BBBBBBBB) + ")");
+    return formatString("v" + AA + ", " + formatRelativeOffset(BBBBBBBB));
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/code/Instruction.java b/src/main/java/com/android/tools/r8/code/Instruction.java
index 6b61845..617642c 100644
--- a/src/main/java/com/android/tools/r8/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/code/Instruction.java
@@ -142,9 +142,18 @@
     return 0;
   }
 
+  static String formatOffset(int offset) {
+    return StringUtils.hexString(offset, 2);
+  }
+
+  String formatRelativeOffset(int offset) {
+    String relativeString = offset >= 0 ? ("+" + offset) : Integer.toString(offset);
+    return formatOffset(getOffset() + offset) + " (" + relativeString + ")";
+  }
+
   String formatString(String left) {
     StringBuilder builder = new StringBuilder();
-    StringUtils.appendLeftPadded(builder, Integer.toString(getOffset()), 6);
+    StringUtils.appendLeftPadded(builder, formatOffset(getOffset()), 6);
     builder.append(": ");
     StringUtils.appendRightPadded(builder, getName(), 20);
     builder.append(left == null ? "" : left);
diff --git a/src/main/java/com/android/tools/r8/code/PackedSwitchPayload.java b/src/main/java/com/android/tools/r8/code/PackedSwitchPayload.java
index a3e2d15..5032ab0 100644
--- a/src/main/java/com/android/tools/r8/code/PackedSwitchPayload.java
+++ b/src/main/java/com/android/tools/r8/code/PackedSwitchPayload.java
@@ -94,7 +94,6 @@
     StringBuilder builder = new StringBuilder();
     builder.append("    ");
     builder.append(".packed-switch ");
-    builder.append("0x");
     builder.append(StringUtils.hexString(first_key, 8));
     builder.append("  # ");
     builder.append(first_key);
diff --git a/src/main/java/com/android/tools/r8/code/SparseSwitchPayload.java b/src/main/java/com/android/tools/r8/code/SparseSwitchPayload.java
index 328e6d2..e0e1280 100644
--- a/src/main/java/com/android/tools/r8/code/SparseSwitchPayload.java
+++ b/src/main/java/com/android/tools/r8/code/SparseSwitchPayload.java
@@ -103,7 +103,6 @@
     builder.append("\n");
     for (int i = 0; i < keys.length; i++) {
       builder.append("      ");
-      builder.append("0x");
       builder.append(StringUtils.hexString(keys[i], 8));
       builder.append(" -> :label_");
       builder.append(payloadUser.getOffset() + targets[i]);
diff --git a/src/main/java/com/android/tools/r8/graph/DexCode.java b/src/main/java/com/android/tools/r8/graph/DexCode.java
index 7915df3..8bd13f0 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCode.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCode.java
@@ -193,7 +193,7 @@
     int instructionNumber = 0;
     for (Instruction insn : instructions) {
       while (debugInfo != null && debugInfo.address == insn.getOffset()) {
-        builder.append("      ").append(debugInfo).append("\n");
+        builder.append("         ").append(debugInfo.toString(false)).append("\n");
         debugInfo = debugInfoIterator.hasNext() ? debugInfoIterator.next() : null;
       }
       StringUtils.appendLeftPadded(builder, Integer.toString(instructionNumber++), 5);
@@ -353,9 +353,9 @@
 
     public String toString() {
       return "["
-          + startAddress
+          + StringUtils.hexString(startAddress, 2)
           + " .. "
-          + (startAddress + instructionCount - 1)
+          + StringUtils.hexString(startAddress + instructionCount - 1, 2)
           + "] -> "
           + handlerIndex;
     }
@@ -420,12 +420,12 @@
         builder.append("       ");
         builder.append(pair.type);
         builder.append(" -> ");
-        builder.append(pair.addr);
+        builder.append(StringUtils.hexString(pair.addr, 2));
         builder.append("\n");
       }
       if (catchAllAddr != NO_HANDLER) {
         builder.append("       default -> ");
-        builder.append(catchAllAddr);
+        builder.append(StringUtils.hexString(catchAllAddr, 2));
         builder.append("\n");
       }
       builder.append("     ]");
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEntry.java b/src/main/java/com/android/tools/r8/graph/DexDebugEntry.java
index de7b544..f083794 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugEntry.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEntry.java
@@ -33,8 +33,15 @@
 
   @Override
   public String toString() {
+    return toString(true);
+  }
+
+  public String toString(boolean withPcPrefix) {
     StringBuilder builder = new StringBuilder();
-    builder.append("pc 0x").append(StringUtils.hexString(address, 2));
+    if (withPcPrefix) {
+      builder.append("pc ");
+    }
+    builder.append(StringUtils.hexString(address, 2));
     builder.append(", line ").append(line);
     if (sourceFile != null) {
       builder.append(", file ").append(sourceFile);
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 484354a..8d36ef7 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
@@ -258,23 +258,15 @@
   @Override
   public void buildPrelude(IRBuilder builder) {
     Map<Integer, MoveType> initializedLocals = new HashMap<>(node.localVariables.size());
+    // Record types for arguments.
+    recordArgumentTypes(initializedLocals);
+    // Add debug information for all locals at the initial label.
     if (initialLabel != null) {
       state.openLocals(initialLabel);
     }
-    int argumentRegister = 0;
-    if (!isStatic()) {
-      Type thisType = Type.getType(clazz.descriptor.toString());
-      int register = state.writeLocal(argumentRegister++, thisType);
-      builder.addThisArgument(register);
-      initializedLocals.put(register, moveType(thisType));
-    }
-    for (Type type : parameterTypes) {
-      MoveType moveType = moveType(type);
-      int register = state.writeLocal(argumentRegister, type);
-      builder.addNonThisArgument(register, moveType);
-      argumentRegister += moveType.requiredRegisters();
-      initializedLocals.put(register, moveType);
-    }
+    // Build the actual argument instructions now that type and debug information is known
+    // for arguments.
+    buildArgumentInstructions(builder);
     if (isSynchronized()) {
       generatingMethodSynchronization = true;
       Type clazzType = Type.getType(clazz.toDescriptorString());
@@ -297,6 +289,23 @@
     for (Object o : node.localVariables) {
       LocalVariableNode local = (LocalVariableNode) o;
       Type localType = Type.getType(local.desc);
+      int sort = localType.getSort();
+      switch (sort) {
+        case Type.OBJECT:
+        case Type.ARRAY:
+          localType = JarState.NULL_TYPE;
+          break;
+        case Type.DOUBLE:
+        case Type.LONG:
+        case Type.FLOAT:
+          break;
+        case Type.VOID:
+        case Type.METHOD:
+          throw new Unreachable("Invalid local variable type: " + localType);
+        default:
+          localType = Type.INT_TYPE;
+          break;
+      }
       int localRegister = state.getLocalRegister(local.index, localType);
       MoveType exitingLocalType = initializedLocals.get(localRegister);
       assert exitingLocalType == null || exitingLocalType == moveType(localType);
@@ -311,6 +320,36 @@
     state.setBuilding();
   }
 
+  private void buildArgumentInstructions(IRBuilder builder) {
+    int argumentRegister = 0;
+    if (!isStatic()) {
+      Type thisType = Type.getType(clazz.descriptor.toString());
+      Slot slot = state.readLocal(argumentRegister++, thisType);
+      builder.addThisArgument(slot.register);
+    }
+    for (Type type : parameterTypes) {
+      MoveType moveType = moveType(type);
+      Slot slot = state.readLocal(argumentRegister, type);
+      builder.addNonThisArgument(slot.register, moveType);
+      argumentRegister += moveType.requiredRegisters();
+    }
+  }
+
+  private void recordArgumentTypes(Map<Integer, MoveType> initializedLocals) {
+    int argumentRegister = 0;
+    if (!isStatic()) {
+      Type thisType = Type.getType(clazz.descriptor.toString());
+      int register = state.writeLocal(argumentRegister++, thisType);
+      initializedLocals.put(register, moveType(thisType));
+    }
+    for (Type type : parameterTypes) {
+      MoveType moveType = moveType(type);
+      int register = state.writeLocal(argumentRegister, type);
+      argumentRegister += moveType.requiredRegisters();
+      initializedLocals.put(register, moveType);
+    }
+  }
+
   private void computeBlockEntryJarStates(IRBuilder builder) {
     Int2ReferenceSortedMap<BlockInfo> CFG = builder.getCFG();
     Queue<JarStateWorklistItem> worklist = new LinkedList<>();
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 5009d5e..a6b1f2c 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
@@ -87,6 +87,9 @@
       if (type == BYTE_OR_BOOL_TYPE) {
         type = Type.BYTE_TYPE;
       }
+      if (other == BYTE_OR_BOOL_TYPE) {
+        other = Type.BYTE_TYPE;
+      }
       int sort = type.getSort();
       int otherSort = other.getSort();
       if (isReferenceCompatible(type, other)) {
@@ -256,7 +259,7 @@
     Collection<LocalVariableNode> nodes = localVariableStartPoints.get(label);
     ArrayList<Local> locals = new ArrayList<>(nodes.size());
     for (LocalVariableNode node : nodes) {
-      locals.add(setLocal(node.index, Type.getType(node.desc), localVariables.get(node)));
+      locals.add(setLocalInfo(node.index, Type.getType(node.desc), localVariables.get(node)));
     }
     // Sort to ensure deterministic instruction ordering (correctness is unaffected).
     locals.sort(Comparator.comparingInt(local -> local.slot.register));
@@ -335,6 +338,18 @@
     return local;
   }
 
+  private Local setLocalInfo(int index, Type type, DebugLocalInfo info) {
+    return setLocalInfoForRegister(getLocalRegister(index, type), type, info);
+  }
+
+  private Local setLocalInfoForRegister(int register, Type type, DebugLocalInfo info) {
+    Local existingLocal = getLocalForRegister(register);
+    Local local = new Local(existingLocal.slot, info);
+    locals[register] = local;
+    return local;
+  }
+
+
   public int writeLocal(int index, Type type) {
     assert nonNullType(type);
     Local local = getLocal(index, type);
@@ -344,8 +359,9 @@
     }
     // We cannot assume consistency for writes because we do not have complete information about the
     // scopes of locals. We assume the program to be verified and overwrite if the types mismatch.
-    if (local == null || (local.info == null && !typeEquals(local.slot.type, type))) {
-      local = setLocal(index, type, null);
+    if (local == null || !typeEquals(local.slot.type, type)) {
+      DebugLocalInfo info = local == null ? null : local.info;
+      local = setLocal(index, type, info);
     }
     return local.slot.register;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/BasicBlockInstructionsEquivalence.java b/src/main/java/com/android/tools/r8/ir/optimize/BasicBlockInstructionsEquivalence.java
index 27cf89a..9f833f0 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/BasicBlockInstructionsEquivalence.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/BasicBlockInstructionsEquivalence.java
@@ -5,19 +5,25 @@
 
 import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.CatchHandlers;
+import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.Instruction;
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.ir.regalloc.RegisterAllocator;
 import com.google.common.base.Equivalence;
+import java.util.Arrays;
+import java.util.Comparator;
 import java.util.List;
 
 class BasicBlockInstructionsEquivalence extends Equivalence<BasicBlock> {
-
+  private static final int UNKNOW_HASH = -1;
   private static final int MAX_HASH_INSTRUCTIONS = 5;
   private final RegisterAllocator allocator;
+  private final int[] hashes;
 
-  BasicBlockInstructionsEquivalence(RegisterAllocator allocator) {
+  BasicBlockInstructionsEquivalence(IRCode code, RegisterAllocator allocator) {
     this.allocator = allocator;
+    hashes = new int[code.getHighestBlockNumber() + 1];
+    Arrays.fill(hashes, UNKNOW_HASH);
   }
 
   private boolean hasIdenticalInstructions(BasicBlock first, BasicBlock second) {
@@ -60,8 +66,23 @@
     return hasIdenticalInstructions(a, b);
   }
 
+  void clearComputedHash(BasicBlock basicBlock) {
+    hashes[basicBlock.getNumber()] = UNKNOW_HASH;
+  }
+
   @Override
   protected int doHash(BasicBlock basicBlock) {
+    int hash = hashes[basicBlock.getNumber()];
+    if (hash != UNKNOW_HASH) {
+      assert hash == computeHash(basicBlock);
+      return hash;
+    }
+    hash = computeHash(basicBlock);
+    hashes[basicBlock.getNumber()] = hash;
+    return hash;
+  }
+
+  private int computeHash(BasicBlock basicBlock) {
     List<Instruction> instructions = basicBlock.getInstructions();
     int hash = instructions.size();
     for (int i = 0; i < instructions.size() && i < MAX_HASH_INSTRUCTIONS; i++) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/PeepholeOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/PeepholeOptimizer.java
index 4ad05cc..a7d6e8d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/PeepholeOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/PeepholeOptimizer.java
@@ -173,7 +173,7 @@
    */
   private static void removeIdenticalPredecessorBlocks(IRCode code, RegisterAllocator allocator) {
     BasicBlockInstructionsEquivalence equivalence =
-        new BasicBlockInstructionsEquivalence(allocator);
+        new BasicBlockInstructionsEquivalence(code, allocator);
     // Locate one block at a time that has identical predecessors. Rewrite those predecessors and
     // then start over. Restarting when one blocks predecessors have been rewritten simplifies
     // the rewriting and reduces the size of the data structures.
@@ -194,6 +194,7 @@
             BasicBlock otherPred = block.getPredecessors().get(otherPredIndex);
             pred.clearCatchHandlers();
             pred.getInstructions().clear();
+            equivalence.clearComputedHash(pred);
             for (BasicBlock succ : pred.getSuccessors()) {
               succ.removePredecessor(pred);
             }
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
index 632aa6f..32342fc 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
@@ -20,15 +20,16 @@
 import com.android.tools.r8.naming.signature.GenericSignatureParser;
 import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
+import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.InternalOptions.PackageObfuscationMode;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.Timing;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
-import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.function.Consumer;
@@ -42,7 +43,8 @@
 
   private final Map<DexType, DexString> renaming = Maps.newIdentityHashMap();
   private final Map<String, ClassNamingState> states = new HashMap<>();
-  private final List<String> dictionary;
+  private final ImmutableList<String> packageDictionary;
+  private final ImmutableList<String> classDictionary;
   private final boolean keepInnerClassStructure;
 
   private final ClassNamingState topLevelState;
@@ -55,18 +57,17 @@
   ClassNameMinifier(
       AppInfoWithLiveness appInfo,
       RootSet rootSet,
-      PackageObfuscationMode packageObfuscationMode,
-      String packagePrefix,
-      List<String> dictionary,
-      boolean keepInnerClassStructure) {
+      InternalOptions options) {
     this.appInfo = appInfo;
     this.rootSet = rootSet;
-    this.packageObfuscationMode = packageObfuscationMode;
-    this.dictionary = dictionary;
-    this.keepInnerClassStructure = keepInnerClassStructure;
+    this.packageObfuscationMode = options.proguardConfiguration.getPackageObfuscationMode();
+    this.packageDictionary = options.proguardConfiguration.getPackageObfuscationDictionary();
+    this.classDictionary = options.proguardConfiguration.getClassObfuscationDictionary();
+    this.keepInnerClassStructure = options.attributeRemoval.signature;
 
     // Initialize top-level naming state.
-    topLevelState = new ClassNamingState(getPackageBinaryNameFromJavaType(packagePrefix));
+    topLevelState = new ClassNamingState(getPackageBinaryNameFromJavaType(
+        options.proguardConfiguration.getPackagePrefix()));
     states.computeIfAbsent("", k -> topLevelState);
   }
 
@@ -270,7 +271,8 @@
           // L or La/b/ (or La/b/C$)
           + (packageName.isEmpty() ? "" : separator))
           .toCharArray();
-      this.dictionaryIterator = dictionary.iterator();
+      // TODO(b/36799686): general obfuscation should use packageDictionary when renaming package.
+      this.dictionaryIterator = classDictionary.iterator();
     }
 
     public char[] getPackagePrefix() {
diff --git a/src/main/java/com/android/tools/r8/naming/DictionaryReader.java b/src/main/java/com/android/tools/r8/naming/DictionaryReader.java
index 6c1e8e5..146d531 100644
--- a/src/main/java/com/android/tools/r8/naming/DictionaryReader.java
+++ b/src/main/java/com/android/tools/r8/naming/DictionaryReader.java
@@ -52,7 +52,7 @@
     }
   }
 
-  public static List<String> readAllNames(Path path) {
+  public static ImmutableList<String> readAllNames(Path path) {
     if (path != null) {
       Builder<String> namesBuilder = new ImmutableList.Builder<String>();
       try (DictionaryReader reader = new DictionaryReader(path);) {
diff --git a/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
index eec918f..ac65c5c 100644
--- a/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
@@ -10,9 +10,10 @@
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
+import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.Timing;
+import com.google.common.collect.ImmutableList;
 import java.util.IdentityHashMap;
-import java.util.List;
 import java.util.Map;
 
 class FieldNameMinifier {
@@ -20,13 +21,13 @@
   private final AppInfoWithSubtyping appInfo;
   private final RootSet rootSet;
   private final Map<DexField, DexString> renaming = new IdentityHashMap<>();
-  private final List<String> dictionary;
+  private final ImmutableList<String> dictionary;
   private final Map<DexType, NamingState<DexType>> states = new IdentityHashMap<>();
 
-  FieldNameMinifier(AppInfoWithSubtyping appInfo, RootSet rootSet, List<String> dictionary) {
+  FieldNameMinifier(AppInfoWithSubtyping appInfo, RootSet rootSet, InternalOptions options) {
     this.appInfo = appInfo;
     this.rootSet = rootSet;
-    this.dictionary = dictionary;
+    this.dictionary = options.proguardConfiguration.getObfuscationDictionary();
   }
 
   Map<DexField, DexString> computeRenaming(Timing timing) {
diff --git a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
index 86670b8..4fa3809 100644
--- a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
@@ -3,7 +3,9 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.naming;
 
+import com.android.tools.r8.utils.InternalOptions;
 import com.google.common.base.Equivalence.Wrapper;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Sets;
 
 import com.android.tools.r8.graph.AppInfoWithSubtyping;
@@ -91,13 +93,12 @@
   private final Map<DexType, NamingState<DexProto>> states = new IdentityHashMap<>();
   private final NamingState<DexProto> globalState;
   private MethodSignatureEquivalence equivalence = MethodSignatureEquivalence.get();
-  private final List<String> dictionary;
+  private final ImmutableList<String> dictionary;
 
-  MethodNameMinifier(AppInfoWithSubtyping appInfo, RootSet rootSet,
-      List<String> dictionary) {
+  MethodNameMinifier(AppInfoWithSubtyping appInfo, RootSet rootSet, InternalOptions options) {
     this.appInfo = appInfo;
     this.rootSet = rootSet;
-    this.dictionary = dictionary;
+    this.dictionary = options.proguardConfiguration.getObfuscationDictionary();
     this.globalState = NamingState.createRoot(appInfo.dexItemFactory, dictionary);
   }
 
diff --git a/src/main/java/com/android/tools/r8/naming/Minifier.java b/src/main/java/com/android/tools/r8/naming/Minifier.java
index b1a15b6..951790c 100644
--- a/src/main/java/com/android/tools/r8/naming/Minifier.java
+++ b/src/main/java/com/android/tools/r8/naming/Minifier.java
@@ -36,29 +36,21 @@
 
   public NamingLens run(Timing timing) {
     assert !options.skipMinification;
-    if (!options.allowAccessModification) {
+    // TODO(b/62048823): Minifier should not depend on -allowaccessmodification.
+    if (!options.proguardConfiguration.isAccessModificationAllowed()) {
       throw new CompilationError("Minification requires allowaccessmodification.");
     }
     timing.begin("MinifyClasses");
     Map<DexType, DexString> classRenaming =
-        new ClassNameMinifier(
-            appInfo,
-            rootSet,
-            options.packageObfuscationMode,
-            options.packagePrefix,
-            options.classObfuscationDictionary,
-            options.attributeRemoval.signature)
-            .computeRenaming(timing);
+        new ClassNameMinifier(appInfo, rootSet, options).computeRenaming(timing);
     timing.end();
     timing.begin("MinifyMethods");
     Map<DexMethod, DexString> methodRenaming =
-        new MethodNameMinifier(appInfo, rootSet, options.obfuscationDictionary)
-            .computeRenaming(timing);
+        new MethodNameMinifier(appInfo, rootSet, options).computeRenaming(timing);
     timing.end();
     timing.begin("MinifyFields");
     Map<DexField, DexString> fieldRenaming =
-        new FieldNameMinifier(appInfo, rootSet, options.obfuscationDictionary)
-            .computeRenaming(timing);
+        new FieldNameMinifier(appInfo, rootSet, options).computeRenaming(timing);
     timing.end();
     return new MinifiedRenaming(classRenaming, methodRenaming, fieldRenaming, appInfo);
   }
diff --git a/src/main/java/com/android/tools/r8/naming/NamingState.java b/src/main/java/com/android/tools/r8/naming/NamingState.java
index 0474c1a..f42af78 100644
--- a/src/main/java/com/android/tools/r8/naming/NamingState.java
+++ b/src/main/java/com/android/tools/r8/naming/NamingState.java
@@ -8,6 +8,7 @@
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.utils.StringUtils;
 import com.google.common.collect.HashBiMap;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Sets;
 import java.util.IdentityHashMap;
 import java.util.Iterator;
@@ -15,19 +16,22 @@
 import java.util.Map;
 import java.util.Set;
 
-public class NamingState<T extends CanonicalizedDexItem> {
+class NamingState<T extends CanonicalizedDexItem> {
 
   private final NamingState<T> parent;
   private final Map<T, InternalState> usedNames = new IdentityHashMap<>();
   private final DexItemFactory itemFactory;
-  private final List<String> dictionary;
+  private final ImmutableList<String> dictionary;
 
-  public static <T extends CanonicalizedDexItem> NamingState<T> createRoot(
-      DexItemFactory itemFactory, List<String> dictionary) {
+  static <T extends CanonicalizedDexItem> NamingState<T> createRoot(
+      DexItemFactory itemFactory, ImmutableList<String> dictionary) {
     return new NamingState<>(null, itemFactory, dictionary);
   }
 
-  private NamingState(NamingState<T> parent, DexItemFactory itemFactory, List<String> dictionary) {
+  private NamingState(
+      NamingState<T> parent,
+      DexItemFactory itemFactory,
+      ImmutableList<String> dictionary) {
     this.parent = parent;
     this.itemFactory = itemFactory;
     this.dictionary = dictionary;
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
index 642b459..93c4dbb 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
@@ -31,7 +31,7 @@
     private boolean printUsage = false;
     private Path printUsageFile;
     private boolean printMapping;
-    private Path printMappingOutput;
+    private Path printMappingFile;
     private boolean verbose = false;
     private final List<String> attributesRemovalPatterns = new ArrayList<>();
     private final Set<ProguardTypeMatcher> dontWarnPatterns = new HashSet<>();
@@ -106,8 +106,8 @@
       this.printMapping = printMapping;
     }
 
-    public void setPrintMappingOutput(Path file) {
-      this.printMappingOutput = file;
+    public void setPrintMappingFile(Path file) {
+      this.printMappingFile = file;
     }
 
     public void setVerbose(boolean verbose) {
@@ -130,7 +130,7 @@
       this.seedFile = seedFile;
     }
 
-    public void setPrintSeed(boolean printSeeds) {
+    public void setPrintSeeds(boolean printSeeds) {
       this.printSeeds = printSeeds;
     }
 
@@ -161,7 +161,7 @@
           printUsage,
           printUsageFile,
           printMapping,
-          printMappingOutput,
+          printMappingFile,
           verbose,
           attributesRemovalPatterns,
           dontWarnPatterns,
@@ -175,8 +175,8 @@
   }
 
   private final DexItemFactory dexItemFactory;
-  private final List<Path> injars;
-  private final List<Path> libraryjars;
+  private final ImmutableList<Path> injars;
+  private final ImmutableList<Path> libraryjars;
   private final PackageObfuscationMode packageObfuscationMode;
   private final String packagePrefix;
   private final boolean allowAccessModification;
@@ -187,16 +187,16 @@
   private final boolean printUsage;
   private final Path printUsageFile;
   private final boolean printMapping;
-  private final Path printMappingOutput;
+  private final Path printMappingFile;
   private final boolean verbose;
-  private final List<String> attributesRemovalPatterns;
+  private final ImmutableList<String> attributesRemovalPatterns;
   private final ImmutableSet<ProguardTypeMatcher> dontWarnPatterns;
   protected final ImmutableList<ProguardConfigurationRule> rules;
   private final boolean printSeeds;
   private final Path seedFile;
-  private final List<String> obfuscationDictionary;
-  private final List<String> classObfuscationDictionary;
-  private final List<String> packageObfuscationDictionary;
+  private final ImmutableList<String> obfuscationDictionary;
+  private final ImmutableList<String> classObfuscationDictionary;
+  private final ImmutableList<String> packageObfuscationDictionary;
 
   private ProguardConfiguration(
       DexItemFactory factory,
@@ -212,16 +212,16 @@
       boolean printUsage,
       Path printUsageFile,
       boolean printMapping,
-      Path printMappingOutput,
+      Path printMappingFile,
       boolean verbose,
       List<String> attributesRemovalPatterns,
       Set<ProguardTypeMatcher> dontWarnPatterns,
       List<ProguardConfigurationRule> rules,
       boolean printSeeds,
       Path seedFile,
-      List<String> obfuscationDictionary,
-      List<String> classObfuscationDictionary,
-      List<String> packageObfuscationDictionary) {
+      ImmutableList<String> obfuscationDictionary,
+      ImmutableList<String> classObfuscationDictionary,
+      ImmutableList<String> packageObfuscationDictionary) {
     this.dexItemFactory = factory;
     this.injars = ImmutableList.copyOf(injars);
     this.libraryjars = ImmutableList.copyOf(libraryjars);
@@ -235,7 +235,7 @@
     this.printUsage = printUsage;
     this.printUsageFile = printUsageFile;
     this.printMapping = printMapping;
-    this.printMappingOutput = printMappingOutput;
+    this.printMappingFile = printMappingFile;
     this.verbose = verbose;
     this.attributesRemovalPatterns = ImmutableList.copyOf(attributesRemovalPatterns);
     this.dontWarnPatterns = ImmutableSet.copyOf(dontWarnPatterns);
@@ -262,11 +262,11 @@
     return false;
   }
 
-  public List<Path> getInjars() {
+  public ImmutableList<Path> getInjars() {
     return injars;
   }
 
-  public List<Path> getLibraryjars() {
+  public ImmutableList<Path> getLibraryjars() {
     return libraryjars;
   }
 
@@ -278,16 +278,16 @@
     return packagePrefix;
   }
 
-  public boolean getAllowAccessModification() {
+  public boolean isAccessModificationAllowed() {
     return allowAccessModification;
   }
 
-  public boolean isPrintingMapping() {
+  public boolean isPrintMapping() {
     return printMapping;
   }
 
-  public Path getPrintMappingOutput() {
-    return printMappingOutput;
+  public Path getPrintMappingFile() {
+    return printMappingFile;
   }
 
   public boolean isIgnoreWarnings() {
@@ -318,7 +318,7 @@
     return verbose;
   }
 
-  public List<String> getAttributesRemovalPatterns() {
+  public ImmutableList<String> getAttributesRemovalPatterns() {
     return attributesRemovalPatterns;
   }
 
@@ -330,21 +330,20 @@
     return rules;
   }
 
-  public List<String> getObfuscationDictionary() {
+  public ImmutableList<String> getObfuscationDictionary() {
     return obfuscationDictionary;
   }
 
-  public List<String> getPackageObfuscationDictionary() {
-    return packageObfuscationDictionary;
-  }
-
-  public List<String> getClassObfuscationDictionary() {
+  public ImmutableList<String> getClassObfuscationDictionary() {
     return classObfuscationDictionary;
   }
 
+  public ImmutableList<String> getPackageObfuscationDictionary() {
+    return packageObfuscationDictionary;
+  }
+
   public static ProguardConfiguration defaultConfiguration(DexItemFactory dexItemFactory) {
-    ProguardConfiguration config = new DefaultProguardConfiguration(dexItemFactory);
-    return config;
+    return new DefaultProguardConfiguration(dexItemFactory);
   }
 
   public static class DefaultProguardConfiguration extends ProguardConfiguration {
@@ -372,7 +371,7 @@
           null                  /* seedFile */,
           ImmutableList.of()     /* obfuscationDictionary */,
           ImmutableList.of()     /* classObfuscationDictionary */,
-          ImmutableList.of()     /* packageObfucationDictionary */);
+          ImmutableList.of()     /* packageObfuscationDictionary */);
     }
 
     @Override
@@ -381,7 +380,7 @@
     }
   }
 
-  public boolean getPrintSeeds() {
+  public boolean isPrintSeeds() {
     return printSeeds;
   }
 
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 0486d52..5b34221 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -76,6 +76,10 @@
     configurationBuilder = ProguardConfiguration.builder(dexItemFactory);
   }
 
+  public ProguardConfiguration.Builder getConfigurationBuilder() {
+    return configurationBuilder;
+  }
+
   public ProguardConfiguration getConfig() {
     return configurationBuilder.build();
   }
@@ -214,7 +218,7 @@
         configurationBuilder.setPrintMapping(true);
         skipWhitespace();
         if (isOptionalArgumentGiven()) {
-          configurationBuilder.setPrintMappingOutput(parseFileName());
+          configurationBuilder.setPrintMappingFile(parseFileName());
         }
       } else if (acceptString("assumenosideeffects")) {
         ProguardAssumeNoSideEffectRule rule = parseAssumeNoSideEffectsRule();
@@ -233,7 +237,7 @@
       } else if (acceptString("libraryjars")) {
         configurationBuilder.addLibraryJars(parseClassPath());
       } else if (acceptString("printseeds")) {
-        configurationBuilder.setPrintSeed(true);
+        configurationBuilder.setPrintSeeds(true);
         skipWhitespace();
         if (isOptionalArgumentGiven()) {
           configurationBuilder.setSeedFile(parseFileName());
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 d8fd6c2..58646a8 100644
--- a/src/main/java/com/android/tools/r8/shaking/TreePruner.java
+++ b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
@@ -30,7 +30,8 @@
     this.application = application;
     this.appInfo = appInfo;
     this.options = options;
-    this.usagePrinter = options.printUsage ? new UsagePrinter() : UsagePrinter.DONT_PRINT;
+    this.usagePrinter = options.proguardConfiguration.isPrintUsage()
+        ? new UsagePrinter() : UsagePrinter.DONT_PRINT;
   }
 
   public DexApplication run() throws IOException {
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 70a2e53..b03721f 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -9,10 +9,9 @@
 import com.android.tools.r8.errors.InvalidDebugInfoException;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.shaking.ProguardConfiguration;
 import com.android.tools.r8.shaking.ProguardConfigurationRule;
-import com.android.tools.r8.shaking.ProguardTypeMatcher;
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
 import java.nio.file.Path;
 import java.util.List;
 import java.util.function.Function;
@@ -20,14 +19,26 @@
 public class InternalOptions {
 
   public final DexItemFactory itemFactory;
+  public final ProguardConfiguration proguardConfiguration;
 
+  // Constructor for testing and/or other utilities.
   public InternalOptions() {
     itemFactory = new DexItemFactory();
+    proguardConfiguration = ProguardConfiguration.defaultConfiguration(itemFactory);
   }
 
+  // Constructor for D8.
   public InternalOptions(DexItemFactory factory) {
     assert factory != null;
     itemFactory = factory;
+    proguardConfiguration = ProguardConfiguration.defaultConfiguration(itemFactory);
+  }
+
+  // Constructor for R8.
+  public InternalOptions(ProguardConfiguration proguardConfiguration) {
+    assert proguardConfiguration != null;
+    this.proguardConfiguration = proguardConfiguration;
+    itemFactory = proguardConfiguration.getDexItemFactory();
   }
 
   public final int NOT_SPECIFIED = -1;
@@ -80,21 +91,12 @@
   public OutputMode outputMode = OutputMode.Indexed;
 
   public boolean useTreeShaking = true;
-  public boolean printUsage = false;
-  public Path printUsageFile = null;
 
   public boolean printCfg = false;
   public String printCfgFile;
-  public boolean printSeeds;
-  public Path seedsFile;
-  public boolean printMapping;
-  public Path printMappingFile;
   public Path printMainDexListFile;
   public boolean ignoreMissingClasses = false;
   public boolean skipMinification = false;
-  public PackageObfuscationMode packageObfuscationMode = PackageObfuscationMode.NONE;
-  public String packagePrefix = "";
-  public boolean allowAccessModification = true;
   public boolean inlineAccessors = true;
   public boolean removeSwitchMaps = true;
   public boolean disableAssertions = true;
@@ -107,14 +109,8 @@
   public boolean singleStepDebug = false;
   public final TestingOptions testing = new TestingOptions();
 
-  // TODO(zerny): These stateful dictionaries do not belong here.
-  public List<String> classObfuscationDictionary = ImmutableList.of();
-  public List<String> obfuscationDictionary = ImmutableList.of();
-
   public ImmutableList<ProguardConfigurationRule> mainDexKeepRules = ImmutableList.of();
   public boolean minimalMainDex;
-  public ImmutableList<ProguardConfigurationRule> keepRules = ImmutableList.of();
-  public ImmutableSet<ProguardTypeMatcher> dontWarnPatterns = ImmutableSet.of();
 
   public String warningInvalidParameterAnnotations = null;
 
@@ -200,8 +196,8 @@
 
   public static class TestingOptions {
 
-    public Function<List<DexEncodedMethod>, List<DexEncodedMethod>> irOrdering
-        = Function.identity();
+    public Function<List<DexEncodedMethod>, List<DexEncodedMethod>> irOrdering =
+        Function.identity();
   }
 
   public static class AttributeRemovalOptions {
diff --git a/src/main/java/com/android/tools/r8/utils/StringUtils.java b/src/main/java/com/android/tools/r8/utils/StringUtils.java
index 8404703..ea81b10 100644
--- a/src/main/java/com/android/tools/r8/utils/StringUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/StringUtils.java
@@ -158,11 +158,11 @@
     assert(0 <= width && width <= 8);
     String hex = Integer.toHexString(value);
     if (value >= 0) {
-      return zeroPrefixString(hex, width);
+      return "0x" + zeroPrefixString(hex, width);
     } else {
       // Negative ints are always formatted as 8 characters.
       assert(hex.length() == 8);
-      return hex;
+      return "0x" + hex;
     }
   }
 
@@ -170,11 +170,11 @@
     assert(0 <= width && width <= 16);
     String hex = Long.toHexString(value);
     if (value >= 0) {
-      return zeroPrefixString(hex, width);
+      return "0x" + zeroPrefixString(hex, width);
     } else {
       // Negative longs are always formatted as 16 characters.
       assert(hex.length() == 16);
-      return hex;
+      return "0x" + hex;
     }
   }
 
diff --git a/src/test/java/com/android/tools/r8/classmerging/ClassMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/ClassMergingTest.java
index 0d0d6f0..7bd18e0 100644
--- a/src/test/java/com/android/tools/r8/classmerging/ClassMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/ClassMergingTest.java
@@ -36,7 +36,6 @@
             .addProguardConfigurationFiles(EXAMPLE_KEEP)
             .setMinification(false)
             .build(), o -> {
-          o.allowAccessModification = false;
           o.skipClassMerging = false;
         });
     inspector = new DexInspector(
diff --git a/src/test/java/com/android/tools/r8/internal/CompilationTestBase.java b/src/test/java/com/android/tools/r8/internal/CompilationTestBase.java
index 10947a4..0b3726c 100644
--- a/src/test/java/com/android/tools/r8/internal/CompilationTestBase.java
+++ b/src/test/java/com/android/tools/r8/internal/CompilationTestBase.java
@@ -82,14 +82,11 @@
         builder.addProguardConfigurationFiles(Paths.get(pgConf));
       }
       builder.setMode(mode);
-      outputApp = ToolHelper.runR8(builder.build(),
-                  options -> {
-                    options.printSeeds = false;
-                    options.minApiLevel = Constants.ANDROID_L_API;
-                    if (optionsConsumer != null) {
-                      optionsConsumer.accept(options);
-                    }
-                  });
+      builder.setMinApiLevel(Constants.ANDROID_L_API);
+      builder.addProguardConfigurationConsumer(b -> {
+        b.setPrintSeeds(false);
+      });
+      outputApp = ToolHelper.runR8(builder.build(), optionsConsumer);
     } else {
       assert compiler == CompilerUnderTest.D8;
       outputApp =
diff --git a/src/test/java/com/android/tools/r8/jasmin/DebugLocalTests.java b/src/test/java/com/android/tools/r8/jasmin/DebugLocalTests.java
index 72bbaed..2782c91 100644
--- a/src/test/java/com/android/tools/r8/jasmin/DebugLocalTests.java
+++ b/src/test/java/com/android/tools/r8/jasmin/DebugLocalTests.java
@@ -183,9 +183,9 @@
         "MethodStart:",
         ".line 1",
 
-        "LabelXStart:",
         "  ldc 0",
         "  istore 1",
+        "LabelXStart:",
         ".line 2",
         "  invokestatic Test/ensureLine()V",
         "LabelXEnd:",
diff --git a/src/test/java/com/android/tools/r8/jasmin/Regress64658224.java b/src/test/java/com/android/tools/r8/jasmin/Regress64658224.java
new file mode 100644
index 0000000..8c1df56
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/jasmin/Regress64658224.java
@@ -0,0 +1,50 @@
+// 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.jasmin;
+
+import static org.junit.Assert.assertEquals;
+
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+
+public class Regress64658224 extends JasminTestBase {
+
+  @Test
+  public void testInvalidTypeInfoFromLocals() throws Exception {
+    JasminBuilder builder = new JasminBuilder();
+    JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+    clazz.addStaticMethod("foo", ImmutableList.of("I"), "V",
+        ".limit stack 2",
+        ".limit locals 2",
+        ".var 1 is x Ljava/lang/Object; from L1 to L2",
+        "  aconst_null",
+        "  astore 1",
+        "L1:",
+        "  iload 0",
+        "  ifeq L3",
+        "L2:",
+        "  goto L5",
+        "L3:",
+        "  aload 1",
+        "  iconst_0",
+        "  aaload",
+        "  pop",
+        "L5:",
+        "  return");
+
+    clazz.addMainMethod(
+        ".limit stack 1",
+        ".limit locals 1",
+        "  ldc 2",
+        "  invokestatic Test/foo(I)V",
+        "  return");
+
+    String expected = "";
+    String javaResult = runOnJava(builder, clazz.name);
+    assertEquals(expected, javaResult);
+    String artResult = runOnArtD8(builder, clazz.name);
+    assertEquals(expected, artResult);
+  }
+}
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 7a81186..8818bbb 100644
--- a/src/test/java/com/android/tools/r8/naming/NamingTestBase.java
+++ b/src/test/java/com/android/tools/r8/naming/NamingTestBase.java
@@ -64,10 +64,9 @@
       throws IOException, ProguardRuleParserException, ExecutionException {
     ProguardConfiguration configuration =
         ToolHelper.loadProguardConfiguration(dexItemFactory, configPaths);
-    InternalOptions options = new InternalOptions();
-    copyProguardConfigurationToInternalOptions(configuration, options);
+    InternalOptions options = new InternalOptions(configuration);
 
-    if (options.allowAccessModification) {
+    if (options.proguardConfiguration.isAccessModificationAllowed()) {
       ClassAndMemberPublicizer.run(program);
     }
 
@@ -78,16 +77,6 @@
     return new Minifier(appInfo.withLiveness(), rootSet, options).run(timing);
   }
 
-  private void copyProguardConfigurationToInternalOptions(
-      ProguardConfiguration config, InternalOptions options) {
-    options.packageObfuscationMode = config.getPackageObfuscationMode();
-    options.packagePrefix = config.getPackagePrefix();
-    options.allowAccessModification = config.getAllowAccessModification();
-    options.classObfuscationDictionary = config.getClassObfuscationDictionary();
-    options.obfuscationDictionary = config.getObfuscationDictionary();
-    options.keepRules = config.getRules();
-  }
-
   static <T> Collection<Object[]> createTests(List<String> tests, Map<String, T> inspections) {
     List<Object[]> testCases = new ArrayList<>();
     Set<String> usedInspections = new HashSet<>();
diff --git a/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java b/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java
index 3138f4e..4cb6736 100644
--- a/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java
@@ -71,13 +71,15 @@
             .setOutputPath(out)
             .addProgramFiles(Paths.get(programFile))
             .addProguardConfigurationFiles(ListUtils.map(keepRulesFiles, Paths::get))
+            .addProguardConfigurationConsumer(builder -> {
+              builder.setPrintUsage(true);
+              builder.setPrintUsageFile(out.resolve(test + PRINT_USAGE_FILE_SUFFIX));
+            })
             .addLibraryFiles(Paths.get(ANDROID_JAR))
             .build();
     ToolHelper.runR8(command, options -> {
       // Disable inlining to make this test not depend on inlining decisions.
       options.inlineAccessors = false;
-      options.printUsage = true;
-      options.printUsageFile = out.resolve(test + PRINT_USAGE_FILE_SUFFIX);
     });
   }
 
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 6f2a347..28722af 100644
--- a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
@@ -384,7 +384,7 @@
     ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
     parser.parse(Paths.get(SEEDS));
     ProguardConfiguration config = parser.getConfig();
-    assertTrue(config.getPrintSeeds());
+    assertTrue(config.isPrintSeeds());
     assertNull(config.getSeedFile());
   }
 
@@ -393,7 +393,7 @@
     ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
     parser.parse(Paths.get(SEEDS_2));
     ProguardConfiguration config = parser.getConfig();
-    assertTrue(config.getPrintSeeds());
+    assertTrue(config.isPrintSeeds());
     assertNotNull(config.getSeedFile());
   }
 
diff --git a/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java b/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
index 99be74a..4c1fc69 100644
--- a/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
@@ -130,13 +130,15 @@
             .setOutputPath(out)
             .addProgramFiles(Paths.get(programFile))
             .addProguardConfigurationFiles(ListUtils.map(keepRulesFiles, Paths::get))
+            .addProguardConfigurationConsumer(builder -> {
+              builder.setPrintMapping(true);
+              builder.setPrintMappingFile(out.resolve(AndroidApp.DEFAULT_PROGUARD_MAP_FILE));
+            })
             .addLibraryFiles(JAR_LIBRARIES)
             .setMinification(minify)
             .build();
     ToolHelper.runR8(command, options -> {
       options.inlineAccessors = inline;
-      options.printMapping = true;
-      options.printMappingFile = out.resolve(AndroidApp.DEFAULT_PROGUARD_MAP_FILE);
     });
   }
 
diff --git a/third_party/framework.tar.gz.sha1 b/third_party/framework.tar.gz.sha1
index 7c11eec..675bb98 100644
--- a/third_party/framework.tar.gz.sha1
+++ b/third_party/framework.tar.gz.sha1
@@ -1 +1 @@
-d36913d75cde7c1293959698c45cf62342c709e0
\ No newline at end of file
+58224fa0baf2e9bd995918db690c35b2200c83a2
\ No newline at end of file
diff --git a/tools/test.py b/tools/test.py
index 8e016c7..ccc36d8 100755
--- a/tools/test.py
+++ b/tools/test.py
@@ -58,6 +58,9 @@
   result.add_option('--disable_assertions',
       help="Disable assertions when running tests.",
       default=False, action='store_true')
+  result.add_option('--with_code_coverage',
+      help="Enable code coverage with Jacoco.",
+      default=False, action='store_true')
 
   return result.parse_args()
 
@@ -72,10 +75,12 @@
 
 def Main():
   (options, args) = ParseOptions()
-  gradle_args = ['cleanTest', 'test']
   if len(args) > 1:
     print("test.py takes at most one argument, the pattern for tests to run")
     return -1
+
+  gradle_args = []
+  # Set all necessary Gradle properties and options first.
   if options.verbose:
     gradle_args.append('-Pprint_test_stdout')
   if options.no_internal:
@@ -96,9 +101,8 @@
     gradle_args.append('-Pjctf_compile_only')
   if options.disable_assertions:
     gradle_args.append('-Pdisable_assertions')
-  if len(args) > 0:
-    gradle_args.append('--tests')
-    gradle_args.append(args[0])
+  if options.with_code_coverage:
+    gradle_args.append('-Pwith_code_coverage')
   if os.name == 'nt':
     # temporary hack
     gradle_args.append('-Pno_internal')
@@ -108,6 +112,19 @@
     gradle_args.append('jctfCommonJar')
     gradle_args.append('-x')
     gradle_args.append('jctfTestsClasses')
+
+  # Add Gradle tasks
+  gradle_args.append('cleanTest')
+  gradle_args.append('test')
+  if len(args) > 0:
+    # Test filtering. Must always follow the 'test' task.
+    gradle_args.append('--tests')
+    gradle_args.append(args[0])
+  if options.with_code_coverage:
+    # Create Jacoco report after tests.
+    gradle_args.append('jacocoTestReport')
+
+  # Now run tests on selected runtime(s).
   vms_to_test = [options.dex_vm] if options.dex_vm != "all" else ALL_ART_VMS
   for art_vm in vms_to_test:
     return_code = gradle.RunGradle(gradle_args + ['-Pdex_vm=%s' % art_vm],
diff --git a/tools/test_framework.py b/tools/test_framework.py
index a153847..32f0bc7 100755
--- a/tools/test_framework.py
+++ b/tools/test_framework.py
@@ -34,7 +34,7 @@
 GOYT_EXE = os.path.join('third_party', 'goyt',
     'goyt_164843480')
 FRAMEWORK_JAR = os.path.join('third_party', 'framework',
-    'framework_160115954.jar')
+    'framework_14082017_desugared.jar')
 MIN_SDK_VERSION = '24'
 
 def parse_arguments():
@@ -43,7 +43,7 @@
           ' third_party/framework/framework*.jar.'
           ' Report Golem-compatible CodeSize and RunTimeRaw values.')
   parser.add_argument('--tool',
-      choices = ['dx', 'd8', 'd8-release', 'goyt'],
+      choices = ['dx', 'd8', 'd8-release', 'goyt', 'goyt-release'],
       required = True,
       help = 'Compiler tool to use.')
   parser.add_argument('--name',
@@ -65,13 +65,15 @@
 
   with utils.TempDir() as temp_dir:
 
-    if args.tool in ['dx', 'goyt']:
+    if args.tool in ['dx', 'goyt', 'goyt-release']:
       tool_args = ['--dex', '--output=' + temp_dir, '--multi-dex',
           '--min-sdk-version=' + MIN_SDK_VERSION]
 
-    if args.tool == 'goyt':
+    if args.tool.startswith('goyt'):
       tool_file = GOYT_EXE
       tool_args = ['--num-threads=8'] + tool_args
+      if args.tool == 'goyt-release':
+        tool_args.append('--no-locals')
     elif args.tool == 'dx':
       tool_file = DX_JAR
     else: