Merge "Update framework.jar used for benchmarking to a new version"
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/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/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/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 89f0f3b..03df65c 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);
     });
   }