Merge remote-tracking branch 'aosp/upstream-mirror' into master am: e60b2c831a am: 2a277aff30
am: fc5cad1237

Change-Id: Ia143a8489d5ecedc4665717300582d18f9b51a71
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index a710912..aed4886 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -363,6 +363,9 @@
     internal.skipMinification = !useMinification();
     assert internal.useTreeShaking;
     internal.useTreeShaking = useTreeShaking();
+    assert !internal.printUsage;
+    internal.printUsage = proguardConfiguration.isPrintUsage();
+    internal.printUsageFile = proguardConfiguration.getPrintUsageFile();
     assert !internal.ignoreMissingClasses;
     internal.ignoreMissingClasses = ignoreMissingClasses;
 
diff --git a/src/main/java/com/android/tools/r8/ReadMainDexList.java b/src/main/java/com/android/tools/r8/ReadMainDexList.java
index 3a76c1f..a0c4789 100644
--- a/src/main/java/com/android/tools/r8/ReadMainDexList.java
+++ b/src/main/java/com/android/tools/r8/ReadMainDexList.java
@@ -6,7 +6,11 @@
 import com.android.tools.r8.naming.ClassNameMapper;
 import com.android.tools.r8.naming.ProguardMapReader;
 import com.android.tools.r8.utils.FileUtils;
