Merge "Regression test for bug on 6.0.1 dex2oat compiler."
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 063d14d..df3d81b 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -358,9 +358,12 @@
         }
         application = application.asDirect().rewrittenWithLense(graphLense);
         appInfo = appInfo.withLiveness().rewrittenWithLense(application.asDirect(), graphLense);
-        // Collect switch maps and ordinals maps.
-        appInfo = new SwitchMapCollector(appInfo.withLiveness(), options).run();
-        appInfo = new EnumOrdinalMapCollector(appInfo.withLiveness(), options).run();
+        // TODO(mathiasr): Remove this check when CF->IR construction is complete.
+        if (!options.skipIR) {
+          // Collect switch maps and ordinals maps.
+          appInfo = new SwitchMapCollector(appInfo.withLiveness(), options).run();
+          appInfo = new EnumOrdinalMapCollector(appInfo.withLiveness(), options).run();
+        }
 
         // TODO(b/79143143): re-enable once fixed.
         // graphLense = new BridgeMethodAnalysis(graphLense, appInfo.withLiveness()).run();
diff --git a/src/main/java/com/android/tools/r8/ResourceShrinker.java b/src/main/java/com/android/tools/r8/ResourceShrinker.java
index d2969d6..7fee6fe 100644
--- a/src/main/java/com/android/tools/r8/ResourceShrinker.java
+++ b/src/main/java/com/android/tools/r8/ResourceShrinker.java
@@ -153,8 +153,9 @@
       }
 
       for (DexEncodedField field : classDef.staticFields()) {
-        if (field.staticValue != null) {
-          processFieldValue(field.staticValue);
+        DexValue staticValue = field.getStaticValue();
+        if (staticValue != null) {
+          processFieldValue(staticValue);
         }
       }
 
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 509a91a..b116e65 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
 
   // This field is accessed from release scripts using simple pattern matching.
   // Therefore, changing this field could break our release scripts.
-  public static final String LABEL = "v1.2.12-dev";
+  public static final String LABEL = "v1.2.14-dev";
 
   private Version() {
   }
diff --git a/src/main/java/com/android/tools/r8/cf/CfPrinter.java b/src/main/java/com/android/tools/r8/cf/CfPrinter.java
index 70ce7c8..300335a 100644
--- a/src/main/java/com/android/tools/r8/cf/CfPrinter.java
+++ b/src/main/java/com/android/tools/r8/cf/CfPrinter.java
@@ -412,14 +412,16 @@
 
   public void print(CfSwitch cfSwitch) {
     indent();
-    builder.append(cfSwitch.getKind() == Kind.LOOKUP ? "lookup" : "table").append("switch");
+    Kind kind = cfSwitch.getKind();
+    builder.append(kind == Kind.LOOKUP ? "lookup" : "table").append("switch");
     IntList keys = cfSwitch.getKeys();
     List<CfLabel> targets = cfSwitch.getTargets();
-    for (int i = 0; i < keys.size(); i++) {
+    for (int i = 0; i < targets.size(); i++) {
       indent();
+      int key = kind == Kind.LOOKUP ? keys.getInt(i) : (keys.getInt(0) + i);
       builder
           .append("  ")
-          .append(keys.getInt(i))
+          .append(key)
           .append(": ")
           .append(getLabel(targets.get(i)));
     }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfSwitch.java b/src/main/java/com/android/tools/r8/cf/code/CfSwitch.java
index f27064c..1dc72d4 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfSwitch.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfSwitch.java
@@ -20,12 +20,13 @@
   private final int[] keys;
   private final List<CfLabel> targets;
 
-  public CfSwitch(CfLabel defaultTarget, int[] keys, List<CfLabel> targets) {
-    // TODO(zerny): Support emitting table switches.
-    this.kind = Kind.LOOKUP;
+  public CfSwitch(Kind kind, CfLabel defaultTarget, int[] keys, List<CfLabel> targets) {
+    this.kind = kind;
     this.defaultTarget = defaultTarget;
     this.keys = keys;
     this.targets = targets;
+    assert kind != Kind.LOOKUP || keys.length == targets.size();
+    assert kind != Kind.TABLE || keys.length == 1;
   }
 
   public Kind getKind() {
@@ -50,7 +51,16 @@
     for (int i = 0; i < targets.size(); i++) {
       labels[i] = targets.get(i).getLabel();
     }
-    visitor.visitLookupSwitchInsn(defaultTarget.getLabel(), keys, labels);
+    switch (kind) {
+      case LOOKUP:
+        visitor.visitLookupSwitchInsn(defaultTarget.getLabel(), keys, labels);
+        break;
+      case TABLE: {
+        int min = keys[0];
+        int max = min + labels.length - 1;
+        visitor.visitTableSwitchInsn(min, max, defaultTarget.getLabel(), labels);
+      }
+    }
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/dex/DexParser.java b/src/main/java/com/android/tools/r8/dex/DexParser.java
index 958deb4..4524505 100644
--- a/src/main/java/com/android/tools/r8/dex/DexParser.java
+++ b/src/main/java/com/android/tools/r8/dex/DexParser.java
@@ -576,8 +576,6 @@
       if (accessFlags.isStatic()) {
         if (staticValues != null && i < staticValues.length) {
           staticValue = staticValues[i];
-        } else {
-          staticValue = DexValue.defaultForType(field.type, dexItemFactory);
         }
       }
       fields[i] = new DexEncodedField(field, accessFlags, fieldAnnotations, staticValue);
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotationSetRefList.java b/src/main/java/com/android/tools/r8/graph/DexAnnotationSetRefList.java
index 336eac5..e6b4a2c 100644
--- a/src/main/java/com/android/tools/r8/graph/DexAnnotationSetRefList.java
+++ b/src/main/java/com/android/tools/r8/graph/DexAnnotationSetRefList.java
@@ -12,6 +12,7 @@
   private static final DexAnnotationSetRefList theEmptyTypeList = new DexAnnotationSetRefList();
 
   public final DexAnnotationSet[] values;
+  private final int missingParameterAnnotations;
 
   public static DexAnnotationSetRefList empty() {
     return theEmptyTypeList;
@@ -19,11 +20,17 @@
 
   private DexAnnotationSetRefList() {
     this.values = new DexAnnotationSet[0];
+    this.missingParameterAnnotations = 0;
   }
 
   public DexAnnotationSetRefList(DexAnnotationSet[] values) {
+    this(values, 0);
+  }
+
+  public DexAnnotationSetRefList(DexAnnotationSet[] values, int missingParameterAnnotations) {
     assert values != null && values.length > 0;
     this.values = values;
+    this.missingParameterAnnotations = missingParameterAnnotations;
   }
 
   @Override
@@ -57,4 +64,8 @@
   public boolean isEmpty() {
     return values.length == 0;
   }
+
+  public int getMissingParameterAnnotations() {
+    return missingParameterAnnotations;
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index e9b9255..7b09cab 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -369,7 +369,7 @@
 
   public boolean defaultValuesForStaticFieldsMayTriggerAllocation() {
     return Arrays.stream(staticFields())
-        .anyMatch(field -> !field.staticValue.mayTriggerAllocation());
+        .anyMatch(field -> !field.getStaticValue().mayTriggerAllocation());
   }
 
   public List<InnerClassAttribute> getInnerClasses() {
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
index bdfadfc..a28dc82 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
@@ -15,14 +15,13 @@
   public final DexField field;
   public final FieldAccessFlags accessFlags;
   public DexAnnotationSet annotations;
-  public DexValue staticValue;
+  private DexValue staticValue;
 
   public DexEncodedField(
       DexField field,
       FieldAccessFlags accessFlags,
       DexAnnotationSet annotations,
       DexValue staticValue) {
-    assert !accessFlags.isStatic() || staticValue != null;
     this.field = field;
     this.accessFlags = accessFlags;
     this.annotations = annotations;
@@ -33,8 +32,8 @@
   public void collectIndexedItems(IndexedItemCollection indexedItems) {
     field.collectIndexedItems(indexedItems);
     annotations.collectIndexedItems(indexedItems);
-    if (staticValue != null) {
-      staticValue.collectIndexedItems(indexedItems);
+    if (accessFlags.isStatic()) {
+      getStaticValue().collectIndexedItems(indexedItems);
     }
   }
 
@@ -67,6 +66,22 @@
     return !annotations.isEmpty();
   }
 
+  public boolean hasExplicitStaticValue() {
+    assert accessFlags.isStatic();
+    return staticValue != null;
+  }
+
+  public void setStaticValue(DexValue staticValue) {
+    assert accessFlags.isStatic();
+    assert staticValue != null;
+    this.staticValue = staticValue;
+  }
+
+  public DexValue getStaticValue() {
+    assert accessFlags.isStatic();
+    return staticValue == null ? DexValue.defaultForType(field.type) : staticValue;
+  }
+
   // Returns a const instructions if this field is a compile time final const.
   public Instruction valueAsConstInstruction(AppInfo appInfo, Value dest) {
     // The only way to figure out whether the DexValue contains the final value
@@ -74,7 +89,7 @@
     if (accessFlags.isStatic() && accessFlags.isPublic() && accessFlags.isFinal()) {
       DexClass clazz = appInfo.definitionFor(field.getHolder());
       assert clazz != null : "Class for the field must be present";
-      return staticValue.asConstInstruction(clazz.hasClassInitializer(), dest);
+      return getStaticValue().asConstInstruction(clazz.hasClassInitializer(), dest);
     }
     return null;
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java b/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java
index 9644558..0fac5e8 100644
--- a/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java
@@ -49,7 +49,7 @@
     // Set all static field values to unknown. We don't want to use the value from the library
     // at compile time, as it can be different at runtime.
     for (DexEncodedField staticField : staticFields) {
-      staticField.staticValue = DexValue.UNKNOWN;
+      staticField.setStaticValue(DexValue.UNKNOWN);
     }
     assert kind == Kind.CF : "Invalid kind " + kind + " for library-path class " + type;
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
index 90db1d2..1473191 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -275,9 +275,10 @@
         List<DexValue> values = new ArrayList<>(fields.length);
         for (int i = 0; i < fields.length; i++) {
           DexEncodedField field = fields[i];
-          assert field.staticValue != null;
-          values.add(field.staticValue);
-          if (!field.staticValue.isDefault(field.field.type, factory)) {
+          DexValue staticValue = field.getStaticValue();
+          assert staticValue != null;
+          values.add(staticValue);
+          if (!staticValue.isDefault(field.field.type)) {
             length = i + 1;
           }
         }
diff --git a/src/main/java/com/android/tools/r8/graph/DexValue.java b/src/main/java/com/android/tools/r8/graph/DexValue.java
index f1a0f4d..342ea2a 100644
--- a/src/main/java/com/android/tools/r8/graph/DexValue.java
+++ b/src/main/java/com/android/tools/r8/graph/DexValue.java
@@ -96,35 +96,29 @@
   @Override
   public abstract String toString();
 
-  public static DexValue defaultForType(DexType type, DexItemFactory factory) {
-    if (type == factory.booleanType) {
-      return DexValueBoolean.DEFAULT;
+  public static DexValue defaultForType(DexType type) {
+    switch (type.toShorty()) {
+      case 'Z':
+        return DexValueBoolean.DEFAULT;
+      case 'B':
+        return DexValueByte.DEFAULT;
+      case 'C':
+        return DexValueChar.DEFAULT;
+      case 'S':
+        return DexValueShort.DEFAULT;
+      case 'I':
+        return DexValueInt.DEFAULT;
+      case 'J':
+        return DexValueLong.DEFAULT;
+      case 'F':
+        return DexValueFloat.DEFAULT;
+      case 'D':
+        return DexValueDouble.DEFAULT;
+      case 'L':
+        return DexValueNull.NULL;
+      default:
+        throw new Unreachable("No default value for unexpected type " + type);
     }
-    if (type == factory.byteType) {
-      return DexValueByte.DEFAULT;
-    }
-    if (type == factory.charType) {
-      return DexValueChar.DEFAULT;
-    }
-    if (type == factory.shortType) {
-      return DexValueShort.DEFAULT;
-    }
-    if (type == factory.intType) {
-      return DexValueInt.DEFAULT;
-    }
-    if (type == factory.longType) {
-      return DexValueLong.DEFAULT;
-    }
-    if (type == factory.floatType) {
-      return DexValueFloat.DEFAULT;
-    }
-    if (type == factory.doubleType) {
-      return DexValueDouble.DEFAULT;
-    }
-    if (type.isArrayType() || type.isClassType()) {
-      return DexValueNull.NULL;
-    }
-    throw new Unreachable("No default value for unexpected type " + type);
   }
 
   public abstract Object getBoxedValue();
@@ -134,8 +128,8 @@
     return null;
   }
 
-  public boolean isDefault(DexType type, DexItemFactory factory) {
-    return this == defaultForType(type, factory);
+  public boolean isDefault(DexType type) {
+    return this == defaultForType(type);
   }
 
   /**
diff --git a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
index 28709bd..bef2f57 100644
--- a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
+++ b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
@@ -49,7 +49,6 @@
 import com.android.tools.r8.cf.code.CfUnop;
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.errors.CompilationError;
-import com.android.tools.r8.errors.Unimplemented;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.CfCode.LocalVariableInfo;
 import com.android.tools.r8.graph.DexValue.DexValueAnnotation;
@@ -105,6 +104,8 @@
 
   // Hidden ASM "synthetic attribute" bit we need to clear.
   private static final int ACC_SYNTHETIC_ATTRIBUTE = 0x40000;
+  // Descriptor used by ASM for missing annotations.
+  public static final String SYNTHETIC_ANNOTATION = "Ljava/lang/Synthetic;";
 
   private final JarApplicationReader application;
   private final Consumer<DexClass> classConsumer;
@@ -404,7 +405,7 @@
 
     private DexValue getStaticValue(Object value, DexType type) {
       if (value == null) {
-        return DexValue.defaultForType(type, parent.application.getFactory());
+        return null;
       }
       DexItemFactory factory = parent.application.getFactory();
       if (type == factory.booleanType) {
@@ -521,7 +522,7 @@
       // with that descriptor. If javac is fixed, the ASM workaround will not be hit and we will
       // never see this non-existing annotation descriptor. ASM uses the same check to make
       // sure to undo their workaround for the javac bug in their MethodWriter.
-      if (desc.equals("Ljava/lang/Synthetic;")) {
+      if (desc.equals(SYNTHETIC_ANNOTATION)) {
         // We can iterate through all the parameters twice. Once for visible and once for
         // invisible parameter annotations. We only record the number of fake parameter
         // annotations once.
@@ -595,7 +596,7 @@
         for (int i = 0; i < parameterAnnotations.size(); i++) {
           sets[i] = createAnnotationSet(parameterAnnotations.get(i));
         }
-        parameterAnnotationSets = new DexAnnotationSetRefList(sets);
+        parameterAnnotationSets = new DexAnnotationSetRefList(sets, fakeParameterAnnotations);
       }
       InternalOptions internalOptions = parent.application.options;
       if (parameterNames != null && internalOptions.canUseParameterNameAnnotations()) {
@@ -1216,8 +1217,12 @@
 
     @Override
     public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) {
-      // TODO(mathiasr): Support emitting table switches in CfSwitch.
-      throw new Unimplemented("Table switches not supported in CF backend");
+      assert max == min + labels.length - 1;
+      ArrayList<CfLabel> targets = new ArrayList<>(labels.length);
+      for (Label label : labels) {
+        targets.add(getLabel(label));
+      }
+      instructions.add(new CfSwitch(CfSwitch.Kind.TABLE, getLabel(dflt), new int[] {min}, targets));
     }
 
     @Override
@@ -1226,7 +1231,7 @@
       for (Label label : labels) {
         targets.add(getLabel(label));
       }
-      instructions.add(new CfSwitch(getLabel(dflt), keys, targets));
+      instructions.add(new CfSwitch(CfSwitch.Kind.LOOKUP, getLabel(dflt), keys, targets));
     }
 
     @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Switch.java b/src/main/java/com/android/tools/r8/ir/code/Switch.java
index b8f90e6..50e59f1 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Switch.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Switch.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.cf.LoadStoreHelper;
 import com.android.tools.r8.cf.code.CfLabel;
 import com.android.tools.r8.cf.code.CfSwitch;
+import com.android.tools.r8.cf.code.CfSwitch.Kind;
 import com.android.tools.r8.code.Nop;
 import com.android.tools.r8.code.PackedSwitch;
 import com.android.tools.r8.code.PackedSwitchPayload;
@@ -294,6 +295,6 @@
     for (int index : targetBlockIndices) {
       labels.add(builder.getLabel(successors.get(index)));
     }
-    builder.add(new CfSwitch(builder.getLabel(fallthroughBlock()), keys, labels));
+    builder.add(new CfSwitch(Kind.LOOKUP, builder.getLabel(fallthroughBlock()), keys, labels));
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java
index 8fca809..5ea5d0e 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java
@@ -113,8 +113,10 @@
 
     DexItemFactory factory = factory();
     if (a.isArrayType()) {
-      // Arrays are only adaptable to java.lang.Object.
-      return b == factory.objectType;
+      // Arrays are only adaptable to java.lang.Object or other arrays, note that we
+      // don't check element type inheritance in the second case since we assume the
+      // input code is verifiable.
+      return b == factory.objectType || b.isArrayType();
     }
 
     if (a.isPrimitiveType()) {
@@ -342,8 +344,10 @@
       }
     }
 
-    if (fromType.isArrayType() && toType == factory().objectType) {
+    if (fromType.isArrayType() && (toType == factory().objectType || toType.isArrayType())) {
       // If `fromType` is an array and `toType` is java.lang.Object, no cast is needed.
+      // If both `fromType` and `toType` are arrays, no cast is needed since we assume
+      // the input code is verifiable.
       return register;
     }
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index 7073215..85a3ac6 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -1086,47 +1086,47 @@
           if (put.inValue().isConstant()) {
             if (put.inValue().isConstNumber()) {
               assert put.inValue().isZero();
-              encodedField.staticValue = DexValueNull.NULL;
+              encodedField.setStaticValue(DexValueNull.NULL);
             } else {
               ConstString cnst = put.inValue().getConstInstruction().asConstString();
-              encodedField.staticValue = new DexValueString(cnst.getValue());
+              encodedField.setStaticValue(new DexValueString(cnst.getValue()));
             }
           } else {
             InvokeVirtual invoke = put.inValue().definition.asInvokeVirtual();
             String name = method.method.getHolder().toSourceString();
             if (invoke.getInvokedMethod() == dexItemFactory.classMethods.getSimpleName) {
               String simpleName = name.substring(name.lastIndexOf('.') + 1);
-              encodedField.staticValue =
-                  new DexValueString(dexItemFactory.createString(simpleName));
+              encodedField.setStaticValue(
+                  new DexValueString(dexItemFactory.createString(simpleName)));
             } else {
               assert invoke.getInvokedMethod() == dexItemFactory.classMethods.getName;
-              encodedField.staticValue = new DexValueString(dexItemFactory.createString(name));
+              encodedField.setStaticValue(new DexValueString(dexItemFactory.createString(name)));
             }
           }
         } else if (field.type.isClassType() || field.type.isArrayType()) {
           if (put.inValue().isZero()) {
-            encodedField.staticValue = DexValueNull.NULL;
+            encodedField.setStaticValue(DexValueNull.NULL);
           } else {
             throw new Unreachable("Unexpected default value for field type " + field.type + ".");
           }
         } else {
           ConstNumber cnst = put.inValue().getConstInstruction().asConstNumber();
           if (field.type == dexItemFactory.booleanType) {
-            encodedField.staticValue = DexValueBoolean.create(cnst.getBooleanValue());
+            encodedField.setStaticValue(DexValueBoolean.create(cnst.getBooleanValue()));
           } else if (field.type == dexItemFactory.byteType) {
-            encodedField.staticValue = DexValueByte.create((byte) cnst.getIntValue());
+            encodedField.setStaticValue(DexValueByte.create((byte) cnst.getIntValue()));
           } else if (field.type == dexItemFactory.shortType) {
-            encodedField.staticValue = DexValueShort.create((short) cnst.getIntValue());
+            encodedField.setStaticValue(DexValueShort.create((short) cnst.getIntValue()));
           } else if (field.type == dexItemFactory.intType) {
-            encodedField.staticValue = DexValueInt.create(cnst.getIntValue());
+            encodedField.setStaticValue(DexValueInt.create(cnst.getIntValue()));
           } else if (field.type == dexItemFactory.longType) {
-            encodedField.staticValue = DexValueLong.create(cnst.getLongValue());
+            encodedField.setStaticValue(DexValueLong.create(cnst.getLongValue()));
           } else if (field.type == dexItemFactory.floatType) {
-            encodedField.staticValue = DexValueFloat.create(cnst.getFloatValue());
+            encodedField.setStaticValue(DexValueFloat.create(cnst.getFloatValue()));
           } else if (field.type == dexItemFactory.doubleType) {
-            encodedField.staticValue = DexValueDouble.create(cnst.getDoubleValue());
+            encodedField.setStaticValue(DexValueDouble.create(cnst.getDoubleValue()));
           } else if (field.type == dexItemFactory.charType) {
-            encodedField.staticValue = DexValueChar.create((char) cnst.getIntValue());
+            encodedField.setStaticValue(DexValueChar.create((char) cnst.getIntValue()));
           } else {
             throw new Unreachable("Unexpected field type " + field.type + ".");
           }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
index 65afb32..cdfa2b1 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
@@ -78,7 +78,7 @@
       DexEncodedField staticField = appInfo.lookupStaticTarget(field.clazz, field);
       if (staticField != null) {
         Value value = code.createValue(valueType, instruction.getLocalInfo());
-        replacement = staticField.staticValue.asConstInstruction(false, value);
+        replacement = staticField.getStaticValue().asConstInstruction(false, value);
       } else {
         throw new CompilationError(field.clazz.toSourceString() + "." + field.name.toString() +
             " used in assumevalues rule does not exist.");
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/PeepholeOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/PeepholeOptimizer.java
index 2734ff4..7b3eef0 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/PeepholeOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/PeepholeOptimizer.java
@@ -5,7 +5,7 @@
 
 import com.android.tools.r8.graph.DebugLocalInfo;
 import com.android.tools.r8.ir.code.BasicBlock;
-import com.android.tools.r8.ir.code.ConstInstruction;
+import com.android.tools.r8.ir.code.ConstNumber;
 import com.android.tools.r8.ir.code.DebugLocalsChange;
 import com.android.tools.r8.ir.code.Goto;
 import com.android.tools.r8.ir.code.IRCode;
@@ -266,10 +266,9 @@
       // Mapping from register number to const number instructions for this basic block.
       // Used to remove redundant const instructions that reloads the same constant into
       // the same register.
-      Map<Integer, ConstInstruction> registerToConstant = new HashMap<>();
+      Map<Integer, ConstNumber> registerToNumber = new HashMap<>();
       MoveEliminator moveEliminator = new MoveEliminator(allocator);
       ListIterator<Instruction> iterator = block.getInstructions().listIterator();
-      boolean mayNoLongerThrow = false;
       while (iterator.hasNext()) {
         Instruction current = iterator.next();
         if (moveEliminator.shouldBeEliminated(current)) {
@@ -277,30 +276,28 @@
         } else if (current.outValue() != null && current.outValue().needsRegister()) {
           Value outValue = current.outValue();
           int instructionNumber = current.getNumber();
-          if (!outValue.hasLocalInfo() && (current.isConstNumber() || current.isConstString())) {
-            if (constantSpilledAtDefinition(current.asConstInstruction(), allocator)) {
+          if (outValue.isConstant() && current.isConstNumber()) {
+            if (constantSpilledAtDefinition(current.asConstNumber(), allocator)) {
               // Remove constant instructions that are spilled at their definition and are
               // therefore unused.
               iterator.remove();
-              mayNoLongerThrow |= current.instructionTypeCanThrow();
+              continue;
+            }
+            int outRegister = allocator.getRegisterForValue(outValue, instructionNumber);
+            ConstNumber numberInRegister = registerToNumber.get(outRegister);
+            if (numberInRegister != null
+                && numberInRegister.identicalNonValueNonPositionParts(current)) {
+              // This instruction is not needed, the same constant is already in this register.
+              // We don't consider the positions of the two (non-throwing) instructions.
+              iterator.remove();
             } else {
-              int outRegister = allocator.getRegisterForValue(outValue, instructionNumber);
-              ConstInstruction constantInRegister = registerToConstant.get(outRegister);
-              if (constantInRegister != null
-                  && constantInRegister.identicalNonValueNonPositionParts(current)) {
-                // This instruction is not needed, the same constant is already in this register.
-                // We don't consider the positions of the two (non-throwing) instructions.
-                iterator.remove();
-                mayNoLongerThrow |= current.instructionTypeCanThrow();
+              // Insert the current constant in the mapping. Make sure to clobber the second
+              // register if wide and register-1 if that defines a wide value.
+              registerToNumber.put(outRegister, current.asConstNumber());
+              if (current.outType().isWide()) {
+                registerToNumber.remove(outRegister + 1);
               } else {
-                // Insert the current constant in the mapping. Make sure to clobber the second
-                // register if wide and register-1 if that defines a wide value.
-                registerToConstant.put(outRegister, current.asConstNumber());
-                if (current.outType().isWide()) {
-                  registerToConstant.remove(outRegister + 1);
-                } else {
-                  removeWideConstantCovering(registerToConstant, outRegister);
-                }
+                removeWideConstantCovering(registerToNumber, outRegister);
               }
             }
           } else {
@@ -308,40 +305,32 @@
             // from the mapping.
             int outRegister = allocator.getRegisterForValue(outValue, instructionNumber);
             for (int i = 0; i < outValue.requiredRegisters(); i++) {
-              registerToConstant.remove(outRegister + i);
+              registerToNumber.remove(outRegister + i);
             }
             // Check if the first register written is the second part of a wide value. If so
             // the wide value is no longer active.
-            removeWideConstantCovering(registerToConstant, outRegister);
+            removeWideConstantCovering(registerToNumber, outRegister);
           }
         }
       }
-
-      if (mayNoLongerThrow && block.hasCatchHandlers() && !block.canThrow()) {
-        block.clearCatchHandlers();
-      }
     }
   }
 
   private static void removeWideConstantCovering(
-      Map<Integer, ConstInstruction> registerToConstant, int register) {
-    ConstInstruction constant = registerToConstant.get(register - 1);
-    if (constant != null && constant.outType().isWide()) {
-      registerToConstant.remove(register - 1);
+      Map<Integer, ConstNumber> registerToNumber, int register) {
+    ConstNumber number = registerToNumber.get(register - 1);
+    if (number != null && number.outType().isWide()) {
+      registerToNumber.remove(register - 1);
     }
   }
 
   private static boolean constantSpilledAtDefinition(
-      ConstInstruction constInstruction, LinearScanRegisterAllocator allocator) {
-    assert constInstruction.isConstNumber() || constInstruction.isConstString();
-    if (constInstruction.outValue().isFixedRegisterValue()) {
+      ConstNumber constNumber, LinearScanRegisterAllocator allocator) {
+    if (constNumber.outValue().isFixedRegisterValue()) {
       return false;
     }
     LiveIntervals definitionIntervals =
-        constInstruction
-            .outValue()
-            .getLiveIntervals()
-            .getSplitCovering(constInstruction.getNumber());
+        constNumber.outValue().getLiveIntervals().getSplitCovering(constNumber.getNumber());
     return definitionIntervals.isSpilledAndRematerializable(allocator);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
index e5c19d9..769c281 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
@@ -672,7 +672,7 @@
       // const number instructions are for values that can be rematerialized instead of
       // spilled.
       assert instruction.getNumber() == -1;
-      assert instruction.isMove() || instruction.isConstNumber() || instruction.isConstString();
+      assert instruction.isMove() || instruction.isConstNumber();
       assert !instruction.isDebugInstruction();
       return true;
     }
@@ -1927,11 +1927,8 @@
         newActive.add(splitChild);
         // If the constant is split before its first actual use, mark the constant as being
         // spilled. That will allows us to remove it afterwards if it is rematerializable.
-        Value intervalsValue = intervals.getValue();
-        boolean isRematerializableConstantValue =
-            intervalsValue.isConstNumber() || intervalsValue.isConstString();
-        if (isRematerializableConstantValue
-            && intervals.getStart() == intervalsValue.definition.getNumber()
+        if (intervals.getValue().isConstNumber()
+            && intervals.getStart() == intervals.getValue().definition.getNumber()
             && intervals.getUses().size() == 1) {
           intervals.setSpilled(true);
         }
@@ -1941,7 +1938,9 @@
             LiveIntervals splitOfSplit = splitChild.splitBefore(splitChild.getFirstUse());
             splitOfSplit.setRegister(intervals.getRegister());
             inactive.add(splitOfSplit);
-          } else if (isRematerializableConstantValue) {
+          } else if (intervals.getValue().isConstNumber()) {
+            // TODO(ager): Do this for all constants. Currently we only rematerialize const
+            // number and therefore we only do it for numbers at this point.
             splitRangesForSpilledConstant(splitChild, registerNumber);
           } else if (intervals.isArgumentInterval()) {
             splitRangesForSpilledArgument(splitChild);
@@ -1972,7 +1971,6 @@
     // register for as long as possible to avoid further moves.
     assert spilled.isSpilled();
     assert !spilled.getValue().isConstNumber();
-    assert !spilled.getValue().isConstString();
     assert !spilled.isLinked() || spilled.isArgumentInterval();
     boolean isSpillingToArgumentRegister =
         (spilled.isArgumentInterval() || registerNumber < numberOfArgumentRegisters);
@@ -2005,7 +2003,7 @@
     // spill we are running low on registers and this constant should get out of the way
     // as much as possible.
     assert spilled.isSpilled();
-    assert spilled.getValue().isConstNumber() || spilled.getValue().isConstString();
+    assert spilled.getValue().isConstNumber();
     assert !spilled.isLinked() || spilled.isArgumentInterval();
     // Do not split range if constant is reused by one of the eleven following instruction.
     int maxGapSize = 11 * INSTRUCTION_NUMBER_DELTA;
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/LiveIntervals.java b/src/main/java/com/android/tools/r8/ir/regalloc/LiveIntervals.java
index d7c6588..7e398e6 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/LiveIntervals.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/LiveIntervals.java
@@ -99,8 +99,8 @@
     if (value.isArgument()) {
       return true;
     }
-    boolean isRematerializableConstantValue = value.isConstNumber() || value.isConstString();
-    if (!isRematerializableConstantValue) {
+    // TODO(ager): rematerialize const string as well.
+    if (!value.isConstNumber()) {
       return false;
     }
     // If one of the non-spilled splits uses a register that is higher than U8BIT_MAX we cannot
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMoveScheduler.java b/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMoveScheduler.java
index b1e5e4f..d1fe04c 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMoveScheduler.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMoveScheduler.java
@@ -8,7 +8,6 @@
 import com.android.tools.r8.ir.code.Argument;
 import com.android.tools.r8.ir.code.ConstInstruction;
 import com.android.tools.r8.ir.code.ConstNumber;
-import com.android.tools.r8.ir.code.ConstString;
 import com.android.tools.r8.ir.code.FixedRegisterValue;
 import com.android.tools.r8.ir.code.Instruction;
 import com.android.tools.r8.ir.code.InstructionListIterator;
@@ -141,12 +140,10 @@
         instruction = new Move(to, from);
       } else {
         assert move.definition.isOutConstant();
-        Value to = new FixedRegisterValue(move.definition.outType(), move.dst);
         ConstInstruction definition = move.definition.getOutConstantConstInstruction();
         if (definition.isConstNumber()) {
+          Value to = new FixedRegisterValue(move.definition.outType(), move.dst);
           instruction = new ConstNumber(to, definition.asConstNumber().getRawValue());
-        } else if (definition.isConstString()) {
-          instruction = new ConstString(to, definition.asConstString().getValue());
         } else {
           throw new Unreachable("Unexpected definition");
         }
diff --git a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
index a7b3f77..e49ad85 100644
--- a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
@@ -13,6 +13,7 @@
 import com.android.tools.r8.graph.DexAnnotation;
 import com.android.tools.r8.graph.DexAnnotationElement;
 import com.android.tools.r8.graph.DexAnnotationSet;
+import com.android.tools.r8.graph.DexAnnotationSetRefList;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexEncodedAnnotation;
 import com.android.tools.r8.graph.DexEncodedField;
@@ -32,6 +33,7 @@
 import com.android.tools.r8.graph.DexValue.DexValueType;
 import com.android.tools.r8.graph.DexValue.UnknownDexValue;
 import com.android.tools.r8.graph.InnerClassAttribute;
+import com.android.tools.r8.graph.JarClassFileReader;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.naming.ProguardMapSupplier;
 import com.android.tools.r8.utils.ExceptionUtils;
@@ -217,10 +219,10 @@
   }
 
   private Object getStaticValue(DexEncodedField field) {
-    if (!field.accessFlags.isStatic() || field.staticValue == null) {
+    if (!field.accessFlags.isStatic() || !field.hasExplicitStaticValue()) {
       return null;
     }
-    return field.staticValue.asAsmEncodedObject();
+    return field.getStaticValue().asAsmEncodedObject();
   }
 
   private void writeField(DexEncodedField field, ClassWriter writer) {
@@ -250,18 +252,31 @@
       }
     }
     writeAnnotations(visitor::visitAnnotation, method.annotations.annotations);
-    for (int i = 0; i < method.parameterAnnotations.values.length; i++) {
-      final int iFinal = i;
-      writeAnnotations(
-          (d, vis) -> visitor.visitParameterAnnotation(iFinal, d, vis),
-          method.parameterAnnotations.values[i].annotations);
-    }
+    writeParameterAnnotations(visitor, method.parameterAnnotations);
     if (!method.accessFlags.isAbstract() && !method.accessFlags.isNative()) {
       writeCode(method.getCode(), visitor);
     }
     visitor.visitEnd();
   }
 
+  private void writeParameterAnnotations(
+      MethodVisitor visitor, DexAnnotationSetRefList parameterAnnotations) {
+    int missingParameterAnnotations = parameterAnnotations.getMissingParameterAnnotations();
+    for (int i = 0; i < missingParameterAnnotations; i++) {
+      AnnotationVisitor av =
+          visitor.visitParameterAnnotation(i, JarClassFileReader.SYNTHETIC_ANNOTATION, false);
+      if (av != null) {
+        av.visitEnd();
+      }
+    }
+    for (int i = 0; i < parameterAnnotations.values.length; i++) {
+      int parameterIndex = i + missingParameterAnnotations;
+      writeAnnotations(
+          (d, vis) -> visitor.visitParameterAnnotation(parameterIndex, d, vis),
+          parameterAnnotations.values[i].annotations);
+    }
+  }
+
   private interface AnnotationConsumer {
     AnnotationVisitor visit(String desc, boolean visible);
   }
diff --git a/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java b/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java
index f766061..2fd66d0 100644
--- a/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java
@@ -19,6 +19,7 @@
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexValue;
 import com.android.tools.r8.graph.DexValue.DexValueString;
 import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.ProguardClassFilter;
@@ -62,13 +63,17 @@
   }
 
   private void adaptClassStringsInField(DexEncodedField encodedField) {
-    if (!(encodedField.staticValue instanceof DexValueString)) {
+    if (!encodedField.accessFlags.isStatic()) {
       return;
     }
-    DexString original = ((DexValueString) encodedField.staticValue).getValue();
+    DexValue staticValue = encodedField.getStaticValue();
+    if (!(staticValue instanceof DexValueString)) {
+      return;
+    }
+    DexString original = ((DexValueString) staticValue).getValue();
     DexString renamed = getRenamedStringLiteral(original);
     if (renamed != original) {
-      encodedField.staticValue = new DexValueString(renamed);
+      encodedField.setStaticValue(new DexValueString(renamed));
     }
   }
 
@@ -123,12 +128,16 @@
   }
 
   private void replaceIdentifierNameStringInField(DexEncodedField encodedField) {
-    if (!(encodedField.staticValue instanceof DexValueString)) {
+    if (!encodedField.accessFlags.isStatic()) {
       return;
     }
-    DexString original = ((DexValueString) encodedField.staticValue).getValue();
+    DexValue staticValue = encodedField.getStaticValue();
+    if (!(staticValue instanceof DexValueString)) {
+      return;
+    }
+    DexString original = ((DexValueString) staticValue).getValue();
     if (original instanceof DexItemBasedString) {
-      encodedField.staticValue = new DexValueString(materialize((DexItemBasedString) original));
+      encodedField.setStaticValue(new DexValueString(materialize((DexItemBasedString) original)));
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
index f6e087b..57e8514 100644
--- a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
+++ b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
@@ -18,6 +18,7 @@
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexValue;
 import com.android.tools.r8.graph.DexValue.DexValueString;
 import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.ConstString;
@@ -61,13 +62,17 @@
     if (!identifierNameStrings.contains(encodedField.field)) {
       return;
     }
-    if (!(encodedField.staticValue instanceof DexValueString)) {
+    if (!encodedField.accessFlags.isStatic()) {
       return;
     }
-    DexString original = ((DexValueString) encodedField.staticValue).getValue();
+    DexValue staticValue = encodedField.getStaticValue();
+    if (!(staticValue instanceof DexValueString)) {
+      return;
+    }
+    DexString original = ((DexValueString) staticValue).getValue();
     DexItemBasedString itemBasedString = inferMemberOrTypeFromNameString(appInfo, original);
     if (itemBasedString != null) {
-      encodedField.staticValue = new DexValueString(itemBasedString);
+      encodedField.setStaticValue(new DexValueString(itemBasedString));
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardAlwaysInlineRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardAlwaysInlineRule.java
index ab8342d..8d4a14b 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardAlwaysInlineRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardAlwaysInlineRule.java
@@ -3,7 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking;
 
-import java.util.Set;
+import java.util.List;
 
 public class ProguardAlwaysInlineRule extends ProguardConfigurationRule {
 
@@ -29,7 +29,7 @@
       ProguardTypeMatcher inheritanceAnnotation,
       ProguardTypeMatcher inheritanceClassName,
       boolean inheritanceIsExtends,
-      Set<ProguardMemberRule> memberRules) {
+      List<ProguardMemberRule> memberRules) {
     super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
         classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
   }
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardAssumeNoSideEffectRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardAssumeNoSideEffectRule.java
index 736c778..39d0335 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardAssumeNoSideEffectRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardAssumeNoSideEffectRule.java
@@ -3,7 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking;
 
-import java.util.Set;
+import java.util.List;
 
 public class ProguardAssumeNoSideEffectRule extends ProguardConfigurationRule {
 
@@ -28,7 +28,7 @@
       ProguardTypeMatcher inheritanceAnnotation,
       ProguardTypeMatcher inheritanceClassName,
       boolean inheritanceIsExtends,
-      Set<ProguardMemberRule> memberRules) {
+      List<ProguardMemberRule> memberRules) {
     super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
         classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
   }
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardAssumeValuesRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardAssumeValuesRule.java
index 97cb5c3..0dafb11 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardAssumeValuesRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardAssumeValuesRule.java
@@ -3,7 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking;
 
-import java.util.Set;
+import java.util.List;
 
 public class ProguardAssumeValuesRule extends ProguardConfigurationRule {
   public static class Builder extends ProguardClassSpecification.Builder {
@@ -27,7 +27,7 @@
       ProguardTypeMatcher inheritanceAnnotation,
       ProguardTypeMatcher inheritanceClassName,
       boolean inheritanceIsExtends,
-      Set<ProguardMemberRule> memberRules) {
+      List<ProguardMemberRule> memberRules) {
     super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
         classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
   }
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardCheckDiscardRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardCheckDiscardRule.java
index da5bb26..2461732 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardCheckDiscardRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardCheckDiscardRule.java
@@ -3,7 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking;
 
-import java.util.Set;
+import java.util.List;
 
 public class ProguardCheckDiscardRule extends ProguardConfigurationRule {
 
@@ -29,7 +29,7 @@
       ProguardTypeMatcher inheritanceAnnotation,
       ProguardTypeMatcher inheritanceClassName,
       boolean inheritanceIsExtends,
-      Set<ProguardMemberRule> memberRules) {
+      List<ProguardMemberRule> memberRules) {
     super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
         classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
   }
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 0a71f43..fd1d305 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardClassNameList.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardClassNameList.java
@@ -14,6 +14,7 @@
 import java.util.List;
 import java.util.function.Consumer;
 import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
 
 public abstract class ProguardClassNameList {
 
@@ -74,6 +75,10 @@
 
   public abstract boolean matches(DexType type);
 
+  protected Iterable<String> getWildcards() {
+    return ImmutableList.of();
+  }
+
   public abstract void forEachTypeMatcher(Consumer<ProguardTypeMatcher> consumer);
 
   private static class EmptyClassNameList extends ProguardClassNameList {
@@ -135,6 +140,11 @@
     }
 
     @Override
+    protected Iterable<String> getWildcards() {
+      return className.getWildcards();
+    }
+
+    @Override
     public void forEachTypeMatcher(Consumer<ProguardTypeMatcher> consumer) {
       consumer.accept(className);
     }
@@ -180,6 +190,14 @@
     }
 
     @Override
+    protected Iterable<String> getWildcards() {
+      return classNames.stream()
+          .map(ProguardTypeMatcher::getWildcards)
+          .flatMap(it -> StreamSupport.stream(it.spliterator(), false))
+          .collect(Collectors.toList());
+    }
+
+    @Override
     public void forEachTypeMatcher(Consumer<ProguardTypeMatcher> consumer) {
       classNames.forEach(consumer);
     }
@@ -230,6 +248,14 @@
     }
 
     @Override
+    protected Iterable<String> getWildcards() {
+      return classNames.keySet().stream()
+          .map(ProguardTypeMatcher::getWildcards)
+          .flatMap(it -> StreamSupport.stream(it.spliterator(), false))
+          .collect(Collectors.toList());
+    }
+
+    @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/ProguardClassSpecification.java b/src/main/java/com/android/tools/r8/shaking/ProguardClassSpecification.java
index cb81639..653f0cf 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardClassSpecification.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardClassSpecification.java
@@ -4,10 +4,10 @@
 package com.android.tools.r8.shaking;
 
 import com.android.tools.r8.utils.StringUtils;
-import java.util.Collections;
-import java.util.LinkedHashSet;
+import com.google.common.collect.ImmutableList;
+import java.util.LinkedList;
+import java.util.List;
 import java.util.Objects;
-import java.util.Set;
 
 public abstract class ProguardClassSpecification {
 
@@ -22,16 +22,16 @@
     protected ProguardTypeMatcher inheritanceAnnotation;
     protected ProguardTypeMatcher inheritanceClassName;
     protected boolean inheritanceIsExtends = false;
-    protected Set<ProguardMemberRule> memberRules = new LinkedHashSet<>();
+    protected List<ProguardMemberRule> memberRules = new LinkedList<>();
 
     protected Builder() {
     }
 
-    public Set<ProguardMemberRule> getMemberRules() {
+    public List<ProguardMemberRule> getMemberRules() {
       return memberRules;
     }
 
-    public void setMemberRules(Set<ProguardMemberRule> memberRules) {
+    public void setMemberRules(List<ProguardMemberRule> memberRules) {
       this.memberRules = memberRules;
     }
 
@@ -113,7 +113,7 @@
 
     protected void matchAllSpecification() {
       setClassNames(ProguardClassNameList.singletonList(ProguardTypeMatcher.defaultAllMatcher()));
-      setMemberRules(Collections.singleton(ProguardMemberRule.defaultKeepAllRule()));
+      setMemberRules(ImmutableList.of(ProguardMemberRule.defaultKeepAllRule()));
     }
   }
 
@@ -126,7 +126,7 @@
   private final ProguardTypeMatcher inheritanceAnnotation;
   private final ProguardTypeMatcher inheritanceClassName;
   private final boolean inheritanceIsExtends;
-  private final Set<ProguardMemberRule> memberRules;
+  private final List<ProguardMemberRule> memberRules;
 
   protected ProguardClassSpecification(
       ProguardTypeMatcher classAnnotation,
@@ -138,7 +138,7 @@
       ProguardTypeMatcher inheritanceAnnotation,
       ProguardTypeMatcher inheritanceClassName,
       boolean inheritanceIsExtends,
-      Set<ProguardMemberRule> memberRules) {
+      List<ProguardMemberRule> memberRules) {
     this.classAnnotation = classAnnotation;
     this.classAccessFlags = classAccessFlags;
     this.negatedClassAccessFlags = negatedClassAccessFlags;
@@ -152,7 +152,7 @@
     this.memberRules = memberRules;
   }
 
-  public Set<ProguardMemberRule> getMemberRules() {
+  public List<ProguardMemberRule> getMemberRules() {
     return memberRules;
   }
 
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 9b89374..568ba54 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -3,6 +3,8 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking;
 
+import static com.android.tools.r8.utils.DescriptorUtils.javaTypeToDescriptor;
+
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexItemFactory;
@@ -16,7 +18,6 @@
 import com.android.tools.r8.shaking.ProguardConfiguration.Builder;
 import com.android.tools.r8.shaking.ProguardTypeMatcher.ClassOrType;
 import com.android.tools.r8.shaking.ProguardTypeMatcher.MatchSpecificType;
-import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.IdentifierUtils;
 import com.android.tools.r8.utils.InternalOptions.PackageObfuscationMode;
 import com.android.tools.r8.utils.LongInterval;
@@ -33,9 +34,12 @@
 import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Iterator;
 import java.util.List;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 public class ProguardConfigurationParser {
 
@@ -511,7 +515,8 @@
         // If there are no member rules, a default rule for the parameterless constructor
         // applies. So we add that here.
         ProguardMemberRule.Builder defaultRuleBuilder = ProguardMemberRule.builder();
-        defaultRuleBuilder.setName(Constants.INSTANCE_INITIALIZER_NAME);
+        defaultRuleBuilder.setName(
+            IdentifierPatternWithWildcards.withoutWildcards(Constants.INSTANCE_INITIALIZER_NAME));
         defaultRuleBuilder.setRuleType(ProguardMemberType.INIT);
         defaultRuleBuilder.setArguments(Collections.emptyList());
         keepRuleBuilder.getMemberRules().add(defaultRuleBuilder.build());
@@ -565,10 +570,33 @@
       if (acceptString("-keep")) {
         ProguardKeepRule subsequentRule = parseKeepRule();
         ifRuleBuilder.setSubsequentRule(subsequentRule);
-        return ifRuleBuilder.build();
+        ProguardIfRule ifRule = ifRuleBuilder.build();
+        verifyWildcardRange(ifRule.getWildcards());
+        return ifRule;
       }
       throw reporter.fatalError(new StringDiagnostic(
-          "Option -if without a subsequent keep rule.", origin, getPosition(optionStart)));
+          "Expecting '-keep' option after '-if' option.", origin, getPosition(optionStart)));
+    }
+
+    private void verifyWildcardRange(Iterable<String> wildcards) {
+      Pattern backReference = Pattern.compile("<(\\d+)>");
+      int i = 1;
+      Iterator<String> iterator = wildcards.iterator();
+      while (iterator.hasNext()) {
+        String wildcard = iterator.next();
+        Matcher m = backReference.matcher(wildcard);
+        if (m.matches()) {
+          int n = Integer.parseInt(m.group(1));
+          if (i <= n) {
+            throw reporter.fatalError(new StringDiagnostic(
+                "Wildcard <" + n + "> is invalid.", origin, getPosition()));
+          }
+        } else {
+          // Increase the index of wildcards for non-back-reference only
+          // to not allow one back-reference to point to another back-reference
+          i++;
+        }
+      }
     }
 
     private void parseClassSpec(
@@ -639,14 +667,16 @@
       skipWhitespace();
       int startPosition = position;
       if (acceptChar('@')) {
-        String className = parseClassName();
+        IdentifierPatternWithWildcards identifierPatternWithWildcards = parseClassName();
+        String className = identifierPatternWithWildcards.pattern;
         if (className.equals("interface")) {
           // Not an annotation after all but a class type. Move position back to start
           // so this can be dealt with as a class type instead.
           position = startPosition;
           return null;
         }
-        return ProguardTypeMatcher.create(className, ClassOrType.CLASS, dexItemFactory);
+        return ProguardTypeMatcher.create(
+            identifierPatternWithWildcards, ClassOrType.CLASS, dexItemFactory);
       }
       return null;
     }
@@ -815,13 +845,14 @@
         ruleBuilder.setRuleType(ProguardMemberType.ALL_FIELDS);
       } else if (acceptString("<init>")) {
         ruleBuilder.setRuleType(ProguardMemberType.INIT);
-        ruleBuilder.setName("<init>");
+        ruleBuilder.setName(IdentifierPatternWithWildcards.withoutWildcards("<init>"));
         ruleBuilder.setArguments(parseArgumentList());
       } else {
-        String first = acceptIdentifierWithBackreference(IdentifierType.ANY);
+        IdentifierPatternWithWildcards first =
+            acceptIdentifierWithBackreference(IdentifierType.ANY);
         if (first != null) {
           skipWhitespace();
-          if (first.equals("*") && hasNextChar(';')) {
+          if (first.pattern.equals("*") && hasNextChar(';')) {
             ruleBuilder.setRuleType(ProguardMemberType.ALL);
           } else {
             if (hasNextChar('(')) {
@@ -829,7 +860,8 @@
               ruleBuilder.setName(first);
               ruleBuilder.setArguments(parseArgumentList());
             } else {
-              String second = acceptIdentifierWithBackreference(IdentifierType.ANY);
+              IdentifierPatternWithWildcards second =
+                  acceptIdentifierWithBackreference(IdentifierType.ANY);
               if (second != null) {
                 skipWhitespace();
                 if (hasNextChar('(')) {
@@ -880,7 +912,7 @@
                               .getTypeMatcher()).type;
                           DexType fieldClass =
                               dexItemFactory.createType(
-                                  DescriptorUtils.javaTypeToDescriptor(
+                                  javaTypeToDescriptor(
                                       qualifiedFieldNameOrInteger.substring(0, lastDotIndex)));
                           DexString fieldName =
                               dexItemFactory.createString(
@@ -918,13 +950,16 @@
         return arguments;
       }
       if (acceptString("...")) {
-        arguments
-            .add(ProguardTypeMatcher.create("...", ClassOrType.TYPE, dexItemFactory));
+        arguments.add(ProguardTypeMatcher.create(
+            IdentifierPatternWithWildcards.withoutWildcards("..."),
+            ClassOrType.TYPE,
+            dexItemFactory));
       } else {
-        for (String name = parseClassName(); name != null; name =
-            acceptChar(',') ? parseClassName() : null) {
-          arguments
-              .add(ProguardTypeMatcher.create(name, ClassOrType.TYPE, dexItemFactory));
+        for (IdentifierPatternWithWildcards identifierPatternWithWildcards = parseClassName();
+            identifierPatternWithWildcards != null;
+            identifierPatternWithWildcards = acceptChar(',') ? parseClassName() : null) {
+          arguments.add(ProguardTypeMatcher.create(
+              identifierPatternWithWildcards, ClassOrType.TYPE, dexItemFactory));
           skipWhitespace();
         }
       }
@@ -1126,13 +1161,17 @@
       return acceptString(CLASS_NAME_PREDICATE);
     }
 
-    private String acceptIdentifierWithBackreference(IdentifierType kind) {
+    private IdentifierPatternWithWildcards acceptIdentifierWithBackreference(IdentifierType kind) {
+      ImmutableList.Builder<String> wildcardsCollector = ImmutableList.builder();
+      StringBuilder currentAsterisks = null;
       StringBuilder currentBackreference = null;
       skipWhitespace();
       int start = position;
       int end = position;
       while (!eof(end)) {
         int current = contents.codePointAt(end);
+        // Should not be both in asterisk collecting state and back reference collecting state.
+        assert currentAsterisks == null || currentBackreference == null;
         if (currentBackreference != null) {
           if (current == '>') {
             try {
@@ -1146,29 +1185,55 @@
                   "Wildcard <" + currentBackreference.toString() + "> is invalid.",
                   origin, getPosition()));
             }
+            wildcardsCollector.add("<" + currentBackreference.toString() + ">");
             currentBackreference = null;
+            end += Character.charCount(current);
+            continue;
           } else if (('0' <= current && current <= '9')
-              // Only collect integer literal for the backreference.
+              // Only collect integer literal for the back reference.
               || (current == '-' && currentBackreference.length() == 0)) {
             currentBackreference.append((char) current);
+            end += Character.charCount(current);
+            continue;
           } else if (kind == IdentifierType.CLASS_NAME) {
             throw reporter.fatalError(new StringDiagnostic(
                 "Use of generics not allowed for java type.", origin, getPosition()));
           } else {
             // If not parsing a class name allow identifiers including <'s by canceling the
-            // collection of the backreference.
+            // collection of the back reference.
             currentBackreference = null;
           }
+        } else if (currentAsterisks != null) {
+          if (current == '*') {
+            currentAsterisks.append((char) current);
+            end += Character.charCount(current);
+            continue;
+          } else {
+            wildcardsCollector.add(currentAsterisks.toString());
+            currentAsterisks = null;
+          }
+        }
+        // From now on, neither in asterisk collecting state nor back reference collecting state.
+        assert currentAsterisks == null && currentBackreference == null;
+        if (current == '*') {
+          currentAsterisks = new StringBuilder();
+          currentAsterisks.append((char) current);
+          end += Character.charCount(current);
+        } else if (current == '?' || current == '%') {
+          wildcardsCollector.add(String.valueOf((char) current));
           end += Character.charCount(current);
         } else if (CLASS_NAME_PREDICATE.test(current) || current == '>') {
           end += Character.charCount(current);
         } else if (current == '<') {
           currentBackreference = new StringBuilder();
-          ++end;
+          end += Character.charCount(current);
         } else {
           break;
         }
       }
+      if (currentAsterisks != null) {
+        wildcardsCollector.add(currentAsterisks.toString());
+      }
       if (kind == IdentifierType.CLASS_NAME && currentBackreference != null) {
         // Proguard 6 reports this error message, so try to be compatible.
         throw reporter.fatalError(
@@ -1178,7 +1243,9 @@
         return null;
       }
       position = end;
-      return contents.substring(start, end);
+      return new IdentifierPatternWithWildcards(
+          contents.substring(start, end),
+          wildcardsCollector.build());
     }
 
     private String acceptFieldNameOrIntegerForReturn() {
@@ -1292,8 +1359,9 @@
       return name == null ? "" : name;
     }
 
-    private String parseClassName() throws ProguardRuleParserException {
-      String name = acceptIdentifierWithBackreference(IdentifierType.CLASS_NAME);
+    private IdentifierPatternWithWildcards parseClassName() throws ProguardRuleParserException {
+      IdentifierPatternWithWildcards name =
+          acceptIdentifierWithBackreference(IdentifierType.CLASS_NAME);
       if (name == null) {
         throw parseError("Class name expected");
       }
@@ -1408,4 +1476,22 @@
       return position - lineStartPosition + 1 /* column starts at 1 */;
     }
   }
+
+  static class IdentifierPatternWithWildcards {
+    final String pattern;
+    final List<String> wildcards;
+
+    IdentifierPatternWithWildcards(String pattern, List<String> wildcards) {
+      this.pattern = pattern;
+      this.wildcards = wildcards;
+    }
+
+    static IdentifierPatternWithWildcards withoutWildcards(String pattern) {
+      return new IdentifierPatternWithWildcards(pattern, ImmutableList.of());
+    }
+
+    boolean isMatchAllNames() {
+      return pattern.equals("*");
+    }
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java
index af1948c..9791def 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java
@@ -4,7 +4,11 @@
 package com.android.tools.r8.shaking;
 
 import com.android.tools.r8.utils.StringUtils;
-import java.util.Set;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
 
 public abstract class ProguardConfigurationRule extends ProguardClassSpecification {
   ProguardConfigurationRule(
@@ -17,7 +21,7 @@
       ProguardTypeMatcher inheritanceAnnotation,
       ProguardTypeMatcher inheritanceClassName,
       boolean inheritanceIsExtends,
-      Set<ProguardMemberRule> memberRules) {
+      List<ProguardMemberRule> memberRules) {
     super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
         classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
   }
@@ -32,6 +36,25 @@
     return false;
   }
 
+  protected Iterable<String> getWildcards() {
+    ProguardTypeMatcher classAnnotation = getClassAnnotation();
+    ProguardTypeMatcher inheritanceAnnotation = getInheritanceAnnotation();
+    ProguardTypeMatcher inheritanceClassName = getInheritanceClassName();
+    List<ProguardMemberRule> memberRules = getMemberRules();
+    return Iterables.concat(
+        classAnnotation != null ? classAnnotation.getWildcards() : ImmutableList.of(),
+        getClassNames().getWildcards(),
+        inheritanceAnnotation != null ? inheritanceAnnotation.getWildcards() : ImmutableList.of(),
+        inheritanceClassName != null ? inheritanceClassName.getWildcards() : ImmutableList.of(),
+        memberRules != null
+            ? memberRules.stream()
+                .map(ProguardMemberRule::getWildcards)
+                .flatMap(it -> StreamSupport.stream(it.spliterator(), false))
+                .collect(Collectors.toList())
+            : ImmutableList.of()
+    );
+  }
+
   @Override
   public boolean equals(Object o) {
     if (!(o instanceof ProguardConfigurationRule)) {
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
index 5852cd9..400422f 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
@@ -11,6 +11,7 @@
 import com.android.tools.r8.graph.DexItem;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.shaking.ProguardConfigurationParser.IdentifierPatternWithWildcards;
 import com.google.common.collect.ImmutableList;
 import java.util.Arrays;
 import java.util.List;
@@ -30,7 +31,7 @@
     if (clazz.hasDefaultInitializer()) {
       ProguardMemberRule.Builder memberRuleBuilder = ProguardMemberRule.builder();
       memberRuleBuilder.setRuleType(ProguardMemberType.INIT);
-      memberRuleBuilder.setName("<init>");
+      memberRuleBuilder.setName(IdentifierPatternWithWildcards.withoutWildcards("<init>"));
       memberRuleBuilder.setArguments(ImmutableList.of());
       builder.getMemberRules().add(memberRuleBuilder.build());
     }
@@ -54,7 +55,8 @@
     ProguardMemberRule.Builder memberRuleBuilder = ProguardMemberRule.builder();
     memberRuleBuilder.setRuleType(ProguardMemberType.FIELD);
     memberRuleBuilder.getAccessFlags().setFlags(field.accessFlags);
-    memberRuleBuilder.setName(field.field.name.toString());
+    memberRuleBuilder.setName(
+        IdentifierPatternWithWildcards.withoutWildcards(field.field.name.toString()));
     memberRuleBuilder.setTypeMatcher(ProguardTypeMatcher.create(field.field.type));
     builder.getMemberRules().add(memberRuleBuilder.build());
     return builder.build();
@@ -77,7 +79,8 @@
     ProguardMemberRule.Builder memberRuleBuilder = ProguardMemberRule.builder();
     memberRuleBuilder.setRuleType(ProguardMemberType.METHOD);
     memberRuleBuilder.getAccessFlags().setFlags(method.accessFlags);
-    memberRuleBuilder.setName(method.method.name.toString());
+    memberRuleBuilder.setName(
+        IdentifierPatternWithWildcards.withoutWildcards(method.method.name.toString()));
     memberRuleBuilder.setTypeMatcher(ProguardTypeMatcher.create(method.method.proto.returnType));
     List<ProguardTypeMatcher> arguments = Arrays.stream(method.method.proto.parameters.values)
         .map(ProguardTypeMatcher::create)
@@ -96,13 +99,15 @@
       DexField field = (DexField) item;
       holderType = field.getHolder();
       memberRuleBuilder.setRuleType(ProguardMemberType.FIELD);
-      memberRuleBuilder.setName(field.name.toString());
+      memberRuleBuilder.setName(
+          IdentifierPatternWithWildcards.withoutWildcards(field.name.toString()));
       memberRuleBuilder.setTypeMatcher(ProguardTypeMatcher.create(field.type));
     } else {
       DexMethod method = (DexMethod) item;
       holderType = method.getHolder();
       memberRuleBuilder.setRuleType(ProguardMemberType.METHOD);
-      memberRuleBuilder.setName(method.name.toString());
+      memberRuleBuilder.setName(
+          IdentifierPatternWithWildcards.withoutWildcards(method.name.toString()));
       memberRuleBuilder.setTypeMatcher(ProguardTypeMatcher.create(method.proto.returnType));
       List<ProguardTypeMatcher> arguments = Arrays.stream(method.proto.parameters.values)
           .map(ProguardTypeMatcher::create)
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardIdentifierNameStringRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardIdentifierNameStringRule.java
index f0d7c63..07ffd85 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardIdentifierNameStringRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardIdentifierNameStringRule.java
@@ -3,7 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking;
 
-import java.util.Set;
+import java.util.List;
 
 public class ProguardIdentifierNameStringRule extends ProguardConfigurationRule {
 
@@ -28,7 +28,7 @@
       ProguardTypeMatcher inheritanceAnnotation,
       ProguardTypeMatcher inheritanceClassName,
       boolean inheritanceIsExtends,
-      Set<ProguardMemberRule> memberRules) {
+      List<ProguardMemberRule> memberRules) {
     super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
         classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
   }
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java
index 9659954..67a0572 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java
@@ -3,7 +3,8 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking;
 
-import java.util.Set;
+import com.google.common.collect.Iterables;
+import java.util.List;
 
 public class ProguardIfRule extends ProguardKeepRule {
 
@@ -32,7 +33,7 @@
       ProguardClassType classType, ProguardClassNameList classNames,
       ProguardTypeMatcher inheritanceAnnotation,
       ProguardTypeMatcher inheritanceClassName, boolean inheritanceIsExtends,
-      Set<ProguardMemberRule> memberRules,
+      List<ProguardMemberRule> memberRules,
       ProguardKeepRule subsequentRule) {
     super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
         classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules,
@@ -45,6 +46,11 @@
   }
 
   @Override
+  protected Iterable<String> getWildcards() {
+    return Iterables.concat(super.getWildcards(), subsequentRule.getWildcards());
+  }
+
+  @Override
   public boolean equals(Object o) {
     if (!(o instanceof ProguardIfRule)) {
       return false;
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardKeepPackageNamesRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardKeepPackageNamesRule.java
index c4731c6..66fdd50 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardKeepPackageNamesRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardKeepPackageNamesRule.java
@@ -3,7 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking;
 
-import java.util.Set;
+import java.util.List;
 
 public class ProguardKeepPackageNamesRule extends ProguardConfigurationRule {
 
@@ -29,7 +29,7 @@
       ProguardTypeMatcher inheritanceAnnotation,
       ProguardTypeMatcher inheritanceClassName,
       boolean inheritanceIsExtends,
-      Set<ProguardMemberRule> memberRules) {
+      List<ProguardMemberRule> memberRules) {
     super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
         classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
   }
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardKeepRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRule.java
index d3d6705..8756f1a 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardKeepRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRule.java
@@ -3,7 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking;
 
-import java.util.Set;
+import java.util.List;
 import java.util.function.Consumer;
 
 public class ProguardKeepRule extends ProguardConfigurationRule {
@@ -11,8 +11,8 @@
   public static class Builder extends ProguardClassSpecification.Builder {
 
     private ProguardKeepRuleType type;
-    private final ProguardKeepRuleModifiers.Builder modifiersBuilder
-        = ProguardKeepRuleModifiers.builder();
+    private final ProguardKeepRuleModifiers.Builder modifiersBuilder =
+        ProguardKeepRuleModifiers.builder();
 
     protected Builder() {}
 
@@ -44,7 +44,7 @@
       ProguardTypeMatcher inheritanceAnnotation,
       ProguardTypeMatcher inheritanceClassName,
       boolean inheritanceIsExtends,
-      Set<ProguardMemberRule> memberRules,
+      List<ProguardMemberRule> memberRules,
       ProguardKeepRuleType type,
       ProguardKeepRuleModifiers modifiers) {
     super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardMemberRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardMemberRule.java
index 5e1293f..1e089a9 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardMemberRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardMemberRule.java
@@ -7,9 +7,14 @@
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.shaking.ProguardConfigurationParser.IdentifierPatternWithWildcards;
 import com.android.tools.r8.utils.StringUtils;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import java.util.Collections;
 import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
 
 public class ProguardMemberRule {
 
@@ -19,8 +24,8 @@
     private ProguardAccessFlags accessFlags = new ProguardAccessFlags();
     private ProguardAccessFlags negatedAccessFlags = new ProguardAccessFlags();
     private ProguardMemberType ruleType;
-    private ProguardNameMatcher name;
     private ProguardTypeMatcher type;
+    private ProguardNameMatcher name;
     private List<ProguardTypeMatcher> arguments;
     private ProguardMemberRuleReturnValue returnValue;
 
@@ -50,10 +55,6 @@
       this.ruleType = ruleType;
     }
 
-    public void setName(String name) {
-      this.name = ProguardNameMatcher.create(name);
-    }
-
     public ProguardTypeMatcher getTypeMatcher() {
       return type;
     }
@@ -62,6 +63,10 @@
       this.type = type;
     }
 
+    public void setName(IdentifierPatternWithWildcards identifierPatternWithWildcards) {
+      this.name = ProguardNameMatcher.create(identifierPatternWithWildcards);
+    }
+
     public void setArguments(List<ProguardTypeMatcher> arguments) {
       this.arguments = arguments;
     }
@@ -76,8 +81,8 @@
 
     public ProguardMemberRule build() {
       assert isValid();
-      return new ProguardMemberRule(annotation, accessFlags, negatedAccessFlags, ruleType, name,
-          type, arguments, returnValue);
+      return new ProguardMemberRule(annotation, accessFlags, negatedAccessFlags, ruleType, type,
+          name, arguments, returnValue);
     }
   }
 
@@ -85,8 +90,8 @@
   private final ProguardAccessFlags accessFlags;
   private final ProguardAccessFlags negatedAccessFlags;
   private final ProguardMemberType ruleType;
-  private final ProguardNameMatcher name;
   private final ProguardTypeMatcher type;
+  private final ProguardNameMatcher name;
   private final List<ProguardTypeMatcher> arguments;
   private final ProguardMemberRuleReturnValue returnValue;
 
@@ -95,17 +100,17 @@
       ProguardAccessFlags accessFlags,
       ProguardAccessFlags negatedAccessFlags,
       ProguardMemberType ruleType,
-      ProguardNameMatcher name,
       ProguardTypeMatcher type,
+      ProguardNameMatcher name,
       List<ProguardTypeMatcher> arguments,
       ProguardMemberRuleReturnValue returnValue) {
     this.annotation = annotation;
     this.accessFlags = accessFlags;
     this.negatedAccessFlags = negatedAccessFlags;
     this.ruleType = ruleType;
-    this.name = name;
     this.type = type;
-    this.arguments = arguments != null ? ImmutableList.copyOf(arguments) : null;
+    this.name = name;
+    this.arguments = arguments != null ? Collections.unmodifiableList(arguments) : null;
     this.returnValue = returnValue;
   }
 
@@ -132,14 +137,14 @@
     return ruleType;
   }
 
-  public ProguardNameMatcher getName() {
-    return name;
-  }
-
   public ProguardTypeMatcher getType() {
     return type;
   }
 
+  public ProguardNameMatcher getName() {
+    return name;
+  }
+
   public List<ProguardTypeMatcher> getArguments() {
     return arguments;
   }
@@ -261,6 +266,20 @@
     return false;
   }
 
+  Iterable<String> getWildcards() {
+    return Iterables.concat(
+        annotation != null ? annotation.getWildcards() : ImmutableList.of(),
+        type != null ? type.getWildcards() : ImmutableList.of(),
+        name != null ? name.getWildcards() : ImmutableList.of(),
+        arguments != null
+            ? arguments.stream()
+                .map(ProguardTypeMatcher::getWildcards)
+                .flatMap(it -> StreamSupport.stream(it.spliterator(), false))
+                .collect(Collectors.toList())
+            : ImmutableList.of()
+    );
+  }
+
   @Override
   public boolean equals(Object o) {
     if (!(o instanceof ProguardMemberRule)) {
@@ -296,8 +315,8 @@
     result = 31 * result + accessFlags.hashCode();
     result = 31 * result + negatedAccessFlags.hashCode();
     result = 31 * result + (ruleType != null ? ruleType.hashCode() : 0);
-    result = 31 * result + (name != null ? name.hashCode() : 0);
     result = 31 * result + (type != null ? type.hashCode() : 0);
+    result = 31 * result + (name != null ? name.hashCode() : 0);
     result = 31 * result + (arguments != null ? arguments.hashCode() : 0);
     return result;
   }
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardNameMatcher.java b/src/main/java/com/android/tools/r8/shaking/ProguardNameMatcher.java
index 9286d0a..b8f583d 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardNameMatcher.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardNameMatcher.java
@@ -3,21 +3,25 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking;
 
+import com.android.tools.r8.shaking.ProguardConfigurationParser.IdentifierPatternWithWildcards;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+
 public abstract class ProguardNameMatcher {
 
   private static final ProguardNameMatcher MATCH_ALL_NAMES = new MatchAllNames();
 
   private ProguardNameMatcher() {
-
   }
 
-  public static ProguardNameMatcher create(String pattern) {
-    if (pattern.equals("*")) {
+  public static ProguardNameMatcher create(
+      IdentifierPatternWithWildcards identifierPatternWithWildcards) {
+    if (identifierPatternWithWildcards.isMatchAllNames()) {
       return MATCH_ALL_NAMES;
-    } else if (pattern.contains("*") || pattern.contains("?")) {
-      return new MatchNamePattern(pattern);
+    } else if (identifierPatternWithWildcards.wildcards.isEmpty()) {
+      return new MatchSpecificName(identifierPatternWithWildcards.pattern);
     } else {
-      return new MatchSpecificName(pattern);
+      return new MatchNamePattern(identifierPatternWithWildcards);
     }
   }
 
@@ -63,6 +67,10 @@
 
   public abstract boolean matches(String name);
 
+  protected Iterable<String> getWildcards() {
+    return ImmutableList.of();
+  }
+
   private static class MatchAllNames extends ProguardNameMatcher {
 
     @Override
@@ -71,6 +79,11 @@
     }
 
     @Override
+    protected Iterable<String> getWildcards() {
+      return ImmutableList.of("*");
+    }
+
+    @Override
     public String toString() {
       return "*";
     }
@@ -79,9 +92,11 @@
   private static class MatchNamePattern extends ProguardNameMatcher {
 
     private final String pattern;
+    private final List<String> wildcards;
 
-    MatchNamePattern(String pattern) {
-      this.pattern = pattern;
+    MatchNamePattern(IdentifierPatternWithWildcards identifierPatternWithWildcards) {
+      this.pattern = identifierPatternWithWildcards.pattern;
+      this.wildcards = identifierPatternWithWildcards.wildcards;
     }
 
     @Override
@@ -90,6 +105,11 @@
     }
 
     @Override
+    protected Iterable<String> getWildcards() {
+      return wildcards;
+    }
+
+    @Override
     public String toString() {
       return pattern;
     }
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java b/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java
index 809278f..51d11bb 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java
@@ -3,9 +3,13 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking;
 
+import static com.android.tools.r8.utils.DescriptorUtils.javaTypeToDescriptor;
+
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.shaking.ProguardConfigurationParser.IdentifierPatternWithWildcards;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
 
 public abstract class ProguardTypeMatcher {
 
@@ -25,6 +29,10 @@
 
   public abstract boolean matches(DexType type);
 
+  protected Iterable<String> getWildcards() {
+    return ImmutableList.of();
+  }
+
   @Override
   public abstract String toString();
 
@@ -32,12 +40,14 @@
     return false;
   }
 
-  public static ProguardTypeMatcher create(String pattern, ClassOrType kind,
+  public static ProguardTypeMatcher create(
+      IdentifierPatternWithWildcards identifierPatternWithWildcards,
+      ClassOrType kind,
       DexItemFactory dexItemFactory) {
-    if (pattern == null) {
+    if (identifierPatternWithWildcards == null || identifierPatternWithWildcards.pattern == null) {
       return null;
     }
-    switch (pattern) {
+    switch (identifierPatternWithWildcards.pattern) {
       case MATCH_ALL_PATTERN:
         return MatchAllTypes.MATCH_ALL_TYPES;
       case MATCH_ANY_ARG_SEQUENCE_PATTERN:
@@ -49,11 +59,11 @@
       case MATCH_BASIC_PATTERN:
         return MatchBasicTypes.MATCH_BASIC_TYPES;
       default:
-        if (!pattern.contains("*") && !pattern.contains("%") && !pattern.contains("?")) {
-          return new MatchSpecificType(
-              dexItemFactory.createType(DescriptorUtils.javaTypeToDescriptor(pattern)));
+        if (identifierPatternWithWildcards.wildcards.isEmpty()) {
+          return new MatchSpecificType(dexItemFactory.createType(
+              javaTypeToDescriptor(identifierPatternWithWildcards.pattern)));
         }
-        return new MatchTypePattern(pattern, kind);
+        return new MatchTypePattern(identifierPatternWithWildcards, kind);
     }
   }
 
@@ -85,6 +95,11 @@
     }
 
     @Override
+    protected Iterable<String> getWildcards() {
+      return ImmutableList.of(MATCH_ALL_PATTERN);
+    }
+
+    @Override
     public String toString() {
       return MATCH_ALL_PATTERN;
     }
@@ -150,6 +165,11 @@
     }
 
     @Override
+    protected Iterable<String> getWildcards() {
+      return ImmutableList.of(pattern);
+    }
+
+    @Override
     public String toString() {
       return pattern;
     }
@@ -175,6 +195,11 @@
     }
 
     @Override
+    protected Iterable<String> getWildcards() {
+      return ImmutableList.of(MATCH_BASIC_PATTERN);
+    }
+
+    @Override
     public String toString() {
       return MATCH_BASIC_PATTERN;
     }
@@ -230,10 +255,13 @@
   private static class MatchTypePattern extends ProguardTypeMatcher {
 
     private final String pattern;
+    private final List<String> wildcards;
     private final ClassOrType kind;
 
-    private MatchTypePattern(String pattern, ClassOrType kind) {
-      this.pattern = pattern;
+    private MatchTypePattern(
+        IdentifierPatternWithWildcards identifierPatternWithWildcards, ClassOrType kind) {
+      this.pattern = identifierPatternWithWildcards.pattern;
+      this.wildcards = identifierPatternWithWildcards.wildcards;
       this.kind = kind;
     }
 
@@ -244,6 +272,11 @@
       return matchClassOrTypeNameImpl(pattern, 0, typeName, 0, kind);
     }
 
+    @Override
+    protected Iterable<String> getWildcards() {
+      return wildcards;
+    }
+
     private static boolean matchClassOrTypeNameImpl(
         String pattern, int patternIndex, String className, int nameIndex, ClassOrType kind) {
       for (int i = patternIndex; i < pattern.length(); i++) {
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardWhyAreYouKeepingRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardWhyAreYouKeepingRule.java
index ef83926..73b5538 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardWhyAreYouKeepingRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardWhyAreYouKeepingRule.java
@@ -3,7 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking;
 
-import java.util.Set;
+import java.util.List;
 
 public class ProguardWhyAreYouKeepingRule extends ProguardConfigurationRule {
 
@@ -29,7 +29,7 @@
       ProguardTypeMatcher inheritanceAnnotation,
       ProguardTypeMatcher inheritanceClassName,
       boolean inheritanceIsExtends,
-      Set<ProguardMemberRule> memberRules) {
+      List<ProguardMemberRule> memberRules) {
     super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
         classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
   }
diff --git a/src/test/examplesAndroidO/lambdadesugaringnplus/LambdasWithStaticAndDefaultMethods.java b/src/test/examplesAndroidO/lambdadesugaringnplus/LambdasWithStaticAndDefaultMethods.java
index 587d947..46cd555 100644
--- a/src/test/examplesAndroidO/lambdadesugaringnplus/LambdasWithStaticAndDefaultMethods.java
+++ b/src/test/examplesAndroidO/lambdadesugaringnplus/LambdasWithStaticAndDefaultMethods.java
@@ -143,6 +143,92 @@
     }
   }
 
+  static class B78901754 {
+    public static class A {
+      public final String msg;
+
+      public A(String msg) {
+        this.msg = msg;
+      }
+    }
+
+    public static class B extends A {
+      public B(String msg) {
+        super(msg);
+      }
+    }
+
+    public interface IAA {
+      A[] foo(A[] p);
+    }
+
+    public interface IAB {
+      A[] foo(B[] p);
+    }
+
+    public interface IBA {
+      B[] foo(A[] p);
+    }
+
+    public interface IBB {
+      B[] foo(B[] p);
+    }
+
+    public static A[] fooAA(A[] p) {
+      return new A[]{new A("fooAA")};
+    }
+
+    public static A[] fooBA(B[] p) {
+      return new A[]{new A("fooBA")};
+    }
+
+    public static B[] fooAB(A[] p) {
+      return new B[]{new B("fooAB")};
+    }
+
+    public static B[] fooBB(B[] p) {
+      return new B[]{new B("fooBB")};
+    }
+
+    public static void testAA(IAA i) {
+      System.out.println(i.foo(null)[0].msg);
+    }
+
+    public static void testAB(IAB i) {
+      System.out.println(i.foo(null)[0].msg);
+    }
+
+    public static void testBA(IBA i) {
+      System.out.println(i.foo(null)[0].msg);
+    }
+
+    public static void testBB(IBB i) {
+      System.out.println(i.foo(null)[0].msg);
+    }
+
+    public static void test() {
+      testAA(B78901754::fooAA);
+      testAA(B78901754::fooAB);
+      // testAA(B78901754::fooBA); javac error: incompatible types: A[] cannot be converted to B[]
+      // testAA(B78901754::fooBB); javac error: incompatible types: A[] cannot be converted to B[]
+
+      testAB(B78901754::fooAA);
+      testAB(B78901754::fooAB);
+      testAB(B78901754::fooBA);
+      testAB(B78901754::fooBB);
+
+      // testBA(B78901754::fooAA); javac error: A[] cannot be converted to B[]
+      testBA(B78901754::fooAB);
+      // testBA(B78901754::fooBA); javac error: incompatible types: A[] cannot be converted to B[]
+      // testBA(B78901754::fooBB); javac error: incompatible types: A[] cannot be converted to B[]
+
+      // testBB(B78901754::fooAA); javac error: A[] cannot be converted to B[]
+      testBB(B78901754::fooAB);
+      // testBB(B78901754::fooBA); javac error: A[] cannot be converted to B[]
+      testBB(B78901754::fooBB);
+    }
+  }
+
   interface B38257037_I1 {
     default Number getNumber() {
       return new Integer(1);
@@ -420,5 +506,6 @@
     B38308515.test();
     B38302860.test();
     B62168701.test();
+    B78901754.test();
   }
 }
diff --git a/src/test/java/com/android/tools/r8/CfFrontendExamplesTest.java b/src/test/java/com/android/tools/r8/CfFrontendExamplesTest.java
index 2ec147b..18c3436 100644
--- a/src/test/java/com/android/tools/r8/CfFrontendExamplesTest.java
+++ b/src/test/java/com/android/tools/r8/CfFrontendExamplesTest.java
@@ -53,6 +53,8 @@
     makeTest("returns.Returns"),
     makeTest("staticfield.StaticField"),
     makeTest("stringbuilding.StringBuilding"),
+    makeTest("switches.Switches"),
+    makeTest("sync.Sync"),
     makeTest("throwing.Throwing"),
     makeTest("trivial.Trivial"),
     makeTest("trycatch.TryCatch"),
@@ -65,25 +67,20 @@
     makeTest("regress_37658666.Regress", CfFrontendExamplesTest::compareRegress37658666),
     makeTest("regress_37875803.Regress"),
     makeTest("regress_37955340.Regress"),
-    // TODO(mathiasr): This test fails because we remove ASM's java.lang.Synthetic annotations.
-    // makeTest("regress_62300145.Regress"),
+    makeTest("regress_62300145.Regress"),
     makeTest("regress_64881691.Regress"),
     makeTest("regress_65104300.Regress"),
     makeTest("regress_70703087.Test"),
     makeTest("regress_70736958.Test"),
     makeTest("regress_70737019.Test"),
     makeTest("regress_72361252.Test"),
+    makeTest("memberrebinding2.Memberrebinding"),
     makeTest("memberrebinding3.Memberrebinding"),
     makeTest("minification.Minification"),
     makeTest("enclosingmethod.Main"),
     makeTest("enclosingmethod_proguarded.Main"),
-    makeTest("interfaceinlining.Main")
-    // TODO(mathiasr): These fail because we add a zero initializer to an field with no initializer.
-    // makeTest("sync.Sync"),
-    // makeTest("memberrebinding2.Memberrebinding"),
-    // TODO(mathiasr): Support emitting table switches in CfSwitch.
-    // makeTest("switches.Switches"),
-    // makeTest("switchmaps.Switches"),
+    makeTest("interfaceinlining.Main"),
+    makeTest("switchmaps.Switches")
   );
 
   private static Object[] makeTest(String className) {
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index f276c46..c7fcdcc 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -1209,6 +1209,11 @@
     return result.stdout;
   }
 
+  public static ProcessResult runProguardRaw(Path inJar, Path outJar, List<Path> config, Path map)
+      throws IOException {
+    return runProguardRaw(getProguardScript(), inJar, outJar, config, map);
+  }
+
   public static ProcessResult runProguardRaw(Path inJar, Path outJar, Path config, Path map)
       throws IOException {
     return runProguardRaw(getProguardScript(), inJar, outJar, ImmutableList.of(config), map);
@@ -1230,6 +1235,11 @@
   }
 
   public static ProcessResult runProguard6Raw(
+      Path inJar, Path outJar, List<Path> config, Path map) throws IOException {
+    return runProguardRaw(getProguard6Script(), inJar, outJar, config, map);
+  }
+
+  public static ProcessResult runProguard6Raw(
       Path inJar, Path outJar, Path lib, Path config, Path map) throws IOException {
     return runProguardRaw(getProguard6Script(), inJar, outJar, lib, ImmutableList.of(config), map);
   }
diff --git a/src/test/java/com/android/tools/r8/debug/ContinuousSteppingTest.java b/src/test/java/com/android/tools/r8/debug/ContinuousSteppingTest.java
index d371243..45e3c96 100644
--- a/src/test/java/com/android/tools/r8/debug/ContinuousSteppingTest.java
+++ b/src/test/java/com/android/tools/r8/debug/ContinuousSteppingTest.java
@@ -4,36 +4,195 @@
 
 package com.android.tools.r8.debug;
 
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.Pair;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableList.Builder;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
+import java.util.function.Predicate;
+import java.util.jar.JarEntry;
+import java.util.jar.JarInputStream;
+import java.util.stream.Collectors;
 import org.apache.harmony.jpda.tests.framework.jdwp.Value;
 import org.junit.Assert;
 import org.junit.BeforeClass;
 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 ContinuousSteppingTest extends DebugTestBase {
 
-  private static DebugTestConfig javaD8Config;
-  private static DebugTestConfig kotlinD8Config;
+  private static final String MAIN_METHOD_NAME = "main";
+
+  // A list of self-contained jars to process (which do not depend on other jar files).
+  private static final List<Pair<Path, Predicate<Version>>> LIST_OF_JARS = new ConfigListBuilder()
+      .add(DebugTestBase.DEBUGGEE_JAR, ContinuousSteppingTest::allVersions)
+      .add(DebugTestBase.DEBUGGEE_JAVA8_JAR, ContinuousSteppingTest::allVersions)
+      .add(KotlinD8Config.DEBUGGEE_KOTLIN_JAR, ContinuousSteppingTest::allVersions)
+      .addAll(findAllJarsIn(Paths.get(ToolHelper.EXAMPLES_ANDROID_N_BUILD_DIR)),
+          ContinuousSteppingTest::fromAndroidN)
+      .addAll(findAllJarsIn(Paths.get(ToolHelper.EXAMPLES_ANDROID_O_BUILD_DIR)),
+          ContinuousSteppingTest::fromAndroidO)
+      .build();
+
+  private static final Map<Path, DebugTestConfig> compiledJarConfig = new HashMap<>();
+
+  private final String mainClass;
+  private final Path jarPath;
+
+  private static class ConfigListBuilder {
+
+    private final Builder<Pair<Path, Predicate<Version>>> builder = ImmutableList.builder();
+
+    public ConfigListBuilder add(Path path, Predicate<Version> predicate) {
+      builder.add(new Pair<>(path, predicate));
+      return this;
+    }
+
+    public ConfigListBuilder addAll(List<Path> paths, Predicate<Version> predicate) {
+      for (Path path : paths) {
+        add(path, predicate);
+      }
+      return this;
+    }
+
+    public List<Pair<Path, Predicate<Version>>> build() {
+      return builder.build();
+    }
+  }
+
+  public static boolean allVersions(Version dexVmVersion) {
+    return true;
+  }
+
+  public static boolean fromAndroidN(Version dexVmVersion) {
+    return dexVmVersion.isAtLeast(Version.V7_0_0);
+  }
+
+  public static boolean fromAndroidO(Version dexVmVersion) {
+    return dexVmVersion.isAtLeast(Version.DEFAULT);
+  }
+
+  private static List<Path> findAllJarsIn(Path root) {
+    try {
+      return Files.walk(root)
+          .filter(p -> p.toFile().getPath().endsWith(FileUtils.JAR_EXTENSION))
+          .collect(Collectors.toList());
+    } catch (IOException e) {
+      return Collections.emptyList();
+    }
+  }
 
   @BeforeClass
   public static void setup() {
-    javaD8Config = new D8DebugTestResourcesConfig(temp);
-    kotlinD8Config = new KotlinD8Config(temp);
+    LIST_OF_JARS.forEach(pair -> {
+      if (pair.getSecond().test(ToolHelper.getDexVm().getVersion())) {
+        Path jarPath = pair.getFirst();
+        DebugTestConfig config = new D8DebugTestConfig().compileAndAdd(temp, jarPath);
+        compiledJarConfig.put(jarPath, config);
+      }
+    });
+  }
+
+  @Parameters(name = "{0} from {1}")
+  public static Collection<Object[]> getData() throws IOException {
+    List<Object[]> testCases = new ArrayList<>();
+    for (Pair<Path, Predicate<Version>> pair : LIST_OF_JARS) {
+      if (pair.getSecond().test(ToolHelper.getDexVm().getVersion())) {
+        Path jarPath = pair.getFirst();
+        List<String> mainClasses = getAllMainClassesFromJar(jarPath);
+        for (String className : mainClasses) {
+          testCases.add(new Object[]{className, jarPath});
+        }
+      }
+    }
+    return testCases;
+  }
+
+  public ContinuousSteppingTest(String mainClass, Path jarPath) {
+    this.mainClass = mainClass;
+    this.jarPath = jarPath;
   }
 
   @Test
-  public void testArithmetic() throws Throwable {
-    runContinuousTest("Arithmetic", javaD8Config);
+  public void testContinuousSingleStep() throws Throwable {
+    assert compiledJarConfig.containsKey(jarPath);
+    DebugTestConfig config = compiledJarConfig.get(jarPath);
+    assert config != null;
+    runContinuousTest(mainClass, config);
   }
 
-  @Test
-  public void testLocals() throws Throwable {
-    runContinuousTest("Locals", javaD8Config);
+  // Returns a list of classes with a "public static void main(String[])" method in the given jar
+  // file.
+  private static List<String> getAllMainClassesFromJar(Path pathToJar) throws IOException {
+    JarInputStream jarInputStream = new JarInputStream(Files.newInputStream(pathToJar,
+        StandardOpenOption.READ));
+    final URL url = pathToJar.toUri().toURL();
+    assert pathToJar.toFile().exists();
+    assert pathToJar.toFile().isFile();
+    List<String> mainClasses = new ArrayList<>();
+    ClassLoader loader = new URLClassLoader(new URL[]{url},
+        Thread.currentThread().getContextClassLoader());
+
+    try {
+      JarEntry entry;
+      while ((entry = jarInputStream.getNextJarEntry()) != null) {
+        String entryName = entry.getName();
+        if (entryName.endsWith(FileUtils.CLASS_EXTENSION)) {
+          String className =
+              entryName.substring(0, entryName.length() - FileUtils.CLASS_EXTENSION.length());
+          className = className.replace(DescriptorUtils.DESCRIPTOR_PACKAGE_SEPARATOR,
+              DescriptorUtils.JAVA_PACKAGE_SEPARATOR);
+          try {
+            Class<?> cls = loader.loadClass(className);
+            if (cls != null) {
+              long mainMethodsCount = Arrays.stream(cls.getMethods())
+                  .filter(ContinuousSteppingTest::isMainMethod)
+                  .count();
+              if (mainMethodsCount == 1) {
+                // Add class to the list
+                mainClasses.add(className);
+              }
+            }
+          } catch (Throwable e) {
+            System.out.println(
+                "Could not load class " + className + " from " + pathToJar.toFile().getPath());
+            return Collections.emptyList();
+          }
+        }
+      }
+    } finally {
+      jarInputStream.close();
+    }
+    return mainClasses;
   }
 
-  @Test
-  public void testKotlinInline() throws Throwable {
-    runContinuousTest("KotlinInline", kotlinD8Config);
+  private static boolean isMainMethod(Method m) {
+    return Modifier.isStatic(m.getModifiers())
+        && m.getReturnType() == void.class
+        && m.getName().equals(MAIN_METHOD_NAME)
+        && m.getParameterCount() == 1
+        && m.getParameterTypes()[0] == String[].class;
   }
 
   private void runContinuousTest(String debuggeeClassName, DebugTestConfig config)
@@ -41,7 +200,7 @@
     runDebugTest(
         config,
         debuggeeClassName,
-        breakpoint(debuggeeClassName, "main"),
+        breakpoint(debuggeeClassName, MAIN_METHOD_NAME),
         run(),
         stepUntil(StepKind.OVER, StepLevel.INSTRUCTION, debuggeeState -> {
           // Fetch local variables.
diff --git a/src/test/java/com/android/tools/r8/debug/KotlinD8Config.java b/src/test/java/com/android/tools/r8/debug/KotlinD8Config.java
index fa5f5ca..d828a5e 100644
--- a/src/test/java/com/android/tools/r8/debug/KotlinD8Config.java
+++ b/src/test/java/com/android/tools/r8/debug/KotlinD8Config.java
@@ -17,7 +17,7 @@
  */
 class KotlinD8Config extends D8DebugTestConfig {
 
-  private static final Path DEBUGGEE_KOTLIN_JAR =
+  public static final Path DEBUGGEE_KOTLIN_JAR =
       Paths.get(ToolHelper.BUILD_DIR, "test", "debug_test_resources_kotlin.jar");
 
   private static AndroidApp compiledResources = null;
diff --git a/src/test/java/com/android/tools/r8/naming/IdentifierMinifierTest.java b/src/test/java/com/android/tools/r8/naming/IdentifierMinifierTest.java
index fa39257..f27c88a 100644
--- a/src/test/java/com/android/tools/r8/naming/IdentifierMinifierTest.java
+++ b/src/test/java/com/android/tools/r8/naming/IdentifierMinifierTest.java
@@ -303,9 +303,10 @@
   private static int countRenamedClassIdentifier(
       DexInspector inspector, DexEncodedField[] fields) {
     return Arrays.stream(fields)
-        .filter(encodedField -> encodedField.staticValue instanceof DexValueString)
+        .filter(encodedField -> encodedField.getStaticValue() instanceof DexValueString)
         .reduce(0, (cnt, encodedField) -> {
-          String cnstString = ((DexValueString) encodedField.staticValue).getValue().toString();
+          String cnstString =
+              ((DexValueString) encodedField.getStaticValue()).getValue().toString();
           if (isValidJavaType(cnstString)) {
             ClassSubject classSubject = inspector.clazz(cnstString);
             if (classSubject.isRenamed()
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 51772fe..087b5f8 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
@@ -100,47 +100,47 @@
         inspector.clazz("Test").clinit().getMethod().getCode().asDexCode().isEmptyVoidMethod());
 
     DexValue value;
-    assertTrue(inspector.clazz("Test").field("boolean", "booleanField").hasStaticValue());
+    assertTrue(inspector.clazz("Test").field("boolean", "booleanField").hasExplicitStaticValue());
     value = inspector.clazz("Test").field("boolean", "booleanField").getStaticValue();
     assertTrue(value instanceof DexValueBoolean);
     assertEquals(true, ((DexValueBoolean) value).getValue());
 
-    assertTrue(inspector.clazz("Test").field("byte", "byteField").hasStaticValue());
+    assertTrue(inspector.clazz("Test").field("byte", "byteField").hasExplicitStaticValue());
     value = inspector.clazz("Test").field("byte", "byteField").getStaticValue();
     assertTrue(value instanceof DexValueByte);
     assertEquals(1, ((DexValueByte) value).getValue());
 
-    assertTrue(inspector.clazz("Test").field("short", "shortField").hasStaticValue());
+    assertTrue(inspector.clazz("Test").field("short", "shortField").hasExplicitStaticValue());
     value = inspector.clazz("Test").field("short", "shortField").getStaticValue();
     assertTrue(value instanceof DexValueShort);
     assertEquals(2, ((DexValueShort) value).getValue());
 
-    assertTrue(inspector.clazz("Test").field("int", "intField").hasStaticValue());
+    assertTrue(inspector.clazz("Test").field("int", "intField").hasExplicitStaticValue());
     value = inspector.clazz("Test").field("int", "intField").getStaticValue();
     assertTrue(value instanceof DexValueInt);
     assertEquals(3, ((DexValueInt) value).getValue());
 
-    assertTrue(inspector.clazz("Test").field("long", "longField").hasStaticValue());
+    assertTrue(inspector.clazz("Test").field("long", "longField").hasExplicitStaticValue());
     value = inspector.clazz("Test").field("long", "longField").getStaticValue();
     assertTrue(value instanceof DexValueLong);
     assertEquals(4, ((DexValueLong) value).getValue());
 
-    assertTrue(inspector.clazz("Test").field("float", "floatField").hasStaticValue());
+    assertTrue(inspector.clazz("Test").field("float", "floatField").hasExplicitStaticValue());
     value = inspector.clazz("Test").field("float", "floatField").getStaticValue();
     assertTrue(value instanceof DexValueFloat);
     assertEquals(5.0f, ((DexValueFloat) value).getValue(), 0.0);
 
-    assertTrue(inspector.clazz("Test").field("double", "doubleField").hasStaticValue());
+    assertTrue(inspector.clazz("Test").field("double", "doubleField").hasExplicitStaticValue());
     value = inspector.clazz("Test").field("double", "doubleField").getStaticValue();
     assertTrue(value instanceof DexValueDouble);
     assertEquals(6.0f, ((DexValueDouble) value).getValue(), 0.0);
 
-    assertTrue(inspector.clazz("Test").field("char", "charField").hasStaticValue());
+    assertTrue(inspector.clazz("Test").field("char", "charField").hasExplicitStaticValue());
     value = inspector.clazz("Test").field("char", "charField").getStaticValue();
     assertTrue(value instanceof DexValueChar);
     assertEquals(0x30 + 7, ((DexValueChar) value).getValue());
 
-    assertTrue(inspector.clazz("Test").field("java.lang.String", "stringField").hasStaticValue());
+    assertTrue(inspector.clazz("Test").field("java.lang.String", "stringField").hasExplicitStaticValue());
     value = inspector.clazz("Test").field("java.lang.String", "stringField").getStaticValue();
     assertTrue(value instanceof DexValueString);
     assertEquals(("8"), ((DexValueString) value).getValue().toString());
@@ -317,12 +317,12 @@
         inspector.clazz("Test").clinit().getMethod().getCode().asDexCode().isEmptyVoidMethod());
 
     DexValue value;
-    assertTrue(inspector.clazz("Test").field("int", "intField").hasStaticValue());
+    assertTrue(inspector.clazz("Test").field("int", "intField").hasExplicitStaticValue());
     value = inspector.clazz("Test").field("int", "intField").getStaticValue();
     assertTrue(value instanceof DexValueInt);
     assertEquals(3, ((DexValueInt) value).getValue());
 
-    assertTrue(inspector.clazz("Test").field("java.lang.String", "stringField").hasStaticValue());
+    assertTrue(inspector.clazz("Test").field("java.lang.String", "stringField").hasExplicitStaticValue());
     value = inspector.clazz("Test").field("java.lang.String", "stringField").getStaticValue();
     assertTrue(value instanceof DexValueString);
     assertEquals(("7"), ((DexValueString) value).getValue().toString());
@@ -388,12 +388,12 @@
     assertTrue(inspector.clazz("Test").clinit().isPresent());
 
     DexValue value;
-    assertTrue(inspector.clazz("Test").field("int", "intField").hasStaticValue());
+    assertTrue(inspector.clazz("Test").field("int", "intField").hasExplicitStaticValue());
     value = inspector.clazz("Test").field("int", "intField").getStaticValue();
     assertTrue(value instanceof DexValueInt);
     assertEquals(3, ((DexValueInt) value).getValue());
 
-    assertTrue(inspector.clazz("Test").field("java.lang.String", "stringField").hasStaticValue());
+    assertTrue(inspector.clazz("Test").field("java.lang.String", "stringField").hasExplicitStaticValue());
     value = inspector.clazz("Test").field("java.lang.String", "stringField").getStaticValue();
     assertTrue(value instanceof DexValueString);
     assertEquals(("7"), ((DexValueString) value).getValue().toString());
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 89a7291..2dfe42e 100644
--- a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
@@ -30,6 +30,7 @@
 import com.android.tools.r8.origin.PathOrigin;
 import com.android.tools.r8.position.TextPosition;
 import com.android.tools.r8.position.TextRange;
+import com.android.tools.r8.shaking.ProguardConfigurationParser.IdentifierPatternWithWildcards;
 import com.android.tools.r8.utils.AbortException;
 import com.android.tools.r8.utils.DefaultDiagnosticsHandler;
 import com.android.tools.r8.utils.DexInspector;
@@ -46,7 +47,6 @@
 import java.util.List;
 import java.util.function.Function;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 
 class EmptyMainClassForProguardTests {
@@ -205,7 +205,8 @@
     assertEquals("some.library.Class", rule.getInheritanceClassName().toString());
     ProguardMemberRule memberRule = rule.getMemberRules().iterator().next();
     assertTrue(memberRule.getAccessFlags().isProtected());
-    assertEquals(ProguardNameMatcher.create("getContents"), memberRule.getName());
+    assertEquals(ProguardNameMatcher.create(
+        IdentifierPatternWithWildcards.withoutWildcards("getContents")), memberRule.getName());
     assertEquals("java.lang.Object[][]", memberRule.getType().toString());
     assertEquals(ProguardMemberType.METHOD, memberRule.getRuleType());
     assertEquals(0, memberRule.getArguments().size());
@@ -1410,10 +1411,12 @@
     assertEquals(ProguardKeepRuleType.KEEP, if0.subsequentRule.getType());
     assertEquals("**$D<2>", if0.subsequentRule.getClassNames().toString());
     // TODO(b/73800755): Test <2> matches with expected wildcard: ** after '$R'.
+
+    verifyWithProguard6(proguardConfig);
   }
 
   @Test
-  public void parse_if_nthWildcard_notNumber() throws Exception {
+  public void parse_if_nthWildcard_notNumber_literalN() throws Exception {
     Path proguardConfig = writeTextToTempFile(
         "-if class **$R**",
         "-keep class **D<n>"
@@ -1424,7 +1427,6 @@
       parser.parse(proguardConfig);
       fail();
     } catch (AbortException e) {
-      System.out.println(handler.errors.get(0));
       checkDiagnostic(handler.errors, proguardConfig, 2, 13,
           "Use of generics not allowed for java type");
     }
@@ -1432,6 +1434,76 @@
   }
 
   @Test
+  public void parse_if_nthWildcard_notNumber_asterisk_inClassName() throws Exception {
+    Path proguardConfig = writeTextToTempFile(
+        "-if class **$R**",
+        "-keep class **D<*>"
+    );
+    try {
+      ProguardConfigurationParser parser =
+          new ProguardConfigurationParser(new DexItemFactory(), reporter);
+      parser.parse(proguardConfig);
+      fail();
+    } catch (AbortException e) {
+      checkDiagnostic(handler.errors, proguardConfig, 2, 13,
+          "Use of generics not allowed for java type");
+    }
+    verifyFailWithProguard6(proguardConfig, "Use of generics not allowed for java type");
+  }
+
+  @Test
+  public void parse_if_nthWildcard_notNumber_asterisk_inMemberName() throws Exception {
+    Path proguardConfig = writeTextToTempFile(
+        "-if class **$R**",
+        "-keep class **D<2> {",
+        "  int id<*>;",
+        "}"
+    );
+    ProguardConfigurationParser parser =
+        new ProguardConfigurationParser(new DexItemFactory(), reporter);
+    parser.parse(proguardConfig);
+    verifyParserEndsCleanly();
+
+    verifyWithProguard6(proguardConfig);
+  }
+
+  @Test
+  public void parse_if_nestedAngularBrackets_inMemberName() throws Exception {
+    Path proguardConfig = writeTextToTempFile(
+        "-if class **$R**",
+        "-keep class **D<2> {",
+        "  int id<<*>>;",
+        "}"
+    );
+    ProguardConfigurationParser parser =
+        new ProguardConfigurationParser(new DexItemFactory(), reporter);
+    parser.parse(proguardConfig);
+    verifyParserEndsCleanly();
+
+    verifyWithProguard6(proguardConfig);
+  }
+
+  @Test
+  public void parse_if_nestedAngularBrackets_outOfRange() throws Exception {
+    Path proguardConfig = writeTextToTempFile(
+        "-if class **$R**",
+        "-keep class **D<2> {",
+        "  int id<<4>>;",  // There are 3 previous referable wildcards in this rule.
+        "}"
+    );
+    try {
+      ProguardConfigurationParser parser =
+          new ProguardConfigurationParser(new DexItemFactory(), reporter);
+      parser.parse(proguardConfig);
+      fail();
+    } catch (AbortException e) {
+      checkDiagnostic(handler.errors, proguardConfig, 4, 2,
+          "Wildcard", "<4>", "invalid");
+    }
+    verifyFailWithProguard6(proguardConfig, "Invalid reference to wildcard (4,");
+  }
+
+  @Test
   public void parse_if_nthWildcard_outOfRange_tooSmall() throws Exception {
     Path proguardConfig = writeTextToTempFile(
         "-if class **$R**",
@@ -1446,9 +1518,9 @@
       checkDiagnostic(handler.errors, proguardConfig, 2, 13,
           "Wildcard", "<0>", "invalid");
     }
+    verifyFailWithProguard6(proguardConfig, "Invalid reference to wildcard (0,");
   }
 
-  @Ignore("b/73800755: verify the range of <n>")
   @Test
   public void parse_if_nthWildcard_outOfRange_tooBig() throws Exception {
     Path proguardConfig = writeTextToTempFile(
@@ -1461,12 +1533,12 @@
       parser.parse(proguardConfig);
       fail();
     } catch (AbortException e) {
-      checkDiagnostic(handler.errors, proguardConfig, 1, 1,
+      checkDiagnostic(handler.errors, proguardConfig, 3, 1,
           "Wildcard", "<4>", "invalid");
     }
+    verifyFailWithProguard6(proguardConfig, "Invalid reference to wildcard (4,");
   }
 
-  @Ignore("b/73800755: verify the range of <n>")
   @Test
   public void parse_if_nthWildcard_outOfRange_inIf() throws Exception {
     Path proguardConfig = writeTextToTempFile(
@@ -1479,12 +1551,12 @@
       parser.parse(proguardConfig);
       fail();
     } catch (AbortException e) {
-      checkDiagnostic(handler.errors, proguardConfig, 1, 1,
+      checkDiagnostic(handler.errors, proguardConfig, 3, 1,
           "Wildcard", "<2>", "invalid");
     }
+    verifyFailWithProguard6(proguardConfig, "Invalid reference to wildcard (2,");
   }
 
-  @Ignore("b/73800755: verify the range of <n>")
   @Test
   public void parse_if_nthWildcard_not_referable() throws Exception {
     Path proguardConfig = writeTextToTempFile(
@@ -1501,9 +1573,10 @@
       parser.parse(proguardConfig);
       fail();
     } catch (AbortException e) {
-      checkDiagnostic(handler.errors, proguardConfig, 1, 1,
+      checkDiagnostic(handler.errors, proguardConfig, 6, 2,
           "Wildcard", "<3>", "invalid");
     }
+    verifyFailWithProguard6(proguardConfig, "Invalid reference to wildcard (3,");
   }
 
   @Test
@@ -1519,8 +1592,9 @@
       fail();
     } catch (AbortException e) {
       checkDiagnostic(handler.errors, proguardConfig, 1, 1,
-          "without", "subsequent", "keep");
+          "Expecting", "'-keep'", "after", "'-if'");
     }
+    verifyFailWithProguard6(proguardConfig, "Expecting '-keep' option after '-if' option");
   }
 
   @Test
@@ -1535,8 +1609,9 @@
       fail();
     } catch (AbortException e) {
       checkDiagnostic(handler.errors, proguardConfig, 1, 1,
-          "without", "subsequent", "keep");
+          "Expecting", "'-keep'", "after", "'-if'");
     }
+    verifyFailWithProguard6(proguardConfig, "Expecting '-keep' option after '-if' option");
   }
 
   @Test
@@ -1690,9 +1765,35 @@
       );
       Path proguardedJar =
           File.createTempFile("proguarded", FileUtils.JAR_EXTENSION, temp.getRoot()).toPath();
-      ToolHelper
-          .runProguard(jarTestClasses(ImmutableList.of(classToKeepForTest)),
-              proguardedJar, ImmutableList.of(proguardConfig, additionalProguardConfig), null);
+      ProcessResult result = ToolHelper.runProguardRaw(
+          jarTestClasses(ImmutableList.of(classToKeepForTest)),
+          proguardedJar,
+          ImmutableList.of(proguardConfig, additionalProguardConfig),
+          null);
+      assertEquals(0, result.exitCode);
+      DexInspector proguardInspector = new DexInspector(readJar(proguardedJar));
+      assertEquals(1, proguardInspector.allClasses().size());
+    }
+  }
+
+  private void verifyWithProguard6(Path proguardConfig) throws Exception {
+    if (isRunProguard()) {
+      // Add a keep rule for the test class as Proguard will fail if the resulting output jar is
+      // empty
+      Class classToKeepForTest = EmptyMainClassForProguardTests.class;
+      Path additionalProguardConfig = writeTextToTempFile(
+          "-keep class " + classToKeepForTest.getCanonicalName() + " {",
+          "  public static void main(java.lang.String[]);",
+          "}"
+      );
+      Path proguardedJar =
+          File.createTempFile("proguarded", FileUtils.JAR_EXTENSION, temp.getRoot()).toPath();
+      ProcessResult result = ToolHelper.runProguard6Raw(
+          jarTestClasses(ImmutableList.of(classToKeepForTest)),
+          proguardedJar,
+          ImmutableList.of(proguardConfig, additionalProguardConfig),
+          null);
+      assertEquals(0, result.exitCode);
       DexInspector proguardInspector = new DexInspector(readJar(proguardedJar));
       assertEquals(1, proguardInspector.allClasses().size());
     }
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 d8c43c9..4a734a4 100644
--- a/src/test/java/com/android/tools/r8/shaking/ProguardNameMatchingTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardNameMatchingTest.java
@@ -7,6 +7,7 @@
 import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.shaking.ProguardConfigurationParser.IdentifierPatternWithWildcards;
 import com.android.tools.r8.shaking.ProguardTypeMatcher.ClassOrType;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.google.common.collect.ImmutableList;
@@ -22,7 +23,8 @@
   private static final DexItemFactory dexItemFactory = new DexItemFactory();
 
   private static boolean matchTypeName(String typeName, String pattern) {
-    return ProguardTypeMatcher.create(pattern, ClassOrType.TYPE, dexItemFactory)
+    return ProguardTypeMatcher.create(
+        toIdentifierPatternWithWildCards(pattern), ClassOrType.TYPE, dexItemFactory)
         .matches(dexItemFactory.createType(DescriptorUtils.javaTypeToDescriptor(typeName)));
   }
 
@@ -37,8 +39,8 @@
       for (String pattern : patterns) {
         boolean isNegated = pattern.startsWith("!");
         String actualPattern = isNegated ? pattern.substring(1) : pattern;
-        listBuilder.addClassName(isNegated,
-            ProguardTypeMatcher.create(actualPattern, ClassOrType.CLASS, dexItemFactory));
+        listBuilder.addClassName(isNegated, ProguardTypeMatcher.create(
+            toIdentifierPatternWithWildCards(actualPattern), ClassOrType.CLASS, dexItemFactory));
       }
       builder.addPattern(listBuilder.build());
     }
@@ -142,5 +144,36 @@
     assertFalse(ProguardNameMatcher.matchFieldOrMethodName("getObject?", "getObject"));
     assertTrue(ProguardNameMatcher.matchFieldOrMethodName("getObject?", "getObject1"));
     assertTrue(ProguardNameMatcher.matchFieldOrMethodName("getObject?", "getObject5"));
- }
+  }
+
+  private static IdentifierPatternWithWildcards toIdentifierPatternWithWildCards(String pattern) {
+    ImmutableList.Builder<String> builder = ImmutableList.builder();
+    String allPattern = "";
+    String backReference = "";
+    for (int i = 0; i < pattern.length(); i++) {
+      char patternChar = pattern.charAt(i);
+      if (patternChar == '?' || patternChar == '%') {
+        builder.add(String.valueOf(patternChar));
+      } else if (patternChar == '*') {
+        allPattern += patternChar;
+      } else if (patternChar == '<') {
+        backReference += patternChar;
+      } else if (patternChar == '>') {
+        backReference += patternChar;
+        builder.add(backReference);
+        backReference = "";
+      } else {
+        if (allPattern.length() > 0) {
+          builder.add(allPattern);
+          allPattern = "";
+        } else if (backReference.length() > 0) {
+          backReference += patternChar;
+        }
+      }
+    }
+    if (allPattern.length() > 0) {
+      builder.add(allPattern);
+    }
+    return new IdentifierPatternWithWildcards(pattern, builder.build());
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
index abbe3d3..1151648 100644
--- a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
@@ -50,7 +50,6 @@
 import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Set;
 import org.junit.Test;
 
 public class ForceProguardCompatibilityTest extends TestBase {
@@ -157,7 +156,7 @@
       assertEquals(1, classNames.size());
       assertEquals(testClass.getCanonicalName(),
           classNames.asSpecificDexTypes().get(0).toSourceString());
-      Set<ProguardMemberRule> memberRules = configuration.getRules().get(0).getMemberRules();
+      List<ProguardMemberRule> memberRules = configuration.getRules().get(0).getMemberRules();
       assertEquals(1, memberRules.size());
       assertEquals(ProguardMemberType.INIT, memberRules.iterator().next().getRuleType());
     } else {
@@ -279,7 +278,7 @@
         assertEquals(ProguardKeepRuleType.KEEP, rule.getType());
         assertTrue(rule.getModifiers().allowsObfuscation);
         assertTrue(rule.getModifiers().allowsOptimization);
-        Set<ProguardMemberRule> memberRules = rule.getMemberRules();
+        List<ProguardMemberRule> memberRules = rule.getMemberRules();
         ProguardClassNameList classNames = rule.getClassNames();
         assertEquals(1, classNames.size());
         DexType type = classNames.asSpecificDexTypes().get(0);
@@ -295,7 +294,7 @@
         }
       });
       Iterables.filter(rules, ProguardIdentifierNameStringRule.class).forEach(rule -> {
-        Set<ProguardMemberRule> memberRules = rule.getMemberRules();
+        List<ProguardMemberRule> memberRules = rule.getMemberRules();
         ProguardClassNameList classNames = rule.getClassNames();
         assertEquals(1, classNames.size());
         DexType type = classNames.asSpecificDexTypes().get(0);
@@ -388,7 +387,7 @@
         assertEquals(ProguardKeepRuleType.KEEP_CLASS_MEMBERS, rule.getType());
         assertTrue(rule.getModifiers().allowsObfuscation);
         assertTrue(rule.getModifiers().allowsOptimization);
-        Set<ProguardMemberRule> memberRules = rule.getMemberRules();
+        List<ProguardMemberRule> memberRules = rule.getMemberRules();
         ProguardClassNameList classNames = rule.getClassNames();
         assertEquals(1, classNames.size());
         DexType type = classNames.asSpecificDexTypes().get(0);
@@ -403,7 +402,7 @@
         }
       });
       Iterables.filter(rules, ProguardIdentifierNameStringRule.class).forEach(rule -> {
-        Set<ProguardMemberRule> memberRules = rule.getMemberRules();
+        List<ProguardMemberRule> memberRules = rule.getMemberRules();
         ProguardClassNameList classNames = rule.getClassNames();
         assertEquals(1, classNames.size());
         DexType type = classNames.asSpecificDexTypes().get(0);
@@ -504,7 +503,7 @@
         assertEquals(ProguardKeepRuleType.KEEP_CLASS_MEMBERS, rule.getType());
         assertTrue(rule.getModifiers().allowsObfuscation);
         assertTrue(rule.getModifiers().allowsOptimization);
-        Set<ProguardMemberRule> memberRules = rule.getMemberRules();
+        List<ProguardMemberRule> memberRules = rule.getMemberRules();
         ProguardClassNameList classNames = rule.getClassNames();
         assertEquals(1, classNames.size());
         DexType type = classNames.asSpecificDexTypes().get(0);
@@ -519,7 +518,7 @@
       assertTrue(keptFields.containsKey("longField"));
       assertTrue(keptFields.containsKey("objField"));
       Iterables.filter(rules, ProguardIdentifierNameStringRule.class).forEach(rule -> {
-        Set<ProguardMemberRule> memberRules = rule.getMemberRules();
+        List<ProguardMemberRule> memberRules = rule.getMemberRules();
         ProguardClassNameList classNames = rule.getClassNames();
         assertEquals(1, classNames.size());
         DexType type = classNames.asSpecificDexTypes().get(0);
@@ -683,7 +682,7 @@
   private void defaultMethodCompatibilityRules(ProguardConfiguration configuration) {
     assertEquals(1, configuration.getRules().size());
     ProguardConfigurationRule rule = configuration.getRules().get(0);
-    Set<ProguardMemberRule> memberRules = rule.getMemberRules();
+    List<ProguardMemberRule> memberRules = rule.getMemberRules();
     ProguardClassNameList classNames = rule.getClassNames();
     assertEquals(1, classNames.size());
     DexType type = classNames.asSpecificDexTypes().get(0);
@@ -708,7 +707,7 @@
   private void defaultMethod2CompatibilityRules(ProguardConfiguration configuration) {
     assertEquals(1, configuration.getRules().size());
     ProguardConfigurationRule rule = configuration.getRules().get(0);
-    Set<ProguardMemberRule> memberRules = rule.getMemberRules();
+    List<ProguardMemberRule> memberRules = rule.getMemberRules();
     ProguardClassNameList classNames = rule.getClassNames();
     assertEquals(1, classNames.size());
     DexType type = classNames.asSpecificDexTypes().get(0);
diff --git a/src/test/java/com/android/tools/r8/utils/DexInspector.java b/src/test/java/com/android/tools/r8/utils/DexInspector.java
index 0cea762..29676da 100644
--- a/src/test/java/com/android/tools/r8/utils/DexInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/DexInspector.java
@@ -821,7 +821,7 @@
   }
 
   public abstract class FieldSubject extends MemberSubject {
-    public abstract boolean hasStaticValue();
+    public abstract boolean hasExplicitStaticValue();
 
     public abstract DexEncodedField getField();
 
@@ -863,7 +863,7 @@
     }
 
     @Override
-    public boolean hasStaticValue() {
+    public boolean hasExplicitStaticValue() {
       return false;
     }
 
@@ -928,13 +928,13 @@
     }
 
     @Override
-    public boolean hasStaticValue() {
-      return dexField.staticValue != null;
+    public boolean hasExplicitStaticValue() {
+      return isStatic() && dexField.hasExplicitStaticValue();
     }
 
     @Override
     public DexValue getStaticValue() {
-      return dexField.staticValue;
+      return dexField.getStaticValue();
     }
 
     @Override