Merge "Use more robust command for getting branches that a commit is on"
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index c6fcc22..39e0342 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -31,8 +31,8 @@
 import com.android.tools.r8.shaking.DiscardedChecker;
 import com.android.tools.r8.shaking.Enqueuer;
 import com.android.tools.r8.shaking.MainDexListBuilder;
+import com.android.tools.r8.shaking.ProguardClassNameList;
 import com.android.tools.r8.shaking.ProguardRuleParserException;
-import com.android.tools.r8.shaking.ProguardTypeMatcher;
 import com.android.tools.r8.shaking.ProguardTypeMatcher.MatchSpecificType;
 import com.android.tools.r8.shaking.ReasonPrinter;
 import com.android.tools.r8.shaking.RootSetBuilder;
@@ -153,15 +153,15 @@
   }
 
   private Set<DexType> filterMissingClasses(Set<DexType> missingClasses,
-      Set<ProguardTypeMatcher> dontWarnPatterns) {
+      ProguardClassNameList dontWarnPatterns) {
     Set<DexType> result = new HashSet<>(missingClasses);
-    for (ProguardTypeMatcher matcher : dontWarnPatterns) {
+    dontWarnPatterns.forEachTypeMatcher(matcher -> {
       if (matcher instanceof MatchSpecificType) {
         result.remove(((MatchSpecificType) matcher).type);
       } else {
         result.removeIf(matcher::matches);
       }
-    }
+    });
     return result;
   }
 
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 ac65c5c..1f6b540 100644
--- a/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
@@ -16,33 +16,24 @@
 import java.util.IdentityHashMap;
 import java.util.Map;
 