+import com.google.common.collect.Iterators;
+import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.util.Iterator;
+import java.util.function.Function;
 import java.util.stream.Collectors;
 
 /**
@@ -20,8 +24,12 @@
     return name.endsWith(DOT_CLASS) ? name.substring(0, name.length() - DOT_CLASS.length()) : name;
   }
 
-  private String addDotClass(String name) {
-    return name + DOT_CLASS;
+  private String toClassFilePath(String name) {
+    return name.replace('.', '/') + DOT_CLASS;
+  }
+
+  private String toKeepRule(String className) {
+    return "-keep class " + className + " {}";
   }
 
   private String deobfuscateClassName(String name, ClassNameMapper mapper) {
@@ -32,21 +40,31 @@
   }
 
   private void run(String[] args) throws Exception {
-    if (args.length != 1 && args.length != 2) {
-      System.out.println("Usage: command <main_dex_list> [<proguard_map>]");
+    if (args.length < 1 || args.length > 3) {
+      System.out.println("Usage: command [-k] <main_dex_list> [<proguard_map>]");
       System.exit(0);
     }
 
-    final ClassNameMapper mapper =
-        args.length == 2 ? ProguardMapReader.mapperFromFile(Paths.get(args[1])) : null;
+    Iterator<String> arguments = Iterators.forArray(args);
+    Function<String, String> outputGenerator;
+    String arg = arguments.next();
+    if (arg.equals("-k")) {
+      outputGenerator = this::toKeepRule;
+      arg = arguments.next();
+    } else {
+      outputGenerator = this::toClassFilePath;
+    }
+    Path mainDexList = Paths.get(arg);
 
-    FileUtils.readTextFile(Paths.get(args[0]))
+    final ClassNameMapper mapper =
+        arguments.hasNext() ? ProguardMapReader.mapperFromFile(Paths.get(arguments.next())) : null;
+
+    FileUtils.readTextFile(mainDexList)
         .stream()
         .map(this::stripDotClass)
         .map(name -> name.replace('/', '.'))
         .map(name -> deobfuscateClassName(name, mapper))
-        .map(name -> name.replace('.', '/'))
-        .map(this::addDotClass)
+        .map(outputGenerator)
         .sorted()
         .collect(Collectors.toList())
         .forEach(System.out::println);
diff --git a/src/main/java/com/android/tools/r8/dex/DexFileReader.java b/src/main/java/com/android/tools/r8/dex/DexFileReader.java
index a064494..1c37073 100644
--- a/src/main/java/com/android/tools/r8/dex/DexFileReader.java
+++ b/src/main/java/com/android/tools/r8/dex/DexFileReader.java
@@ -47,6 +47,7 @@
 import com.android.tools.r8.graph.DexValue;
 import com.android.tools.r8.graph.DexValue.DexValueMethodHandle;
 import com.android.tools.r8.graph.DexValue.DexValueMethodType;
+import com.android.tools.r8.graph.DexValue.DexValueNull;
 import com.android.tools.r8.graph.DexValue.DexValueString;
 import com.android.tools.r8.graph.OffsetToObjectMapping;
 import com.android.tools.r8.logging.Log;
@@ -226,7 +227,7 @@
       }
       case DexValue.VALUE_NULL: {
         assert valueArg == 0;
-        return DexValue.NULL;
+        return DexValueNull.NULL;
       }
       case DexValue.VALUE_BOOLEAN: {
         // 0 is false, and 1 is true.
diff --git a/src/main/java/com/android/tools/r8/dex/FileWriter.java b/src/main/java/com/android/tools/r8/dex/FileWriter.java
index 6c6c3e1..00eea7c 100644
--- a/src/main/java/com/android/tools/r8/dex/FileWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/FileWriter.java
@@ -215,18 +215,16 @@
         this::writeStringData);
     writeItems(mixedSectionOffsets.getAnnotations(), layout::setAnnotationsOffset,
         this::writeAnnotation);
-    writeItems(mixedSectionOffsets.getAnnotationSets(), layout::setAnnotationSetsOffset,
-        this::writeAnnotationSet, 4);
-    writeItems(mixedSectionOffsets.getAnnotationSetRefLists(),
-        layout::setAnnotationSetRefListsOffset, this::writeAnnotationSetRefList, 4);
-    // Write the annotation directories.
-    writeItems(mixedSectionOffsets.getAnnotationDirectories(),
-        layout::setAnnotationDirectoriesOffset, this::writeAnnotationDirectory, 4);
-    // Write the rest.
     writeItems(mixedSectionOffsets.getClassesWithData(), layout::setClassDataOffset,
         this::writeClassData);
     writeItems(mixedSectionOffsets.getEncodedArrays(), layout::setEncodedArrarysOffset,
         this::writeEncodedArray);
+    writeItems(mixedSectionOffsets.getAnnotationSets(), layout::setAnnotationSetsOffset,
+        this::writeAnnotationSet, 4);
+    writeItems(mixedSectionOffsets.getAnnotationSetRefLists(),
+        layout::setAnnotationSetRefListsOffset, this::writeAnnotationSetRefList, 4);
+    writeItems(mixedSectionOffsets.getAnnotationDirectories(),
+        layout::setAnnotationDirectoriesOffset, this::writeAnnotationDirectory, 4);
 
     // Add the map at the end
     layout.setMapOffset(dest.align(4));
@@ -760,6 +758,10 @@
         mixedSectionOffsets.getStringData().size());
     size += writeMapItem(Constants.TYPE_ANNOTATION_ITEM, layout.getAnnotationsOffset(),
         mixedSectionOffsets.getAnnotations().size());
+    size += writeMapItem(Constants.TYPE_CLASS_DATA_ITEM, layout.getClassDataOffset(),
+        mixedSectionOffsets.getClassesWithData().size());
+    size += writeMapItem(Constants.TYPE_ENCODED_ARRAY_ITEM, layout.getEncodedArrarysOffset(),
+        mixedSectionOffsets.getEncodedArrays().size());
     size += writeMapItem(Constants.TYPE_ANNOTATION_SET_ITEM, layout.getAnnotationSetsOffset(),
         mixedSectionOffsets.getAnnotationSets().size());
     size += writeMapItem(Constants.TYPE_ANNOTATION_SET_REF_LIST,
@@ -768,10 +770,6 @@
     size += writeMapItem(Constants.TYPE_ANNOTATIONS_DIRECTORY_ITEM,
         layout.getAnnotationDirectoriesOffset(),
         mixedSectionOffsets.getAnnotationDirectories().size());
-    size += writeMapItem(Constants.TYPE_CLASS_DATA_ITEM, layout.getClassDataOffset(),
-        mixedSectionOffsets.getClassesWithData().size());
-    size += writeMapItem(Constants.TYPE_ENCODED_ARRAY_ITEM, layout.getEncodedArrarysOffset(),
-        mixedSectionOffsets.getEncodedArrays().size());
     size += writeMapItem(Constants.TYPE_MAP_LIST, layout.getMapOffset(), 1);
     dest.moveTo(startOfMap);
     dest.putInt(size);
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotation.java b/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
index 66d9774..55aa9868 100644
--- a/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
+++ b/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
@@ -9,6 +9,7 @@
 import com.android.tools.r8.graph.DexValue.DexValueArray;
 import com.android.tools.r8.graph.DexValue.DexValueInt;
 import com.android.tools.r8.graph.DexValue.DexValueMethod;
+import com.android.tools.r8.graph.DexValue.DexValueNull;
 import com.android.tools.r8.graph.DexValue.DexValueString;
 import com.android.tools.r8.graph.DexValue.DexValueType;
 import java.util.ArrayList;
@@ -111,7 +112,7 @@
                 new DexAnnotationElement(
                     factory.createString("name"),
                     (clazz == null)
-                        ? DexValue.NULL
+                        ? DexValueNull.NULL
                         : new DexValueString(factory.createString(clazz)))
             }));
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index 3df2fab..c8fbbea 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -12,9 +12,12 @@
 import com.android.tools.r8.code.ConstString;
 import com.android.tools.r8.code.ConstStringJumbo;
 import com.android.tools.r8.code.Instruction;
+import com.android.tools.r8.code.InvokeDirect;
 import com.android.tools.r8.code.InvokeStatic;
+import com.android.tools.r8.code.InvokeSuper;
 import com.android.tools.r8.code.NewInstance;
 import com.android.tools.r8.code.Throw;
+import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.dex.IndexedItemCollection;
 import com.android.tools.r8.dex.MixedSectionCollection;
 import com.android.tools.r8.ir.code.IRCode;
@@ -30,6 +33,7 @@
 import com.android.tools.r8.naming.ClassNameMapper;
 import com.android.tools.r8.naming.MemberNaming.MethodSignature;
 import com.android.tools.r8.naming.MemberNaming.Signature;
+import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
 import com.android.tools.r8.utils.InternalOptions;
 
 public class DexEncodedMethod extends KeyedDexItem<DexMethod> {
@@ -238,7 +242,7 @@
    * templates might incur a size overhead.
    */
   private DexCode generateCodeFromTemplate(
-      int numberOfRegisters, int outRegisters, Instruction[] instructions) {
+      int numberOfRegisters, int outRegisters, Instruction... instructions) {
     int offset = 0;
     for (Instruction instruction : instructions) {
       assert !(instruction instanceof ConstString);
@@ -276,21 +280,33 @@
         .createMethod(itemFactory.createType("Landroid/util/Log;"), proto,
             itemFactory.createString("e"));
     DexType exceptionType = itemFactory.createType("Ljava/lang/RuntimeException;");
-    DexType[] exceptionArgs = {exceptionType, itemFactory.stringType};
-    DexMethod initMethod = itemFactory
-        .createMethod(exceptionType, itemFactory.createProto(itemFactory.voidType, exceptionArgs),
+    DexMethod exceptionInitMethod = itemFactory
+        .createMethod(exceptionType, itemFactory.createProto(itemFactory.voidType,
+            itemFactory.stringType),
             itemFactory.constructorMethodName);
-    // These methods might not get registered for jumbo string processing, therefore we always
-    // use the jumbo string encoding for the const string instruction.
-    Instruction insn[] = {
-        new ConstStringJumbo(0, tag),
-        new ConstStringJumbo(1, message),
-        new InvokeStatic(2, logMethod, 0, 1, 0, 0, 0),
-        new NewInstance(0, exceptionType),
-        new InvokeStatic(2, initMethod, 0, 1, 0, 0, 0),
-        new Throw(0)
-    };
-    DexCode code = generateCodeFromTemplate(2, 2, insn);
+    DexCode code;
+    if (accessFlags.isConstructor() && !accessFlags.isStatic()) {
+      // The Java VM Spec requires that a constructor calls an initializer from the super class
+      // or another constructor from the current class. For simplicity we do the latter by just
+      // calling outself. This is ok, as the constructor always throws before the recursive call.
+      code = generateCodeFromTemplate(3, 2, new ConstStringJumbo(0, tag),
+          new ConstStringJumbo(1, message),
+          new InvokeStatic(2, logMethod, 0, 1, 0, 0, 0),
+          new NewInstance(0, exceptionType),
+          new InvokeDirect(2, exceptionInitMethod, 0, 1, 0, 0, 0),
+          new Throw(0),
+          new InvokeDirect(1, method, 2, 0, 0, 0, 0));
+
+    } else {
+      // These methods might not get registered for jumbo string processing, therefore we always
+      // use the jumbo string encoding for the const string instruction.
+      code = generateCodeFromTemplate(2, 2, new ConstStringJumbo(0, tag),
+          new ConstStringJumbo(1, message),
+          new InvokeStatic(2, logMethod, 0, 1, 0, 0, 0),
+          new NewInstance(0, exceptionType),
+          new InvokeDirect(2, exceptionInitMethod, 0, 1, 0, 0, 0),
+          new Throw(0));
+    }
     Builder builder = builder(this);
     builder.setCode(code);
     return builder.build();
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 984215b..5dff4b4 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -222,32 +222,22 @@
       DexString toStringMethodName = createString("toString");
 
 
-      appendBoolean =
-          createMethod(receiver, createProto(receiver, new DexType[]{booleanType}), append);
-      appendChar = createMethod(receiver, createProto(receiver, new DexType[]{charType}), append);
-      appendCharArray =
-          createMethod(receiver, createProto(receiver, new DexType[]{charArrayType}), append);
+      appendBoolean = createMethod(receiver, createProto(receiver, booleanType), append);
+      appendChar = createMethod(receiver, createProto(receiver, charType), append);
+      appendCharArray = createMethod(receiver, createProto(receiver, charArrayType), append);
       appendSubCharArray =
-          createMethod(receiver,
-              createProto(receiver, new DexType[]{charArrayType, intType, intType}), append);
-      appendCharSequence =
-          createMethod(receiver, createProto(receiver, new DexType[]{charSequenceType}), append);
+          createMethod(receiver, createProto(receiver, charArrayType, intType, intType), append);
+      appendCharSequence = createMethod(receiver, createProto(receiver, charSequenceType), append);
       appendSubCharSequence =
-          createMethod(receiver,
-              createProto(receiver, new DexType[]{charSequenceType, intType, intType}), append);
-      appendInt = createMethod(receiver, createProto(receiver, new DexType[]{intType}), append);
-      appendDouble =
-          createMethod(receiver, createProto(receiver, new DexType[]{doubleType}), append);
-      appendFloat = createMethod(receiver, createProto(receiver, new DexType[]{floatType}), append);
-      appendLong = createMethod(receiver, createProto(receiver, new DexType[]{longType}), append);
-      appendObject =
-          createMethod(receiver, createProto(receiver, new DexType[]{objectType}), append);
-      appendString =
-          createMethod(receiver, createProto(receiver, new DexType[]{stringType}), append);
-      appendStringBuffer =
-          createMethod(receiver, createProto(receiver, new DexType[]{sbufType}), append);
-      toString =
-          createMethod(receiver, createProto(stringType, DexType.EMPTY_ARRAY), toStringMethodName);
+          createMethod(receiver, createProto(receiver, charSequenceType, intType, intType), append);
+      appendInt = createMethod(receiver, createProto(receiver, intType), append);
+      appendDouble = createMethod(receiver, createProto(receiver, doubleType), append);
+      appendFloat = createMethod(receiver, createProto(receiver, floatType), append);
+      appendLong = createMethod(receiver, createProto(receiver, longType), append);
+      appendObject = createMethod(receiver, createProto(receiver, objectType), append);
+      appendString = createMethod(receiver, createProto(receiver, stringType), append);
+      appendStringBuffer = createMethod(receiver, createProto(receiver, sbufType), append);
+      toString = createMethod(receiver, createProto(stringType), toStringMethodName);
     }
 
     public void forEachAppendMethod(Consumer<DexMethod> consumer) {
@@ -322,7 +312,7 @@
         parameters.length == 0 ? DexTypeList.empty() : new DexTypeList(parameters));
   }
 
-  public DexProto createProto(DexType returnType, DexType[] parameters) {
+  public DexProto createProto(DexType returnType, DexType... parameters) {
     return createProto(createShorty(returnType, parameters), returnType, parameters);
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java b/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
index e59e7c9..d223735 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
@@ -132,7 +132,7 @@
   public boolean computeEquals(Object other) {
     if (other instanceof DexMethodHandle) {
       DexMethodHandle o = (DexMethodHandle) other;
-      return type.equals(o.type) && fieldOrMethod.computeEquals(o.fieldOrMethod);
+      return type.equals(o.type) && fieldOrMethod.equals(o.fieldOrMethod);
     }
     return false;
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexType.java b/src/main/java/com/android/tools/r8/graph/DexType.java
index 4d3d8cb..234773f 100644
--- a/src/main/java/com/android/tools/r8/graph/DexType.java
+++ b/src/main/java/com/android/tools/r8/graph/DexType.java
@@ -19,8 +19,6 @@
 
 public class DexType extends IndexedDexItem implements PresortedComparable<DexType> {
 
-  public static final DexType[] EMPTY_ARRAY = new DexType[]{};
-
   private final static int ROOT_LEVEL = 0;
   private final static int UNKNOWN_LEVEL = -1;
   private final static int INTERFACE_LEVEL = -2;
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 5430d6e..a0cfa7c 100644
--- a/src/main/java/com/android/tools/r8/graph/DexValue.java
+++ b/src/main/java/com/android/tools/r8/graph/DexValue.java
@@ -36,8 +36,6 @@
   public static final byte VALUE_NULL = 0x1e;
   public static final byte VALUE_BOOLEAN = 0x1f;
 
-  public static final DexValue NULL = new DexValueNull();
-
   private static void writeHeader(byte type, int arg, DexOutputBuffer dest) {
     dest.putByte((byte) ((arg << 5) | type));
   }
@@ -87,7 +85,7 @@
       return DexValueDouble.DEFAULT;
     }
     if (type.isArrayType() || type.isClassType()) {
-      return DexValue.NULL;
+      return DexValueNull.NULL;
     }
     throw new Unreachable("No default value for unexpected type " + type);
   }
@@ -688,7 +686,9 @@
 
   static public class DexValueNull extends SimpleDexValue {
 
-    // See DexValue.NULL
+    public static final DexValue NULL = new DexValueNull();
+
+    // See DexValueNull.NULL
     private DexValueNull() {
     }
 
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 59f187f..ce38bc9 100644
--- a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
+++ b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
@@ -20,6 +20,7 @@
 import com.android.tools.r8.graph.DexValue.DexValueFloat;
 import com.android.tools.r8.graph.DexValue.DexValueInt;
 import com.android.tools.r8.graph.DexValue.DexValueLong;
+import com.android.tools.r8.graph.DexValue.DexValueNull;
 import com.android.tools.r8.graph.DexValue.DexValueShort;
 import com.android.tools.r8.graph.DexValue.DexValueString;
 import com.android.tools.r8.graph.DexValue.DexValueType;
@@ -699,7 +700,7 @@
 
     private DexValue getDexValue(Object value) {
       if (value == null) {
-        return DexValue.NULL;
+        return DexValueNull.NULL;
       }
       if (value instanceof Byte) {
         return DexValueByte.create((Byte) value);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
index cb518a3..c2600b4 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -23,7 +23,7 @@
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DexTypeList;
-import com.android.tools.r8.graph.DexValue;
+import com.android.tools.r8.graph.DexValue.DexValueNull;
 import com.android.tools.r8.ir.code.Invoke;
 import com.android.tools.r8.ir.synthetic.SynthesizedCode;
 import java.util.List;
@@ -219,7 +219,7 @@
         new DexAccessFlags(Constants.ACC_PUBLIC | Constants.ACC_FINAL
             | Constants.ACC_SYNTHETIC | Constants.ACC_STATIC),
         DexAnnotationSet.empty(),
-        DexValue.NULL);
+        DexValueNull.NULL);
     return fields;
   }
 
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 2355e5e..9ec776f 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
@@ -4,8 +4,6 @@
 
 package com.android.tools.r8.ir.desugar;
 
-import static com.android.tools.r8.ir.desugar.LambdaRewriter.EMPTY_TYPE_ARRAY;
-
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexItemFactory;
@@ -421,28 +419,28 @@
     DexProto proto;
     switch (primitive) {
       case 'Z':  // byte
-        proto = factory.createProto(factory.booleanType, EMPTY_TYPE_ARRAY);
+        proto = factory.createProto(factory.booleanType);
         return factory.createMethod(boxType, proto, factory.unboxBooleanMethodName);
       case 'B':  // byte
-        proto = factory.createProto(factory.byteType, EMPTY_TYPE_ARRAY);
+        proto = factory.createProto(factory.byteType);
         return factory.createMethod(boxType, proto, factory.unboxByteMethodName);
       case 'S':  // short
-        proto = factory.createProto(factory.shortType, EMPTY_TYPE_ARRAY);
+        proto = factory.createProto(factory.shortType);
         return factory.createMethod(boxType, proto, factory.unboxShortMethodName);
       case 'C':  // char
-        proto = factory.createProto(factory.charType, EMPTY_TYPE_ARRAY);
+        proto = factory.createProto(factory.charType);
         return factory.createMethod(boxType, proto, factory.unboxCharMethodName);
       case 'I':  // int
-        proto = factory.createProto(factory.intType, EMPTY_TYPE_ARRAY);
+        proto = factory.createProto(factory.intType);
         return factory.createMethod(boxType, proto, factory.unboxIntMethodName);
       case 'J':  // long
-        proto = factory.createProto(factory.longType, EMPTY_TYPE_ARRAY);
+        proto = factory.createProto(factory.longType);
         return factory.createMethod(boxType, proto, factory.unboxLongMethodName);
       case 'F':  // float
-        proto = factory.createProto(factory.floatType, EMPTY_TYPE_ARRAY);
+        proto = factory.createProto(factory.floatType);
         return factory.createMethod(boxType, proto, factory.unboxFloatMethodName);
       case 'D':  // double
-        proto = factory.createProto(factory.doubleType, EMPTY_TYPE_ARRAY);
+        proto = factory.createProto(factory.doubleType);
         return factory.createMethod(boxType, proto, factory.unboxDoubleMethodName);
       default:
         throw new Unreachable("Invalid primitive type descriptor: " + primitive);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
index 10908f0..7d3bc33 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
@@ -54,7 +54,6 @@
 
   static final String LAMBDA_CLASS_NAME_PREFIX = "-$$Lambda$";
   static final String EXPECTED_LAMBDA_METHOD_PREFIX = "lambda$";
-  static final DexType[] EMPTY_TYPE_ARRAY = new DexType[0];
   static final String LAMBDA_INSTANCE_FIELD_NAME = "INSTANCE";
 
   final IRConverter converter;
@@ -103,20 +102,17 @@
     DexType objectArrayType = factory.createType(OBJECT_ARRAY_TYPE_DESCR);
 
     this.metafactoryMethod = factory.createMethod(metafactoryType,
-        factory.createProto(callSiteType, new DexType[] {
-            lookupType, factory.stringType, methodTypeType,
-            methodTypeType, methodHandleType, methodTypeType
-        }),
+        factory.createProto(callSiteType, lookupType, factory.stringType, methodTypeType,
+            methodTypeType, methodHandleType, methodTypeType),
         factory.createString(METAFACTORY_METHOD_NAME));
 
     this.metafactoryAltMethod = factory.createMethod(metafactoryType,
-        factory.createProto(callSiteType, new DexType[] {
-            lookupType, factory.stringType, methodTypeType, objectArrayType
-        }),
+        factory.createProto(callSiteType, lookupType, factory.stringType, methodTypeType,
+            objectArrayType),
         factory.createString(METAFACTORY_ALT_METHOD_NAME));
 
     this.constructorName = factory.createString(Constants.INSTANCE_INITIALIZER_NAME);
-    DexProto initProto = factory.createProto(factory.voidType, EMPTY_TYPE_ARRAY);
+    DexProto initProto = factory.createProto(factory.voidType);
     this.objectInitMethod = factory.createMethod(factory.objectType, initProto, constructorName);
     this.classConstructorName = factory.createString(Constants.CLASS_INITIALIZER_NAME);
     this.instanceFieldName = factory.createString(LAMBDA_INSTANCE_FIELD_NAME);
@@ -124,7 +120,7 @@
 
     this.deserializeLambdaMethodName = factory.createString(DESERIALIZE_LAMBDA_METHOD_NAME);
     this.deserializeLambdaMethodProto = factory.createProto(
-        factory.objectType, new DexType[] { factory.createType(SERIALIZED_LAMBDA_TYPE_DESCR) });
+        factory.objectType, factory.createType(SERIALIZED_LAMBDA_TYPE_DESCR));
   }
 
   /**
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
index de80338..7c12590 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
@@ -22,16 +22,6 @@
 
 public class ClassNameMinifier {
 
-  /**
-   * We ban classes from these prefixes from minification. This is needed as some classes
-   * in the android sdk are given a @hide annotation, which will remove them from the
-   * sdk we tree-shake and minify against. Thus, the class will not be available and hence
-   * we won't find out that it is a library class.
-   * To save space, we by default minify classes we do not have an implementation for.
-   */
-  private static final Set<String> BANNED_CLASS_PREFIXES = ImmutableSet
-      .of("Ljava", "Landroid", "Ldalvik");
-
   private final AppInfoWithLiveness appInfo;
   private final RootSet rootSet;
   private final String packagePrefix;
@@ -65,17 +55,13 @@
         renaming.put(clazz.type, state.nextTypeName());
       }
     }
-    renameTypesInProtosOf(appInfo.staticInvokes);
-    renameTypesInProtosOf(appInfo.superInvokes);
-    renameTypesInProtosOf(appInfo.directInvokes);
-    renameTypesInProtosOf(appInfo.virtualInvokes);
     appInfo.dexItemFactory.forAllTypes(this::renameArrayTypeIfNeeded);
 
     return Collections.unmodifiableMap(renaming);
   }
 
   private String getPackageNameFor(DexClass clazz) {
-    if (packagePrefix == null || rootSet.keepPackageName.contains(clazz)) {
+    if ((packagePrefix == null) || rootSet.keepPackageName.contains(clazz)) {
       return clazz.type.getPackageDescriptor();
     } else {
       return packagePrefix;
@@ -86,38 +72,6 @@
     return states.computeIfAbsent(packageName, NamingState::new);
   }
 
-  private void renameTypesInProtosOf(Iterable<DexMethod> methods) {
-    for (DexMethod method : methods) {
-      renameTypeWithoutClassDefinition(method.proto.returnType);
-      for (DexType type : method.proto.parameters.values) {
-        renameTypeWithoutClassDefinition(type);
-      }
-    }
-  }
-
-  private void renameTypeWithoutClassDefinition(DexType type) {
-    if (type.isArrayType()) {
-      type = type.toBaseType(appInfo.dexItemFactory);
-    }
-    if (type.isClassType() && !renaming.containsKey(type)) {
-      DexClass clazz = appInfo.definitionFor(type);
-      if (clazz == null || !clazz.isLibraryClass()) {
-        if (!classIsBannedFromRenaming(type)) {
-          String packageName = packagePrefix == null ? type.getPackageDescriptor() : packagePrefix;
-          NamingState state = getStateFor(packageName);
-          renaming.put(type, state.nextTypeName());
-        }
-      }
-    }
-  }
-
-  private boolean classIsBannedFromRenaming(DexType type) {
-    String desc = type.toDescriptorString();
-    int index = desc.indexOf('/');
-    String prefix = desc.substring(0, index);
-    return index != -1 && BANNED_CLASS_PREFIXES.contains(prefix);
-  }
-
   private void renameArrayTypeIfNeeded(DexType type) {
     if (type.isArrayType()) {
       DexType base = type.toBaseType(appInfo.dexItemFactory);
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
index 9a63008..71a4dc6 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
@@ -24,6 +24,8 @@
     private boolean ignoreWarnings = false;
     private boolean obfuscating = true;
     private boolean shrinking = true;
+    private boolean printUsage = false;
+    private Path printUsageFile;
     private boolean printMapping;
     private Path printMappingOutput;
     private boolean verbose = false;
@@ -69,6 +71,14 @@
       this.shrinking = shrinking;
     }
 
+    public void setPrintUsage(boolean printUsage) {
+      this.printUsage = printUsage;
+    }
+
+    public void setPrintUsageFile(Path printUsageFile) {
+      this.printUsageFile = printUsageFile;
+    }
+
     public void setPrintMapping(boolean printMapping) {
       this.printMapping = printMapping;
     }
@@ -123,6 +133,8 @@
           ignoreWarnings,
           obfuscating,
           shrinking,
+          printUsage,
+          printUsageFile,
           printMapping,
           printMappingOutput,
           verbose,
@@ -145,6 +157,8 @@
   private final boolean ignoreWarnings;
   private final boolean obfuscating;
   private final boolean shrinking;
+  private final boolean printUsage;
+  private final Path printUsageFile;
   private final boolean printMapping;
   private final Path printMappingOutput;
   private final boolean verbose;
@@ -166,6 +180,8 @@
       boolean ignoreWarnings,
       boolean obfuscating,
       boolean shrinking,
+      boolean printUsage,
+      Path printUsageFile,
       boolean printMapping,
       Path printMappingOutput,
       boolean verbose,
@@ -185,6 +201,8 @@
     this.ignoreWarnings = ignoreWarnings;
     this.obfuscating = obfuscating;
     this.shrinking = shrinking;
+    this.printUsage = printUsage;
+    this.printUsageFile = printUsageFile;
     this.printMapping = printMapping;
     this.printMappingOutput = printMappingOutput;
     this.verbose = verbose;
@@ -249,6 +267,14 @@
     return shrinking;
   }
 
+  public boolean isPrintUsage() {
+    return printUsage;
+  }
+
+  public Path getPrintUsageFile() {
+    return printUsageFile;
+  }
+
   public boolean isVerbose() {
     return verbose;
   }
@@ -293,6 +319,8 @@
           false                 /* ignoreWarnings */,
           false                 /* obfuscating */,
           false                 /* shrinking */,
+          false                 /* printUsage */,
+          null                  /* printUsageFile */,
           false                 /* printMapping */,
           null                  /* outputMapping */,
           false                 /* verbose */,
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 9fcb50a..3b78e1b 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -55,8 +55,7 @@
           "alwaysinline", "identifiernamestring", "whyarenotsimple");
 
   private static final List<String> warnedSingleArgOptions = ImmutableList
-      .of("printusage",
-          "renamesourcefileattribute",
+      .of("renamesourcefileattribute",
           "dontnote",
           "printconfiguration",
           // TODO -outjars (http://b/37137994) and -adaptresourcefilecontents (http://b/37139570)
@@ -154,6 +153,14 @@
         configurationBuilder.setObfuscating(false);
       } else if (acceptString("dontshrink")) {
         configurationBuilder.setShrinking(false);
+      } else if (acceptString("printusage")) {
+        configurationBuilder.setPrintUsage(true);
+        skipWhitespace();
+        if (isOptionalArgumentGiven()) {
+          configurationBuilder.setPrintUsageFile(parseFileName());
+        }
+        // TODO(b/36799826): once fully implemented, no longer necessary to warn.
+        System.out.println("WARNING: Ignoring option: -printusage");
       } else if (acceptString("verbose")) {
         configurationBuilder.setVerbose(true);
       } else if (acceptString("ignorewarnings")) {
@@ -177,7 +184,7 @@
       } else if (acceptString("printmapping")) {
         configurationBuilder.setPrintMapping(true);
         skipWhitespace();
-        if (!eof() && peekChar() != '-') {
+        if (isOptionalArgumentGiven()) {
           configurationBuilder.setPrintMappingOutput(parseFileName());
         }
       } else if (acceptString("assumenosideeffects")) {
@@ -199,7 +206,7 @@
       } else if (acceptString("printseeds")) {
         configurationBuilder.setPrintSeed(true);
         skipWhitespace();
-        if (!eof() && peekChar() != '-') {
+        if (isOptionalArgumentGiven()) {
           configurationBuilder.setSeedFile(parseFileName());
         }
       } else if (acceptString("obfuscationdictionary")) {
@@ -271,7 +278,7 @@
           Log.debug(ProguardConfigurationParser.class, "Skipping '-%s` option", name);
         }
         skipWhitespace();
-        if (!eof() && peekChar() != '-') {
+        if (isOptionalArgumentGiven()) {
           skipSingleArgument();
         }
         return true;
@@ -794,6 +801,10 @@
       return peekChar() == c;
     }
 
+    private boolean isOptionalArgumentGiven() {
+      return !eof() && !hasNextChar('-');
+    }
+
     private boolean acceptChar(char c) {
       if (hasNextChar(c)) {
         position++;
@@ -938,7 +949,6 @@
       }
     }
 
-
     private void checkNotNegatedPattern() throws ProguardRuleParserException {
       skipWhitespace();
       if (acceptChar('!')) {
diff --git a/src/main/java/com/android/tools/r8/shaking/TreePruner.java b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
index 04f6786..3968c9a 100644
--- a/src/main/java/com/android/tools/r8/shaking/TreePruner.java
+++ b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
@@ -55,14 +55,15 @@
   private List<DexProgramClass> getNewProgramClasses(List<DexProgramClass> classes) {
     List<DexProgramClass> newClasses = new ArrayList<>();
     for (DexProgramClass clazz : classes) {
-      if (!appInfo.liveTypes.contains(clazz.type) && !options.debugKeepRules) {
+      if (!appInfo.liveTypes.contains(clazz.type)) {
         // The class is completely unused and we can remove it.
         if (Log.ENABLED) {
           Log.debug(getClass(), "Removing class: " + clazz);
         }
       } else {
         newClasses.add(clazz);
-        if (!appInfo.instantiatedTypes.contains(clazz.type) && !options.debugKeepRules) {
+        if (!appInfo.instantiatedTypes.contains(clazz.type) &&
+            (!options.debugKeepRules || !hasDefaultConstructor(clazz))) {
           // The class is only needed as a type but never instantiated. Make it abstract to reflect
           // this.
           if (clazz.accessFlags.isFinal()) {
@@ -86,6 +87,15 @@
     return newClasses;
   }
 
+  private boolean hasDefaultConstructor(DexProgramClass clazz) {
+    for (DexEncodedMethod method : clazz.directMethods()) {
+      if (isDefaultConstructor(method)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
   private <S extends PresortedComparable<S>, T extends KeyedDexItem<S>> int firstUnreachableIndex(
       T[] items, Set<S> live) {
     for (int i = 0; i < items.length; i++) {
@@ -96,6 +106,11 @@
     return -1;
   }
 
+  private boolean isDefaultConstructor(DexEncodedMethod method) {
+    return method.accessFlags.isConstructor() && !method.accessFlags.isStatic()
+        && method.method.proto.parameters.isEmpty();
+  }
+
   private DexEncodedMethod[] reachableMethods(DexEncodedMethod[] methods, DexClass clazz) {
     int firstUnreachable = firstUnreachableIndex(methods, appInfo.liveMethods);
     // Return the original array if all methods are used.
@@ -109,7 +124,7 @@
     for (int i = firstUnreachable; i < methods.length; i++) {
       if (appInfo.liveMethods.contains(methods[i].getKey())) {
         reachableMethods.add(methods[i]);
-      } else if (options.debugKeepRules) {
+      } else if (options.debugKeepRules && isDefaultConstructor(methods[i])) {
         // Keep the method but rewrite its body, if it has one.
         reachableMethods.add(methods[i].accessFlags.isAbstract()
             ? methods[i]
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 01d8c26..9d6cfa5 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -59,6 +59,8 @@
   public OutputMode outputMode = OutputMode.Indexed;
 
   public boolean useTreeShaking = true;
+  public boolean printUsage = false;
+  public Path printUsageFile = null;
 
   public boolean printCfg = false;
   public String printCfgFile;
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
index 0d0446d..22278e2 100644
--- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -96,6 +96,7 @@
   private static Map<String, Integer> needMinSdkVersion =
       new ImmutableMap.Builder<String, Integer>()
           // Android O
+          .put("952-invoke-custom", Constants.ANDROID_O_API)
           .put("953-invoke-polymorphic-compiler", Constants.ANDROID_O_API)
           .put("957-methodhandle-transforms", Constants.ANDROID_O_API)
           .put("958-methodhandle-stackframe", Constants.ANDROID_O_API)
@@ -218,37 +219,25 @@
       "073-mismatched-field"
   );
 
-  // Tests that make use of Java 8 features.
-  private static List<String> usesJava8 = ImmutableList.of(
-      "914-hello-obsolescence",
-      "915-obsolete-2",
-      "916-obsolete-jit",
-      "919-obsolete-fields",
-      "926-multi-obsolescence",
-      "936-search-onload",
-      "940-recursive-obsolete",
-      "941-recurive-obsolete-jit",
-      "942-private-recursive",
-      "943-private-recursive-jit",
-      "946-obsolete-throw",
-      "951-threaded-obsolete",
-      "952-invoke-custom"
-  );
-
   // Tests that make use of agents/native code. Our test setup does handle flags/linking of these.
   private static List<String> usesNativeAgentCode = ImmutableList.of(
       "497-inlining-and-class-loader",
       "626-const-class-linking",
       "642-fp-callees",
       "909-attach-agent",
+      "914-hello-obsolescence",
+      "915-obsolete-2",
+      "916-obsolete-jit",
       "917-fields-transformation",
       "918-fields",
+      "919-obsolete-fields",
       "920-objects",
       "921-hello-failure",
       "922-properties",
       "923-monitors",
       "924-threads",
       "925-threadgroups",
+      "926-multi-obsolescence",
       "927-timers",
       "928-jni-table",
       "929-search",
@@ -262,12 +251,18 @@
       "937-hello-retransform-package",
       "938-load-transform-bcp",
       "939-hello-transformation-bcp",
+      "940-recursive-obsolete",
+      "941-recurive-obsolete-jit",
+      "942-private-recursive",
+      "943-private-recursive-jit",
       "944-transform-classloaders",
       "945-obsolete-native",
+      "946-obsolete-throw",
       "947-reflect-method",
       "948-change-annotations",
       "949-in-memory-transform",
       "950-redefine-intrinsic",
+      "951-threaded-obsolete",
       "980-redefine-object"
   );
 
@@ -630,6 +625,9 @@
           // is in dex code which D8 does not convert. Therefore the error is a verification error
           // at runtime and that is expected.
           .put("142-classloader2", TestCondition.match(TestCondition.D8_COMPILER))
+          // Invoke-custom is supported by D8 and R8, but it can only run on our newest version
+          // of art.
+          .put("952-invoke-custom", beforeAndroidO)
           // Invoke-polymorphic is supported by D8 and R8, but it can only run on our newest version
           // of art.
           .put("953-invoke-polymorphic-compiler", beforeAndroidO)
@@ -734,6 +732,17 @@
                   .build())
           .build();
 
+  // Tests to skip on some conditions
+  private static final Multimap<String, TestCondition> testToSkip =
+      new ImmutableListMultimap.Builder<String, TestCondition>()
+          // Old runtimes used the legacy test directory which does not contain input for tools
+          // NONE and DX.
+          .put("952-invoke-custom", TestCondition.match(
+              TestCondition.tools(DexTool.NONE, DexTool.DX),
+              TestCondition.runtimes(
+                  DexVm.ART_4_4_4, DexVm.ART_5_1_1, DexVm.ART_6_0_1, DexVm.ART_7_0_0)))
+          .build();
+
   private static List<String> failuresToTriage = ImmutableList.of(
       // This is flaky.
       "104-growth-limit",
@@ -896,7 +905,6 @@
     skipArt.addAll(customRun);
 
     Set<String> skipTest = Sets.newHashSet(skipAltogether);
-    skipTest.addAll(usesJava8);
     skipTest.addAll(usesNativeAgentCode);
     skipTest.addAll(failuresToTriage);
 
@@ -915,6 +923,10 @@
         skipTest.addAll(noInputJar);
       }
 
+      // Collect the test that we should skip in this configuration
+      skipTest.addAll(collectTestsMatchingConditions(
+          dexTool, compilerUnderTest, dexVm, compilationMode, testToSkip));
+
       // Collect the test that we should skip in this configuration.
       skipArt.addAll(
           collectTestsMatchingConditions(
diff --git a/src/test/java/com/android/tools/r8/debug/ContinuousSteppingTest.java b/src/test/java/com/android/tools/r8/debug/ContinuousSteppingTest.java
new file mode 100644
index 0000000..ad94a53
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debug/ContinuousSteppingTest.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.debug;
+
+import java.util.Map;
+import org.apache.harmony.jpda.tests.framework.jdwp.Value;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class ContinuousSteppingTest extends DebugTestBase {
+
+  @Test
+  public void testArithmetic() throws Throwable {
+    runContinuousTest("Arithmetic");
+  }
+
+  @Test
+  public void testLocals() throws Throwable {
+    runContinuousTest("Locals");
+  }
+
+  private void runContinuousTest(String debuggeeClassName) throws Throwable {
+    runDebugTest(debuggeeClassName,
+        breakpoint(debuggeeClassName, "main"),
+        run(),
+        stepUntil(StepKind.OVER, StepLevel.INSTRUCTION, debuggeeState -> {
+          // Fetch local variables.
+          Map<String, Value> localValues = debuggeeState.getLocalValues();
+          Assert.assertNotNull(localValues);
+
+          // Always step until we actually exit the program.
+          return false;
+        }));
+  }
+
+}
diff --git a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
index a272eca..be25097 100644
--- a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
+++ b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
@@ -18,10 +18,10 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.Deque;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
-import java.util.Queue;
 import java.util.TreeMap;
 import java.util.function.Consumer;
 import java.util.function.Function;
@@ -341,7 +341,7 @@
      */
     private DebuggeeState debuggeeState = null;
 
-    private final Queue<Command> commandsQueue;
+    private final Deque<Command> commandsQueue;
 
     // Active event requests.
     private final Map<Integer, EventHandler> events = new TreeMap<>();
@@ -473,6 +473,9 @@
               artCommandBuilder.appendArtOption("-Xcompiler-option");
               artCommandBuilder.appendArtOption("--compiler-filter=interpret-only");
             }
+            if (DEBUG_TESTS) {
+              artCommandBuilder.appendArtOption("-verbose:jdwp");
+            }
             setProperty("jpda.settings.debuggeeJavaPath", artCommandBuilder.build());
           }
 
@@ -1031,8 +1034,8 @@
             stepRequestID = replyPacket.getNextValueAsInt();
             testBase.assertAllDataRead(replyPacket);
           }
-          testBase.events.put(stepRequestID, new StepEventHandler(stepRequestID, stepFilter,
-              stepUntil));
+          testBase.events
+              .put(stepRequestID, new StepEventHandler(this, stepRequestID, stepFilter, stepUntil));
 
           // Resume all threads.
           testBase.resume();
@@ -1092,13 +1095,17 @@
 
     private static class StepEventHandler extends DefaultEventHandler {
 
+      private final JUnit3Wrapper.Command.StepCommand stepCommand;
       private final int stepRequestID;
       private final StepFilter stepFilter;
       private final Function<DebuggeeState, Boolean> stepUntil;
 
-      private StepEventHandler(int stepRequestID,
+      private StepEventHandler(
+          JUnit3Wrapper.Command.StepCommand stepCommand,
+          int stepRequestID,
           StepFilter stepFilter,
           Function<DebuggeeState, Boolean> stepUntil) {
+        this.stepCommand = stepCommand;
         this.stepRequestID = stepRequestID;
         this.stepFilter = stepFilter;
         this.stepUntil = stepUntil;
@@ -1106,19 +1113,23 @@
 
       @Override
       public void handle(JUnit3Wrapper testBase) {
+        // Clear step event.
+        testBase.getMirror().clearEvent(EventKind.SINGLE_STEP, stepRequestID);
+        testBase.events.remove(Integer.valueOf(stepRequestID));
+
+        // Do we need to step again ?
+        boolean repeatStep = false;
         if (stepFilter
             .skipLocation(testBase.getMirror(), testBase.getDebuggeeState().getLocation())) {
-          // Keep the step active and resume so that we do another step.
-          testBase.resume();
+          repeatStep = true;
         } else if (stepUntil.apply(testBase.getDebuggeeState()) == Boolean.FALSE) {
-          // We must not stop yet.
-          testBase.resume();
-        } else {
-          // When hit, the single step must be cleared.
-          testBase.getMirror().clearEvent(EventKind.SINGLE_STEP, stepRequestID);
-          testBase.events.remove(Integer.valueOf(stepRequestID));
-          super.handle(testBase);
+          repeatStep = true;
         }
+        if (repeatStep) {
+          // In order to repeat the step now, we need to add it at the beginning of the queue.
+          testBase.commandsQueue.addFirst(stepCommand);
+        }
+        super.handle(testBase);
       }
     }
 
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 05b06af..242a7d5 100644
--- a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
@@ -18,9 +18,7 @@
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.List;
-import org.junit.Rule;
 import org.junit.Test;
-import org.junit.rules.ExpectedException;
 
 public class ProguardConfigurationParserTest extends TestBase {
 
@@ -47,7 +45,7 @@
   private static final String LIBRARY_JARS =
       VALID_PROGUARD_DIR + "library-jars.flags";
   private static final String LIBRARY_JARS_WIN =
-          VALID_PROGUARD_DIR + "library-jars-win.flags";
+      VALID_PROGUARD_DIR + "library-jars-win.flags";
   private static final String SEEDS =
       VALID_PROGUARD_DIR + "seeds.flags";
   private static final String SEEDS_2 =
@@ -58,6 +56,8 @@
       VALID_PROGUARD_DIR + "keepdirectories.flags";
   private static final String DONT_OBFUSCATE =
       VALID_PROGUARD_DIR + "dontobfuscate.flags";
+  private static final String DONT_SHRINK =
+      VALID_PROGUARD_DIR + "dontshrink.flags";
   private static final String DONT_SKIP_NON_PUBLIC_LIBRARY_CLASSES =
       VALID_PROGUARD_DIR + "dontskipnonpubliclibraryclasses.flags";
   private static final String DONT_SKIP_NON_PUBLIC_LIBRARY_CLASS_MEMBERS =
@@ -70,12 +70,13 @@
       VALID_PROGUARD_DIR + "skipnonpubliclibraryclasses.flags";
   private static final String PARSE_AND_SKIP_SINGLE_ARGUMENT =
       VALID_PROGUARD_DIR + "parse-and-skip-single-argument.flags";
+  private static final String PRINT_USAGE =
+      VALID_PROGUARD_DIR + "printusage.flags";
+  private static final String PRINT_USAGE_TO_FILE =
+      VALID_PROGUARD_DIR + "printusage-to-file.flags";
   private static final String TARGET =
       VALID_PROGUARD_DIR + "target.flags";
 
-  @Rule
-  public ExpectedException thrown = ExpectedException.none();
-
   @Test
   public void parse() throws IOException, ProguardRuleParserException {
     ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
@@ -327,6 +328,14 @@
   }
 
   @Test
+  public void parseDontshrink() throws IOException, ProguardRuleParserException {
+    ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+    parser.parse(Paths.get(DONT_SHRINK));
+    ProguardConfiguration config = parser.getConfig();
+    assertFalse(config.isShrinking());
+  }
+
+  @Test
   public void parseDontSkipNonPublicLibraryClasses()
       throws IOException, ProguardRuleParserException {
     ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
@@ -355,12 +364,14 @@
   }
 
   @Test
-  public void parseSkipNonPublicLibraryClasses()
-      throws IOException, ProguardRuleParserException {
-    thrown.expect(ProguardRuleParserException.class);
-    thrown.expectMessage("Unsupported option: -skipnonpubliclibraryclasses");
-    ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
-    parser.parse(Paths.get(SKIP_NON_PUBLIC_LIBRARY_CLASSES));
+  public void parseSkipNonPublicLibraryClasses() throws IOException {
+    try {
+      ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+      parser.parse(Paths.get(SKIP_NON_PUBLIC_LIBRARY_CLASSES));
+      fail();
+    } catch (ProguardRuleParserException e) {
+      assertTrue(e.getMessage().contains("Unsupported option: -skipnonpubliclibraryclasses"));
+    }
   }
 
   @Test
@@ -370,6 +381,24 @@
   }
 
   @Test
+  public void parsePrintUsage() throws IOException, ProguardRuleParserException {
+    ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+    parser.parse(Paths.get(PRINT_USAGE));
+    ProguardConfiguration config = parser.getConfig();
+    assertTrue(config.isPrintUsage());
+    assertNull(config.getPrintUsageFile());
+  }
+
+  @Test
+  public void parsePrintUsageToFile() throws IOException, ProguardRuleParserException {
+    ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+    parser.parse(Paths.get(PRINT_USAGE_TO_FILE));
+    ProguardConfiguration config = parser.getConfig();
+    assertTrue(config.isPrintUsage());
+    assertNotNull(config.getPrintUsageFile());
+  }
+
+  @Test
   public void parseTarget()
       throws IOException, ProguardRuleParserException {
     ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
@@ -378,15 +407,18 @@
 
   @Test
   public void parseInvalidKeepClassOption() throws IOException, ProguardRuleParserException {
-    thrown.expect(ProguardRuleParserException.class);
-    thrown.expectMessage("Unknown option at ");
-    ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
-    Path proguardConfig = writeTextToTempFile(
-        "-keepclassx public class * {  ",
-        "  native <methods>;           ",
-        "}                             "
-    );
-    parser.parse(proguardConfig);
+    try {
+      ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+      Path proguardConfig = writeTextToTempFile(
+          "-keepclassx public class * {  ",
+          "  native <methods>;           ",
+          "}                             "
+      );
+      parser.parse(proguardConfig);
+      fail();
+    } catch (ProguardRuleParserException e) {
+      assertTrue(e.getMessage().contains("Unknown option at "));
+    }
   }
 
   @Test
diff --git a/src/test/proguard/valid/dontshrink.flags b/src/test/proguard/valid/dontshrink.flags
new file mode 100644
index 0000000..3d7dbaa
--- /dev/null
+++ b/src/test/proguard/valid/dontshrink.flags
@@ -0,0 +1,5 @@
+# Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+-dontshrink
diff --git a/src/test/proguard/valid/printusage-to-file.flags b/src/test/proguard/valid/printusage-to-file.flags
new file mode 100644
index 0000000..c3dcdc5
--- /dev/null
+++ b/src/test/proguard/valid/printusage-to-file.flags
@@ -0,0 +1,5 @@
+# Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+-printusage ~/tmp/random_output_file.txt
diff --git a/src/test/proguard/valid/printusage.flags b/src/test/proguard/valid/printusage.flags
new file mode 100644
index 0000000..2606411
--- /dev/null
+++ b/src/test/proguard/valid/printusage.flags
@@ -0,0 +1,5 @@
+# Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+-printusage