-class FieldNameMinifier {
-
-  private final AppInfoWithSubtyping appInfo;
-  private final RootSet rootSet;
-  private final Map<DexField, DexString> renaming = new IdentityHashMap<>();
-  private final ImmutableList<String> dictionary;
-  private final Map<DexType, NamingState<DexType>> states = new IdentityHashMap<>();
+class FieldNameMinifier extends MemberNameMinifier<DexField, DexType> {
 
   FieldNameMinifier(AppInfoWithSubtyping appInfo, RootSet rootSet, InternalOptions options) {
-    this.appInfo = appInfo;
-    this.rootSet = rootSet;
-    this.dictionary = options.proguardConfiguration.getObfuscationDictionary();
+    super(appInfo, rootSet, options);
   }
 
   Map<DexField, DexString> computeRenaming(Timing timing) {
-    NamingState<DexType> rootState = NamingState.createRoot(appInfo.dexItemFactory, dictionary);
     // Reserve names in all classes first. We do this in subtyping order so we do not
     // shadow a reserved field in subclasses. While there is no concept of virtual field
     // dispatch in Java, field resolution still traverses the super type chain and external
     // code might use a subtype to reference the field.
     timing.begin("reserve-classes");
-    reserveNamesInSubtypes(appInfo.dexItemFactory.objectType, rootState);
+    reserveNamesInSubtypes(appInfo.dexItemFactory.objectType, globalState);
     timing.end();
     // Next, reserve field names in interfaces. These should only be static.
     timing.begin("reserve-interfaces");
     DexType.forAllInterfaces(appInfo.dexItemFactory,
-        iface -> reserveNamesInSubtypes(iface, rootState));
+        iface -> reserveNamesInSubtypes(iface, globalState));
     timing.end();
     // Now rename the rest.
     timing.begin("rename");
@@ -57,7 +48,7 @@
     if (holder == null) {
       return;
     }
-    NamingState<DexType> newState = states.computeIfAbsent(type, t -> state.createChild());
+    NamingState<DexType> newState = computeStateIfAbsent(type, t -> state.createChild());
     holder.forEachField(field -> reserveFieldName(field, newState, holder.isLibraryClass()));
     type.forAllExtendsSubtypes(subtype -> reserveNamesInSubtypes(subtype, newState));
   }
@@ -77,7 +68,7 @@
     if (clazz == null) {
       return;
     }
-    NamingState<DexType> state = states.get(clazz.type);
+    NamingState<DexType> state = getState(clazz.type);
     assert state != null;
     clazz.forEachField(field -> renameField(field, state));
     type.forAllExtendsSubtypes(this::renameFieldsInSubtypes);
diff --git a/src/main/java/com/android/tools/r8/naming/MemberNameMinifier.java b/src/main/java/com/android/tools/r8/naming/MemberNameMinifier.java
new file mode 100644
index 0000000..37cdb34
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/MemberNameMinifier.java
@@ -0,0 +1,45 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.naming;
+
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.CachedHashValueDexItem;
+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.google.common.collect.ImmutableList;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.function.Function;
+
+abstract class MemberNameMinifier<MemberType, StateType extends CachedHashValueDexItem> {
+
+  protected final AppInfoWithSubtyping appInfo;
+  protected final RootSet rootSet;
+  protected final ImmutableList<String> dictionary;
+
+  protected final Map<MemberType, DexString> renaming = new IdentityHashMap<>();
+  protected final Map<DexType, NamingState<StateType>> states = new IdentityHashMap<>();
+  protected final NamingState<StateType> globalState;
+  protected final boolean useUniqueMemberNames;
+
+  MemberNameMinifier(AppInfoWithSubtyping appInfo, RootSet rootSet, InternalOptions options) {
+    this.appInfo = appInfo;
+    this.rootSet = rootSet;
+    this.dictionary = options.proguardConfiguration.getObfuscationDictionary();
+
+    this.globalState = NamingState.createRoot(appInfo.dexItemFactory, dictionary);
+    this.useUniqueMemberNames = options.proguardConfiguration.isUseUniqueClassMemberNames();
+  }
+
+  protected NamingState<StateType> computeStateIfAbsent(
+      DexType type, Function<DexType, NamingState<StateType>> f) {
+    return useUniqueMemberNames ? globalState : states.computeIfAbsent(type, f);
+  }
+
+  protected NamingState<StateType> getState(DexType type) {
+    return useUniqueMemberNames ? globalState : states.get(type);
+  }
+}
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 4fa3809..276c14b 100644
--- a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
@@ -86,20 +86,12 @@
  * TODO(herhut): Currently, we do not minify members of annotation interfaces, as this would require
  * parsing and minification of the string arguments to annotations.
  */
-class MethodNameMinifier {
+class MethodNameMinifier extends MemberNameMinifier<DexMethod, DexProto> {
 
-  private final AppInfoWithSubtyping appInfo;
-  private final RootSet rootSet;
-  private final Map<DexType, NamingState<DexProto>> states = new IdentityHashMap<>();
-  private final NamingState<DexProto> globalState;
   private MethodSignatureEquivalence equivalence = MethodSignatureEquivalence.get();
-  private final ImmutableList<String> dictionary;
 
   MethodNameMinifier(AppInfoWithSubtyping appInfo, RootSet rootSet, InternalOptions options) {
-    this.appInfo = appInfo;
-    this.rootSet = rootSet;
-    this.dictionary = options.proguardConfiguration.getObfuscationDictionary();
-    this.globalState = NamingState.createRoot(appInfo.dexItemFactory, dictionary);
+    super(appInfo, rootSet, options);
   }
 
   Map<DexMethod, DexString> computeRenaming(Timing timing) {
@@ -120,35 +112,32 @@
     // Phase 3: Assign names to interface methods. These are assigned by finding a name that is
     //          free in all naming states that may hold an implementation.
     timing.begin("Phase 3");
-    Map<DexMethod, DexString> renaming = new IdentityHashMap<>();
-    assignNamesToInterfaceMethods(frontierMap, renaming, timing);
+    assignNamesToInterfaceMethods(frontierMap, timing);
     timing.end();
     // Phase 4: Assign names top-down by traversing the subtype hierarchy.
     timing.begin("Phase 4");
-    assignNamesToClassesMethods(appInfo.dexItemFactory.objectType, false, renaming);
+    assignNamesToClassesMethods(appInfo.dexItemFactory.objectType, false);
     timing.end();
     // Phase 4: Do the same for private methods.
     timing.begin("Phase 5");
-    assignNamesToClassesMethods(appInfo.dexItemFactory.objectType, true, renaming);
+    assignNamesToClassesMethods(appInfo.dexItemFactory.objectType, true);
     timing.end();
 
     return renaming;
   }
 
-  private void assignNamesToClassesMethods(DexType type, boolean doPrivates,
-      Map<DexMethod, DexString> renaming) {
+  private void assignNamesToClassesMethods(DexType type, boolean doPrivates) {
     DexClass holder = appInfo.definitionFor(type);
     if (holder != null && !holder.isLibraryClass()) {
-      NamingState<DexProto> state = states
-          .computeIfAbsent(type, k -> states.get(holder.superType).createChild());
-      holder.forEachMethod(method -> assignNameToMethod(method, state, doPrivates, renaming));
+      NamingState<DexProto> state =
+          computeStateIfAbsent(type, k -> getState(holder.superType).createChild());
+      holder.forEachMethod(method -> assignNameToMethod(method, state, doPrivates));
     }
-    type.forAllExtendsSubtypes(
-        subtype -> assignNamesToClassesMethods(subtype, doPrivates, renaming));
+    type.forAllExtendsSubtypes(subtype -> assignNamesToClassesMethods(subtype, doPrivates));
   }
 
-  private void assignNameToMethod(DexEncodedMethod encodedMethod,
-      NamingState<DexProto> state, boolean doPrivates, Map<DexMethod, DexString> renaming) {
+  private void assignNameToMethod(
+      DexEncodedMethod encodedMethod, NamingState<DexProto> state, boolean doPrivates) {
     if (encodedMethod.accessFlags.isPrivate() != doPrivates) {
       return;
     }
@@ -168,10 +157,10 @@
     Set<NamingState<DexProto>> reachableStates = new HashSet<>();
     for (DexType iface : interfaces) {
       // Add the interface itself
-      reachableStates.add(states.get(iface));
+      reachableStates.add(getState(iface));
       // And the frontiers that correspond to the classes that implement the interface.
       iface.forAllImplementsSubtypes(t -> {
-        NamingState<DexProto> state = states.get(frontierMap.get(t));
+        NamingState<DexProto> state = getState(frontierMap.get(t));
         assert state != null;
         reachableStates.add(state);
       });
@@ -179,8 +168,7 @@
     return reachableStates;
   }
 
-  private void assignNamesToInterfaceMethods(Map<DexType, DexType> frontierMap,
-      Map<DexMethod, DexString> renaming, Timing timing) {
+  private void assignNamesToInterfaceMethods(Map<DexType, DexType> frontierMap, Timing timing) {
     // First compute a map from method signatures to a set of naming states for interfaces and
     // frontier states of classes that implement them. We add the frontier states so that we can
     // reserve the names for later method naming.
@@ -209,10 +197,10 @@
     methods.sort((a, b) -> globalStateMap.get(b).size() - globalStateMap.get(a).size());
     for (Wrapper<DexMethod> key : methods) {
       DexMethod method = key.get();
-      assignNameForInterfaceMethodInAllStates(method,
+      assignNameForInterfaceMethodInAllStates(
+          method,
           globalStateMap.get(key),
           sourceMethodsMap.get(key),
-          renaming,
           originStates.get(key));
     }
     timing.end();
@@ -231,7 +219,6 @@
     }
   }
 
-
   private void collectSubInterfaces(DexType iface, Set<DexType> interfaces) {
     iface.forAllExtendsSubtypes(subtype -> {
       assert subtype.isInterface();
@@ -251,13 +238,14 @@
         globalStateMap.computeIfAbsent(key, k -> new HashSet<>());
     stateSet.addAll(collectedStates);
     sourceMethodsMap.computeIfAbsent(key, k -> new HashSet<>()).add(method.method);
-    originStates.putIfAbsent(key, states.get(originInterface));
+    originStates.putIfAbsent(key, getState(originInterface));
   }
 
-  private void assignNameForInterfaceMethodInAllStates(DexMethod method,
+  private void assignNameForInterfaceMethodInAllStates(
+      DexMethod method,
       Set<NamingState<DexProto>> collectedStates,
       Set<DexMethod> sourceMethods,
-      Map<DexMethod, DexString> renaming, NamingState<DexProto> originState) {
+      NamingState<DexProto> originState) {
     boolean isReserved = false;
     if (globalState.isReserved(method.name, method.proto)) {
       for (NamingState<DexProto> state : collectedStates) {
@@ -326,7 +314,8 @@
       Map<DexType, DexType> frontierMap) {
     frontierMap.put(type, libraryFrontier);
     NamingState<DexProto> state =
-        states.computeIfAbsent(libraryFrontier,
+        computeStateIfAbsent(
+            libraryFrontier,
             ignore -> parent == null
                 ? NamingState.createRoot(appInfo.dexItemFactory, dictionary)
                 : parent.createChild());
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardClassNameList.java b/src/main/java/com/android/tools/r8/shaking/ProguardClassNameList.java
index cd1c5fc..979d238 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardClassNameList.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardClassNameList.java
@@ -12,6 +12,7 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
+import java.util.function.Consumer;
 import java.util.stream.Collectors;
 
 public abstract class ProguardClassNameList {
@@ -20,6 +21,10 @@
     return new Builder();
   }
 
+  public static ProguardClassNameList emptyList() {
+    return new EmptyClassNameList();
+  }
+
   public static ProguardClassNameList singletonList(ProguardTypeMatcher matcher) {
     return new SingleClassNameList(matcher);
   }
@@ -69,6 +74,37 @@
 
   public abstract boolean matches(DexType type);
 
+  public abstract void forEachTypeMatcher(Consumer<ProguardTypeMatcher> consumer);
+
+  private static class EmptyClassNameList extends ProguardClassNameList {
+
+    private EmptyClassNameList() {
+    }
+
+    @Override
+    public int size() {
+      return 0;
+    }
+
+    @Override
+    public void writeTo(StringBuilder builder) {
+    }
+
+    @Override
+    public List<DexType> asSpecificDexTypes() {
+      return null;
+    }
+
+    @Override
+    public boolean matches(DexType type) {
+      return false;
+    }
+
+    @Override
+    public void forEachTypeMatcher(Consumer<ProguardTypeMatcher> consumer) {
+    }
+  }
+
   private static class SingleClassNameList extends ProguardClassNameList {
 
     private final ProguardTypeMatcher className;
@@ -97,6 +133,11 @@
     public boolean matches(DexType type) {
       return className.matches(type);
     }
+
+    @Override
+    public void forEachTypeMatcher(Consumer<ProguardTypeMatcher> consumer) {
+      consumer.accept(className);
+    }
   }
 
   private static class PositiveClassNameList extends ProguardClassNameList {
@@ -137,6 +178,11 @@
     public boolean matches(DexType type) {
       return classNames.stream().anyMatch(name -> name.matches(type));
     }
+
+    @Override
+    public void forEachTypeMatcher(Consumer<ProguardTypeMatcher> consumer) {
+      classNames.forEach(consumer);
+    }
   }
 
   private static class MixedClassNameList extends ProguardClassNameList {
@@ -182,5 +228,10 @@
       }
       return false;
     }
+
+    @Override
+    public void forEachTypeMatcher(Consumer<ProguardTypeMatcher> consumer) {
+      classNames.object2BooleanEntrySet().forEach(entry -> consumer.accept(entry.getKey()));
+    }
   }
 }
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 239f9bc..10daea7 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
@@ -8,12 +8,9 @@
 import com.android.tools.r8.utils.InternalOptions.KeepAttributeOptions;
 import com.android.tools.r8.utils.InternalOptions.PackageObfuscationMode;
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
 import java.nio.file.Path;
 import java.util.ArrayList;
-import java.util.HashSet;
 import java.util.List;
-import java.util.Set;
 
 public class ProguardConfiguration {
 
@@ -36,7 +33,7 @@
     private boolean verbose = false;
     private String renameSourceFileAttribute = null;
     private final List<String> keepAttributePatterns = new ArrayList<>();
-    private final Set<ProguardTypeMatcher> dontWarnPatterns = new HashSet<>();
+    private ProguardClassNameList dontWarnPatterns = ProguardClassNameList.emptyList();
     protected final List<ProguardConfigurationRule> rules = new ArrayList<>();
     private final DexItemFactory dexItemFactory;
     private boolean printSeeds;
@@ -133,8 +130,8 @@
       this.rules.add(rule);
     }
 
-    public void addDontWarnPattern(ProguardTypeMatcher pattern) {
-      dontWarnPatterns.add(pattern);
+    public void setDontWarnPatterns(ProguardClassNameList patterns) {
+      dontWarnPatterns = patterns;
     }
 
     public void setSeedFile(Path seedFile) {
@@ -223,7 +220,7 @@
   private final boolean verbose;
   private final String renameSourceFileAttribute;
   private final ImmutableList<String> keepAttributesPatterns;
-  private final ImmutableSet<ProguardTypeMatcher> dontWarnPatterns;
+  private final ProguardClassNameList dontWarnPatterns;
   protected final ImmutableList<ProguardConfigurationRule> rules;
   private final boolean printSeeds;
   private final Path seedFile;
@@ -252,7 +249,7 @@
       boolean verbose,
       String renameSourceFileAttribute,
       List<String> keepAttributesPatterns,
-      Set<ProguardTypeMatcher> dontWarnPatterns,
+      ProguardClassNameList dontWarnPatterns,
       List<ProguardConfigurationRule> rules,
       boolean printSeeds,
       Path seedFile,
@@ -279,7 +276,7 @@
     this.verbose = verbose;
     this.renameSourceFileAttribute = renameSourceFileAttribute;
     this.keepAttributesPatterns = ImmutableList.copyOf(keepAttributesPatterns);
-    this.dontWarnPatterns = ImmutableSet.copyOf(dontWarnPatterns);
+    this.dontWarnPatterns = dontWarnPatterns;
     this.rules = ImmutableList.copyOf(rules);
     this.printSeeds = printSeeds;
     this.seedFile = seedFile;
@@ -377,7 +374,7 @@
     return keepAttributesPatterns;
   }
 
-  public ImmutableSet<ProguardTypeMatcher> getDontWarnPatterns() {
+  public ProguardClassNameList getDontWarnPatterns() {
     return dontWarnPatterns;
   }
 
@@ -430,7 +427,7 @@
           false                 /* verbose */,
           null                  /* renameSourceFileAttribute */,
           KeepAttributeOptions.KEEP_ALL,
-          ImmutableSet.of()     /* dontWarnPatterns */,
+          ProguardClassNameList.emptyList(),
           ImmutableList.of(ProguardKeepRule.defaultKeepAllRule()),
           false                 /* printSeeds */,
           null                  /* seedFile */,
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 08a7eca..f8e3058 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -78,13 +78,6 @@
   }
 
   public ProguardConfiguration getConfig() throws ProguardRuleParserException {
-    if (configurationBuilder.isUseUniqueClassMemberNames()
-        && configurationBuilder.isObfuscating()) {
-      // The flag -useuniqueulassmembernames has only effect when minifying, so ignore it if we
-      // are not.
-      throw new ProguardRuleParserException("-useuniqueulassmembernames is not supported");
-    }
-
     if (configurationBuilder.isKeepParameterNames()
         && configurationBuilder.isObfuscating()) {
       // The flag -keepparameternames has only effect when minifying, so ignore it if we
@@ -198,11 +191,12 @@
       } else if (acceptString("ignorewarnings")) {
         configurationBuilder.setIgnoreWarnings(true);
       } else if (acceptString("dontwarn")) {
-        do {
-          ProguardTypeMatcher pattern = ProguardTypeMatcher.create(parseClassName(),
-              ClassOrType.CLASS, dexItemFactory);
-          configurationBuilder.addDontWarnPattern(pattern);
-        } while (acceptChar(','));
+        if (isOptionalArgumentGiven()) {
+          configurationBuilder.setDontWarnPatterns(parseClassNames());
+        } else {
+          configurationBuilder.setDontWarnPatterns(
+              ProguardClassNameList.singletonList(ProguardTypeMatcher.defaultAllMatcher()));
+        }
       } else if (acceptString("repackageclasses")) {
         if (configurationBuilder.getPackageObfuscationMode() == PackageObfuscationMode.FLATTEN) {
           warnOverridingOptions("repackageclasses", "flattenpackagehierarchy");
diff --git a/src/test/examples/uniquemembernames/AnotherCls.java b/src/test/examples/uniquemembernames/AnotherCls.java
new file mode 100644
index 0000000..a7f8e97
--- /dev/null
+++ b/src/test/examples/uniquemembernames/AnotherCls.java
@@ -0,0 +1,19 @@
+// 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 uniquemembernames;
+
+public class AnotherCls {
+
+  protected int f2;
+  protected double b;
+
+  int b() {
+    return f2 * 3;
+  }
+
+  int foo() {
+    return b() - (int) b;
+  }
+
+}
diff --git a/src/test/examples/uniquemembernames/BaseCls.java b/src/test/examples/uniquemembernames/BaseCls.java
new file mode 100644
index 0000000..9843c7b
--- /dev/null
+++ b/src/test/examples/uniquemembernames/BaseCls.java
@@ -0,0 +1,21 @@
+// 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 uniquemembernames;
+
+public abstract class BaseCls {
+
+  protected int a;
+  protected double f2;
+
+  protected abstract int a();
+
+  protected int foo() {
+    return a * (int) f2;
+  }
+
+  protected double bar() {
+    return a * f2;
+  }
+
+}
diff --git a/src/test/examples/uniquemembernames/ClsA.java b/src/test/examples/uniquemembernames/ClsA.java
new file mode 100644
index 0000000..430a255
--- /dev/null
+++ b/src/test/examples/uniquemembernames/ClsA.java
@@ -0,0 +1,18 @@
+// 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 uniquemembernames;
+
+public class ClsA extends BaseCls {
+
+  @Override
+  protected int a() {
+    return foo() + a;
+  }
+
+  @Override
+  protected int foo() {
+    return (int) bar();
+  }
+
+}
diff --git a/src/test/examples/uniquemembernames/ClsB.java b/src/test/examples/uniquemembernames/ClsB.java
new file mode 100644
index 0000000..2659d5a
--- /dev/null
+++ b/src/test/examples/uniquemembernames/ClsB.java
@@ -0,0 +1,18 @@
+// 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 uniquemembernames;
+
+public class ClsB extends BaseCls {
+
+  @Override
+  protected int a() {
+    return foo() - a;
+  }
+
+  @Override
+  protected double bar() {
+    return f2 != 0 ? a / f2 : Double.MAX_VALUE;
+  }
+
+}
diff --git a/src/test/examples/uniquemembernames/Shaking.java b/src/test/examples/uniquemembernames/Shaking.java
new file mode 100644
index 0000000..ce7ed5d
--- /dev/null
+++ b/src/test/examples/uniquemembernames/Shaking.java
@@ -0,0 +1,23 @@
+// 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 uniquemembernames;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class Shaking {
+
+  public static void main(String[] args) {
+    List<BaseCls> bases = Arrays.asList(new ClsA(), new ClsB());
+    for (BaseCls base : bases) {
+      base.a();
+      base.foo();
+      base.bar();
+    }
+    AnotherCls aa = new AnotherCls();
+    aa.b();
+    aa.foo();
+  }
+
+}
diff --git a/src/test/examples/uniquemembernames/keep-rules-1.txt b/src/test/examples/uniquemembernames/keep-rules-1.txt
new file mode 100644
index 0000000..2acd66a
--- /dev/null
+++ b/src/test/examples/uniquemembernames/keep-rules-1.txt
@@ -0,0 +1,17 @@
+# Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+-dontoptimize
+-dontshrink
+
+# Keep test fields/methods for deterministic naming
+-keepclassmembers public class **.BaseCls {
+  *** a;
+  *** a(...);
+}
+
+-keepclassmembers public class **.AnotherCls {
+  *** b;
+  *** b(...);
+}
diff --git a/src/test/examples/uniquemembernames/keep-rules-2.txt b/src/test/examples/uniquemembernames/keep-rules-2.txt
new file mode 100644
index 0000000..3f1bdad
--- /dev/null
+++ b/src/test/examples/uniquemembernames/keep-rules-2.txt
@@ -0,0 +1,19 @@
+# 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.
+
+-dontoptimize
+-dontshrink
+
+# Keep test fields/methods for deterministic naming
+-keepclassmembers public class **.BaseCls {
+  *** a;
+  *** a(...);
+}
+
+-keepclassmembers public class **.AnotherCls {
+  *** b;
+  *** b(...);
+}
+
+-useuniqueclassmembernames
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
index c094e5b..1468dc1 100644
--- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -931,9 +931,19 @@
   );
 
   private static List<String> failuresToTriage = ImmutableList.of(
+      // Contains a method that falls off the end without a return, the test should not be excluded
+      // for all configurations but for all using a jar file as input
+      "606-erroneous-class",
+
       // const-method-handle and const-method-type
       "979-const-method-handle",
 
+      // Dex file input into a jar file, not yet supported by the test framework.
+      "663-odd-dex-size",
+      "663-odd-dex-size2",
+      "663-odd-dex-size3",
+      "663-odd-dex-size4",
+
       // This is flaky.
       "104-growth-limit",
 
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index 89a7a26..e46ff48 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -101,15 +101,6 @@
   }
 
   /**
-   * Compile an application with D8.
-   */
-  protected AndroidApp compileWithD8(AndroidApp app, Consumer<InternalOptions> optionsConsumer)
-      throws CompilationException, ExecutionException, IOException {
-    D8Command command = ToolHelper.prepareD8CommandBuilder(app).build();
-    return ToolHelper.runD8(command, optionsConsumer);
-  }
-
-  /**
    * Compile an application with R8.
    */
   protected AndroidApp compileWithR8(Class... classes)
@@ -258,25 +249,11 @@
   /**
    * Run application on Art with the specified main class.
    */
-  protected ProcessResult runOnArtRaw(AndroidApp app, String mainClass) throws IOException {
+  protected String runOnArt(AndroidApp app, Class mainClass) throws IOException {
     Path out = File.createTempFile("junit", ".zip", temp.getRoot()).toPath();
     app.writeToZip(out, OutputMode.Indexed);
-    return ToolHelper.runArtNoVerificationErrorsRaw(
-        ImmutableList.of(out.toString()), mainClass, null);
-  }
-
-  /**
-   * Run application on Art with the specified main class.
-   */
-  protected ProcessResult runOnArtRaw(AndroidApp app, Class mainClass) throws IOException {
-    return runOnArtRaw(app, mainClass.getCanonicalName());
-  }
-
-  /**
-   * Run application on Art with the specified main class.
-   */
-  protected String runOnArt(AndroidApp app, Class mainClass) throws IOException {
-    return runOnArtRaw(app, mainClass).stdout;
+    return ToolHelper.runArtNoVerificationErrors(
+        ImmutableList.of(out.toString()), mainClass.getCanonicalName(), null);
   }
 
   /**
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index d435ae5..9947e97 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.DeviceRunner.DeviceRunnerConfigurationException;
 import com.android.tools.r8.ToolHelper.DexVm.Kind;
 import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.graph.AppInfoWithSubtyping;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexItemFactory;
@@ -568,12 +569,19 @@
 
   public static DexApplication buildApplication(List<String> fileNames)
       throws IOException, ExecutionException {
+    return buildApplicationWithAndroidJar(fileNames, getDefaultAndroidJar());
+  }
+
+  public static DexApplication buildApplicationWithAndroidJar(
+      List<String> fileNames, String androidJar)
+      throws IOException, ExecutionException {
+    AndroidApp input = AndroidApp.builder()
+        .addProgramFiles(ListUtils.map(fileNames, FilteredClassPath::unfiltered))
+        .addLibraryFiles(FilteredClassPath.unfiltered(androidJar))
+        .build();
     return new ApplicationReader(
-        AndroidApp.fromProgramFiles(ListUtils.map(fileNames, Paths::get)),
-        new InternalOptions(),
-        new Timing("ToolHelper buildApplication"))
-        .read()
-        .toDirect();
+        input, new InternalOptions(), new Timing("ToolHelper buildApplication"))
+        .read().toDirect();
   }
 
   public static ProguardConfiguration loadProguardConfiguration(
@@ -772,29 +780,18 @@
     return Paths.get(System.getProperty("java.home"), "bin", "java").toString();
   }
 
-  public static ProcessResult runArtNoVerificationErrorsRaw(String file, String mainClass)
-      throws IOException {
-    return runArtNoVerificationErrorsRaw(Collections.singletonList(file), mainClass, null);
-  }
-
   public static String runArtNoVerificationErrors(String file, String mainClass)
       throws IOException {
-    return runArtNoVerificationErrorsRaw(file, mainClass).stdout;
-  }
-
-  public static ProcessResult runArtNoVerificationErrorsRaw(List<String> files, String mainClass,
-      Consumer<ArtCommandBuilder> extras)
-      throws IOException {
-    return runArtNoVerificationErrorsRaw(files, mainClass, extras, null);
+    return runArtNoVerificationErrors(Collections.singletonList(file), mainClass, null);
   }
 
   public static String runArtNoVerificationErrors(List<String> files, String mainClass,
       Consumer<ArtCommandBuilder> extras)
       throws IOException {
-    return runArtNoVerificationErrorsRaw(files, mainClass, extras).stdout;
+    return runArtNoVerificationErrors(files, mainClass, extras, null);
   }
 
-  public static ProcessResult runArtNoVerificationErrorsRaw(List<String> files, String mainClass,
+  public static String runArtNoVerificationErrors(List<String> files, String mainClass,
       Consumer<ArtCommandBuilder> extras,
       DexVm version)
       throws IOException {
@@ -805,30 +802,18 @@
     if (extras != null) {
       extras.accept(builder);
     }
-    return runArtNoVerificationErrorsRaw(builder);
-  }
-
-  public static String runArtNoVerificationErrors(List<String> files, String mainClass,
-      Consumer<ArtCommandBuilder> extras,
-      DexVm version)
-      throws IOException {
-    return runArtNoVerificationErrorsRaw(files, mainClass, extras, version).stdout;
-  }
-
-  public static ProcessResult runArtNoVerificationErrorsRaw(ArtCommandBuilder builder)
-      throws IOException {
-    ProcessResult result = runArtProcessRaw(builder);
-    if (result.stderr.contains("Verification error")) {
-      fail("Verification error: \n" + result.stderr);
-    }
-    return result;
+    return runArtNoVerificationErrors(builder);
   }
 
   public static String runArtNoVerificationErrors(ArtCommandBuilder builder) throws IOException {
-    return runArtNoVerificationErrorsRaw(builder).stdout;
+    ProcessResult result = runArtProcess(builder);
+    if (result.stderr.contains("Verification error")) {
+      fail("Verification error: \n" + result.stderr);
+    }
+    return result.stdout;
   }
 
-  private static ProcessResult runArtProcessRaw(ArtCommandBuilder builder) throws IOException {
+  private static ProcessResult runArtProcess(ArtCommandBuilder builder) throws IOException {
     Assume.assumeTrue(ToolHelper.artSupported());
     ProcessResult result;
     if (builder.isForDevice()) {
@@ -840,11 +825,6 @@
     } else {
       result = runProcess(builder.asProcessBuilder());
     }
-    return result;
-  }
-
-  private static ProcessResult runArtProcess(ArtCommandBuilder builder) throws IOException {
-    ProcessResult result = runArtProcessRaw(builder);
     if (result.exitCode != 0) {
       fail("Unexpected art failure: '" + result.stderr + "'\n" + result.stdout);
     }
diff --git a/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java b/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java
index ab94090..8cf6e9b 100644
--- a/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java
+++ b/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java
@@ -39,7 +39,6 @@
 import java.util.List;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executors;
-import java.util.function.Consumer;
 import org.junit.Rule;
 import org.junit.rules.TemporaryFolder;
 
@@ -81,11 +80,6 @@
     return ToolHelper.runD8(builder.build());
   }
 
-  protected AndroidApp compileWithD8(
-      JasminBuilder builder, Consumer<InternalOptions> optionsConsumer) throws Exception {
-    return ToolHelper.runD8(builder.build(), optionsConsumer);
-  }
-
   protected String runOnArtD8(JasminBuilder builder, String main) throws Exception {
     return runOnArt(compileWithD8(builder), main);
   }
@@ -138,16 +132,10 @@
     return ToolHelper.runArtNoVerificationErrors(dex.toString(), main);
   }
 
-  protected ProcessResult runOnArtRaw(AndroidApp app, String main) throws IOException {
-    Path out = temp.getRoot().toPath().resolve("out.zip");
-    app.writeToZip(out, OutputMode.Indexed);
-    return ToolHelper.runArtNoVerificationErrorsRaw(out.toString(), main);
-  }
-
   protected String runOnArt(AndroidApp app, String main) throws IOException {
     Path out = temp.getRoot().toPath().resolve("out.zip");
     app.writeToZip(out, OutputMode.Indexed);
-    return ToolHelper.runArtNoVerificationErrors(out.toString(), main);
+    return ToolHelper.runArtNoVerificationErrors(ImmutableList.of(out.toString()), main, null);
   }
 
   protected static DexApplication process(DexApplication app, InternalOptions options)
diff --git a/src/test/java/com/android/tools/r8/naming/MinifierTest.java b/src/test/java/com/android/tools/r8/naming/MinifierTest.java
index 1124ceb..4b42880 100644
--- a/src/test/java/com/android/tools/r8/naming/MinifierTest.java
+++ b/src/test/java/com/android/tools/r8/naming/MinifierTest.java
@@ -70,11 +70,6 @@
         a, dexItemFactory.createProto(dexItemFactory.voidType), "privateFunc");
     // method naming001.A.privateFunc would be renamed.
     assertNotEquals("privateFunc", naming.lookupName(p).toSourceString());
-
-    DexType k = dexItemFactory.createType("Lnaming001/K;");
-    DexField h = dexItemFactory.createField(k, dexItemFactory.intType, "h");
-    // field naming001.K.h is dead, not renamed; hence returned as same via identityLens.
-    assertEquals("h", naming.lookupName(h).toSourceString());
   }
 
   private static void test001_rule002(DexItemFactory dexItemFactory, NamingLens naming) {
@@ -170,12 +165,6 @@
         reflect, dexItemFactory.createProto(dexItemFactory.voidType), "keep6");
     // method naming001.Reflect.keep6 should be kept, according to the keep rule.
     assertEquals("keep6", naming.lookupName(keep6).toSourceString());
-
-    DexType reflect2 = dexItemFactory.createType("Lnaming001/Reflect2;");
-    DexField fieldPublic = dexItemFactory.createField(
-        reflect2, dexItemFactory.intType, "fieldPublic");
-    // method naming001.Reflect.keep6 accesses to naming001.Reflect2.fieldPublic via reflection.
-    assertEquals("fieldPublic", naming.lookupName(fieldPublic).toSourceString());
   }
 
   private static void test001_rule017(DexItemFactory dexItemFactory, NamingLens naming) {
@@ -189,12 +178,12 @@
     assertEquals("keep", naming.lookupName(keep).toSourceString());
 
     DexField i = dexItemFactory.createField(k, dexItemFactory.intType, "i");
-    // field naming001.K.i
-    assertEquals("i", naming.lookupName(i).toSourceString());
+    // field naming001.K.i would be renamed
+    assertNotEquals("i", naming.lookupName(i).toSourceString());
 
     DexField j = dexItemFactory.createField(k, dexItemFactory.intType, "j");
-    // field naming001.K.j
-    assertEquals("j", naming.lookupName(j).toSourceString());
+    // field naming001.K.j would be renamed
+    assertNotEquals("j", naming.lookupName(j).toSourceString());
   }
 
 }
diff --git a/src/test/java/com/android/tools/r8/naming/UseUniqueMemberNameTest.java b/src/test/java/com/android/tools/r8/naming/UseUniqueMemberNameTest.java
new file mode 100644
index 0000000..3094756
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/UseUniqueMemberNameTest.java
@@ -0,0 +1,121 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.naming;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.Timing;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.BiConsumer;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class UseUniqueMemberNameTest extends NamingTestBase {
+
+  public UseUniqueMemberNameTest(
+      String test,
+      List<String> keepRulesFiles,
+      BiConsumer<DexItemFactory, NamingLens> inspection) {
+    super(test, keepRulesFiles, inspection, new Timing("UseUniqueMemberNameTest"));
+  }
+
+  @Test
+  public void minifierTest() throws Exception {
+    NamingLens naming = runMinifier(ListUtils.map(keepRulesFiles, Paths::get));
+    inspection.accept(dexItemFactory, naming);
+  }
+
+  @Parameters(name = "test: {0} keep: {1}")
+  public static Collection<Object[]> data() {
+    List<String> tests = Arrays.asList("uniquemembernames");
+
+    Map<String, BiConsumer<DexItemFactory, NamingLens>> inspections = new HashMap<>();
+    inspections.put("uniquemembernames:keep-rules-1.txt", UseUniqueMemberNameTest::test00_rule1);
+    inspections.put("uniquemembernames:keep-rules-2.txt", UseUniqueMemberNameTest::test00_rule2);
+
+    return createTests(tests, inspections);
+  }
+
+  private static void test00_rule1(DexItemFactory dexItemFactory, NamingLens naming) {
+    DexType a = dexItemFactory.createType("Luniquemembernames/ClsA;");
+    assertNotEquals("Luniquemembernames/ClsA;", naming.lookupDescriptor(a).toSourceString());
+
+    DexMethod foo = dexItemFactory.createMethod(
+        a, dexItemFactory.createProto(dexItemFactory.intType), "foo");
+    String foo_renamed = naming.lookupName(foo).toSourceString();
+    assertNotEquals("foo", foo_renamed);
+
+    DexType aa = dexItemFactory.createType("Luniquemembernames/AnotherCls;");
+    assertNotEquals("Luniquemembernames/AnotherCls;", naming.lookupDescriptor(aa).toSourceString());
+
+    DexMethod another_foo = dexItemFactory.createMethod(
+        aa, dexItemFactory.createProto(dexItemFactory.intType), "foo");
+    String another_foo_renamed = naming.lookupName(another_foo).toSourceString();
+    assertNotEquals("foo", another_foo_renamed);
+
+    // BaseCls#a and AnotherCls#b are kept.
+    // Due to BaseCls#a, BaseCls#foo should not be renamed to a.
+    // On the other hand, AnotherCls#foo should be renamed to a.
+    assertNotEquals(foo_renamed, another_foo_renamed);
+
+    DexType base = dexItemFactory.createType("Luniquemembernames/BaseCls;");
+    DexField f2 = dexItemFactory.createField(base, dexItemFactory.doubleType, "f2");
+    DexField another_f2 = dexItemFactory.createField(aa, dexItemFactory.intType, "f2");
+    // Fields f2's are only fields that are allowed to be renamed. Thus, they would be renamed to
+    // the same name as long as R8 is deterministic.
+    assertEquals(
+        naming.lookupName(f2).toSourceString(),
+        naming.lookupName(another_f2).toSourceString());
+  }
+
+  // -useuniqueclassmembernames
+  private static void test00_rule2(DexItemFactory dexItemFactory, NamingLens naming) {
+    DexType a = dexItemFactory.createType("Luniquemembernames/ClsA;");
+    assertNotEquals("Luniquemembernames/ClsA;", naming.lookupDescriptor(a).toSourceString());
+
+    DexMethod foo = dexItemFactory.createMethod(
+        a, dexItemFactory.createProto(dexItemFactory.intType), "foo");
+    String foo_renamed = naming.lookupName(foo).toSourceString();
+    assertNotEquals("foo", foo_renamed);
+
+    DexType aa = dexItemFactory.createType("Luniquemembernames/AnotherCls;");
+    assertNotEquals("Luniquemembernames/AnotherCls;", naming.lookupDescriptor(aa).toSourceString());
+
+    DexMethod another_foo = dexItemFactory.createMethod(
+        aa, dexItemFactory.createProto(dexItemFactory.intType), "foo");
+    String another_foo_renamed = naming.lookupName(another_foo).toSourceString();
+    assertNotEquals("foo", another_foo_renamed);
+
+    // With -useuniquemembernames, foo() with the same signature should be renamed to the same name.
+    assertEquals(foo_renamed, another_foo_renamed);
+    // But, those cannot be renamed to a and b, as those are _globally_ reserved.
+    assertNotEquals("a", foo_renamed);
+    assertNotEquals("a", another_foo_renamed);
+    assertNotEquals("b", foo_renamed);
+    assertNotEquals("b", another_foo_renamed);
+
+    DexType base = dexItemFactory.createType("Luniquemembernames/BaseCls;");
+    DexField f2 = dexItemFactory.createField(base, dexItemFactory.doubleType, "f2");
+    DexField another_f2 = dexItemFactory.createField(aa, dexItemFactory.intType, "f2");
+    // They should be renamed differently even w/ -useuniqueclassmembernames.
+    assertNotEquals(
+        naming.lookupName(f2).toSourceString(),
+        naming.lookupName(another_f2).toSourceString());
+  }
+
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/staticvalues/B67468748.java b/src/test/java/com/android/tools/r8/rewrite/staticvalues/B67468748.java
deleted file mode 100644
index 6d9f582..0000000
--- a/src/test/java/com/android/tools/r8/rewrite/staticvalues/B67468748.java
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.rewrite.staticvalues;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-import com.android.tools.r8.ToolHelper.ProcessResult;
-import com.android.tools.r8.jasmin.JasminBuilder;
-import com.android.tools.r8.jasmin.JasminTestBase;
-import com.android.tools.r8.utils.AndroidApp;
-import com.google.common.collect.ImmutableList;
-import org.junit.Test;
-
-public class B67468748 extends JasminTestBase {
-
-  @Test
-  public void jarInput() throws Exception {
-    final String CLASS_NAME = "Test";
-
-    JasminBuilder builder = new JasminBuilder();
-    JasminBuilder.ClassBuilder clazz = builder.addClass(CLASS_NAME);
-
-    // The static field Test/intField is not defined even though it is written in the
-    // <clinit> code. This class cannot load, but we can still process it to dex, where Art also
-    // cannot load the class.
-    clazz.addStaticMethod("<clinit>", ImmutableList.of(), "V",
-        ".limit stack 1",
-        ".limit locals 1",
-        "iconst_1",
-        "putstatic Test/intField I",
-        "return");
-
-    clazz.addMainMethod(
-        ".limit stack 2",
-        ".limit locals 1",
-        "getstatic java/lang/System/out Ljava/io/PrintStream;",
-        "getstatic Test/intField I",
-        "invokevirtual java/io/PrintStream/print(I)V",
-        "return");
-
-    // The code does not run on the Java VM, as there is a missing field.
-    ProcessResult result = runOnJavaRaw(builder, CLASS_NAME);
-    assertEquals(1, result.exitCode);
-    assertTrue(result.stderr.contains("java.lang.NoSuchFieldError"));
-
-    // Run in release mode to turn on initializer defaults rewriting.
-    AndroidApp application = compileWithD8(builder, options -> options.debug = false);
-
-    // The code does not run on Art, as there is a missing field.
-    result = runOnArtRaw(application, CLASS_NAME);
-    assertEquals(1, result.exitCode);
-    assertTrue(result.stderr.contains("java.lang.NoSuchFieldError"));
-  }
-
-  // Test with dex input is in StaticValuesTest.
-}
diff --git a/src/test/java/com/android/tools/r8/rewrite/staticvalues/StaticValuesTest.java b/src/test/java/com/android/tools/r8/rewrite/staticvalues/StaticValuesTest.java
index 1d26039..9e5a839 100644
--- a/src/test/java/com/android/tools/r8/rewrite/staticvalues/StaticValuesTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/staticvalues/StaticValuesTest.java
@@ -7,7 +7,7 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
-import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.code.Instruction;
 import com.android.tools.r8.code.Sput;
 import com.android.tools.r8.code.SputObject;
@@ -24,7 +24,6 @@
 import com.android.tools.r8.graph.DexValue.DexValueShort;
 import com.android.tools.r8.graph.DexValue.DexValueString;
 import com.android.tools.r8.smali.SmaliTestBase;
-import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.DexInspector;
 import com.android.tools.r8.utils.DexInspector.MethodSubject;
 import com.android.tools.r8.utils.InternalOptions;
@@ -573,43 +572,4 @@
     assertEquals(StringUtils.lines("2"), result);
   }
 
-  @Test
-  public void b67468748() throws Exception {
-    final String CLASS_NAME = "Test";
-
-    SmaliBuilder builder = new SmaliBuilder(CLASS_NAME);
-
-    // The static field LTest;->intField:I is not defined even though it is written in the
-    // <clinit> code. This class cannot load, but we can still process it to output which still
-    // cannot load.
-    builder.addStaticInitializer(
-        2,
-        "const               v0, 3",
-        "sput                v0, LTest;->intField:I",
-        "return-void"
-    );
-    builder.addMainMethod(
-        3,
-        "sget-object         v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
-        "sget                v1, LTest;->intField:I",
-        "invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->println(I)V",
-        "return-void"
-    );
-
-    AndroidApp application = builder.build();
-
-    // The code does not run on Art, as there is a missing field.
-    ProcessResult result = runOnArtRaw(application, CLASS_NAME);
-    assertEquals(1, result.exitCode);
-    assertTrue(result.stderr.contains("java.lang.NoSuchFieldError"));
-
-    // Run in release mode to turn on initializer defaults rewriting.
-    application = compileWithD8(application, options -> options.debug = false);
-
-    // The code does still not run on Art, as there is still a missing field.
-    result = runOnArtRaw(application, CLASS_NAME);
-    assertEquals(1, result.exitCode);
-    assertTrue(result.stderr.contains("java.lang.NoSuchFieldError"));
-  }
-
 }
diff --git a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
index 3f44025..81335c8 100644
--- a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
@@ -145,6 +145,51 @@
   }
 
   @Test
+  public void testDontWarn() throws Exception {
+    DexItemFactory dexItemFactory = new DexItemFactory();
+    ProguardConfigurationParser parser = new ProguardConfigurationParser(dexItemFactory);
+    String dontwarn = "-dontwarn !foobar,*bar";
+    parser.parse(new ProguardConfigurationSourceStrings(ImmutableList.of(dontwarn)));
+    ProguardConfiguration config = parser.getConfig();
+    assertFalse(
+        config.getDontWarnPatterns().matches(dexItemFactory.createType("Lboobaz;")));
+    assertTrue(
+        config.getDontWarnPatterns().matches(dexItemFactory.createType("Lboobar;")));
+    assertFalse(
+        config.getDontWarnPatterns().matches(dexItemFactory.createType("Lfoobar;")));
+  }
+
+  @Test
+  public void testDontWarnAllExplicitly() throws Exception {
+    DexItemFactory dexItemFactory = new DexItemFactory();
+    ProguardConfigurationParser parser = new ProguardConfigurationParser(dexItemFactory);
+    String dontwarnAll = "-dontwarn *";
+    parser.parse(new ProguardConfigurationSourceStrings(ImmutableList.of(dontwarnAll)));
+    ProguardConfiguration config = parser.getConfig();
+    assertTrue(
+        config.getDontWarnPatterns().matches(dexItemFactory.createType("Lboobaz;")));
+    assertTrue(
+        config.getDontWarnPatterns().matches(dexItemFactory.createType("Lboobar;")));
+    assertTrue(
+        config.getDontWarnPatterns().matches(dexItemFactory.createType("Lfoobar;")));
+  }
+
+  @Test
+  public void testDontWarnAllImplicitly() throws Exception {
+    DexItemFactory dexItemFactory = new DexItemFactory();
+    ProguardConfigurationParser parser = new ProguardConfigurationParser(dexItemFactory);
+    String dontwarnAll = "-dontwarn";
+    parser.parse(new ProguardConfigurationSourceStrings(ImmutableList.of(dontwarnAll)));
+    ProguardConfiguration config = parser.getConfig();
+    assertTrue(
+        config.getDontWarnPatterns().matches(dexItemFactory.createType("Lboobaz;")));
+    assertTrue(
+        config.getDontWarnPatterns().matches(dexItemFactory.createType("Lboobar;")));
+    assertTrue(
+        config.getDontWarnPatterns().matches(dexItemFactory.createType("Lfoobar;")));
+  }
+
+  @Test
   public void parseAccessFlags() throws IOException, ProguardRuleParserException {
     ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
     parser.parse(Paths.get(ACCESS_FLAGS_FILE));
@@ -648,36 +693,11 @@
 
   @Test
   public void parseUseUniqueClassMemberNames() throws Exception {
-    try {
-      ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
-      parser.parse(new ProguardConfigurationSourceStrings(ImmutableList.of(
-          "-useuniqueclassmembernames"
-      )));
-      parser.getConfig();
-      fail();
-    } catch (ProguardRuleParserException e) {
-      assertTrue(e.getMessage().contains("-useuniqueulassmembernames is not supported"));
-    }
-  }
-
-  @Test
-  public void parseUseUniqueClassMemberNamesWithoutMinification() throws Exception {
     ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
     parser.parse(new ProguardConfigurationSourceStrings(ImmutableList.of(
-        "-useuniqueclassmembernames",
-        "-dontobfuscate"
-    )));
-    ProguardConfiguration config = parser.getConfig();
-    assertTrue(config.isUseUniqueClassMemberNames());
-
-    parser = new ProguardConfigurationParser(new DexItemFactory());
-    parser.parse(new ProguardConfigurationSourceStrings(ImmutableList.of(
         "-useuniqueclassmembernames"
     )));
-    parser.parse(new ProguardConfigurationSourceStrings(ImmutableList.of(
-        "-dontobfuscate"
-    )));
-    config = parser.getConfig();
+    ProguardConfiguration config = parser.getConfig();
     assertTrue(config.isUseUniqueClassMemberNames());
   }
 
diff --git a/src/test/java/com/android/tools/r8/shaking/ProguardNameMatchingTest.java b/src/test/java/com/android/tools/r8/shaking/ProguardNameMatchingTest.java
index af87086..2737971 100644
--- a/src/test/java/com/android/tools/r8/shaking/ProguardNameMatchingTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardNameMatchingTest.java
@@ -62,6 +62,9 @@
     assertTrue(matchClassName("java.lang.Object", "!java.util.**", "java**"));
     assertFalse(matchClassName("java.lang.Object", "!java.**", "java.lang.*"));
     assertTrue(matchClassName("java.lang.Object", "java.lang.*", "!java.**"));
+
+    assertTrue(matchClassName("boobar", "!foobar", "*bar"));
+    assertFalse(matchClassName("foobar", "!foobar", "*bar"));
   }
 
   private void assertMatchesBasicTypes(String pattern) {
diff --git a/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java b/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
index 41f73d1..f2f1220 100644
--- a/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
+++ b/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
@@ -361,7 +361,7 @@
       getSource(currentClassName).add(builder.toString());
     }
 
-    public List<String> buildSource() {
+    public List<String> build() {
       List<String> result = new ArrayList<>(classes.size());
       for (String clazz : classes.keySet()) {
         Builder classBuilder = classes.get(clazz);
@@ -372,18 +372,12 @@
 
     public byte[] compile()
         throws IOException, RecognitionException, DexOverflowException, ExecutionException {
-      return Smali.compile(buildSource());
+      return Smali.compile(build());
     }
 
-    public AndroidApp build()
-        throws IOException, RecognitionException, DexOverflowException, ExecutionException {
-      return AndroidApp.fromDexProgramData(compile());
-    }
-
-
     @Override
     public String toString() {
-      return String.join("\n\n", buildSource());
+      return String.join("\n\n", build());
     }
   }