Merge "Temporary workaround for class merging issue with static interface invokes"
diff --git a/build.gradle b/build.gradle
index 5631d94..9e84b01 100644
--- a/build.gradle
+++ b/build.gradle
@@ -276,6 +276,8 @@
                 "android_jar/lib-v24",
                 "android_jar/lib-v25",
                 "android_jar/lib-v26",
+                "android_jar/lib-v27",
+                "android_jar/lib-v28",
                 "core-lambda-stubs",
                 "dart-sdk",
                 "ddmlib",
diff --git a/src/main/java/com/android/tools/r8/PrintUses.java b/src/main/java/com/android/tools/r8/PrintUses.java
index 42fed08..15f5662 100644
--- a/src/main/java/com/android/tools/r8/PrintUses.java
+++ b/src/main/java/com/android/tools/r8/PrintUses.java
@@ -22,7 +22,6 @@
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
 import java.io.IOException;
-import java.io.PrintStream;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.ArrayList;
@@ -46,19 +45,20 @@
 public class PrintUses {
 
   private static final String USAGE =
-      "Arguments: <rt.jar> <r8.jar> <sample.jar>\n"
+      "Arguments: [--keeprules] <rt.jar> <r8.jar> <sample.jar>\n"
           + "\n"
           + "PrintUses prints the classes, interfaces, methods and fields used by <sample.jar>,\n"
           + "restricted to classes and interfaces in <r8.jar> that are not in <sample.jar>.\n"
           + "<rt.jar> and <r8.jar> should point to libraries used by <sample.jar>.\n"
           + "\n"
           + "The output is in the same format as what is printed when specifying -printseeds in\n"
-          + "a ProGuard configuration file. See also the "
+          + "a ProGuard configuration file. Use --keeprules for outputting proguard keep rules. "
+          + "See also the "
           + PrintSeeds.class.getSimpleName()
           + " program in R8.";
 
   private final Set<String> descriptors;
-  private final PrintStream out;
+  private final Printer printer;
   private Set<DexType> types = Sets.newIdentityHashSet();
   private Map<DexType, Set<DexMethod>> methods = Maps.newIdentityHashMap();
   private Map<DexType, Set<DexField>> fields = Maps.newIdentityHashMap();
@@ -210,21 +210,28 @@
     }
   }
 
-  public static void main(String[] args) throws Exception {
-    if (args.length != 3) {
+  public static void main(String... args) throws Exception {
+    if (args.length != 3 && args.length != 4) {
       System.out.println(USAGE.replace("\n", System.lineSeparator()));
       return;
     }
+    int argumentIndex = 0;
+    boolean printKeep = false;
+    if (args[0].equals("--keeprules")) {
+      printKeep = true;
+      argumentIndex++;
+    }
     AndroidApp.Builder builder = AndroidApp.builder();
-    Path rtJar = Paths.get(args[0]);
+    Path rtJar = Paths.get(args[argumentIndex++]);
     builder.addLibraryFile(rtJar);
-    Path r8Jar = Paths.get(args[1]);
+    Path r8Jar = Paths.get(args[argumentIndex++]);
     builder.addLibraryFile(r8Jar);
-    Path sampleJar = Paths.get(args[2]);
+    Path sampleJar = Paths.get(args[argumentIndex++]);
     builder.addProgramFile(sampleJar);
     Set<String> descriptors = new HashSet<>(getDescriptors(r8Jar));
     descriptors.removeAll(getDescriptors(sampleJar));
-    PrintUses printUses = new PrintUses(descriptors, builder.build(), System.out);
+    Printer printer = printKeep ? new KeepPrinter() : new DefaultPrinter();
+    PrintUses printUses = new PrintUses(descriptors, builder.build(), printer);
     printUses.analyze();
     printUses.print();
     if (printUses.errors > 0) {
@@ -237,10 +244,10 @@
     return new ArchiveClassFileProvider(path).getClassDescriptors();
   }
 
-  private PrintUses(Set<String> descriptors, AndroidApp inputApp, PrintStream out)
+  private PrintUses(Set<String> descriptors, AndroidApp inputApp, Printer printer)
       throws Exception {
     this.descriptors = descriptors;
-    this.out = out;
+    this.printer = printer;
     InternalOptions options = new InternalOptions();
     application =
         new ApplicationReader(inputApp, options, new Timing("PrintUses")).read().toDirect();
@@ -260,69 +267,178 @@
   }
 
   private void print() {
-    List<DexType> types = new ArrayList<>(this.types);
-    types.sort(Comparator.comparing(DexType::toSourceString));
-    for (DexType type : types) {
-      String typeName = type.toSourceString();
-      DexClass dexClass = application.definitionFor(type);
-      if (dexClass == null) {
-        error("Could not find definition for type " + type.toSourceString());
-        continue;
+    errors = printer.print(application, types, methods, fields);
+  }
+
+  private abstract static class Printer {
+
+    void append(String string) {
+      System.out.print(string);
+    }
+
+    void appendLine(String string) {
+      System.out.println(string);
+    }
+
+    void printArguments(DexMethod method) {
+      append("(");
+      for (int i = 0; i < method.getArity(); i++) {
+        if (i != 0) {
+          append(",");
+        }
+        append(method.proto.parameters.values[i].toSourceString());
       }
-      out.println(typeName);
-      List<DexMethod> methods = new ArrayList<>(this.methods.get(type));
-      List<String> methodDefinitions = new ArrayList<>(methods.size());
-      for (DexMethod method : methods) {
-        DexEncodedMethod encodedMethod = dexClass.lookupMethod(method);
-        if (encodedMethod == null) {
-          error("Could not find definition for method " + method.toSourceString());
+      append(")");
+    }
+
+    abstract void printConstructorName(DexEncodedMethod encodedMethod);
+
+    void printError(String message) {
+      appendLine("# Error: " + message);
+    }
+
+    abstract void printField(DexClass dexClass, DexField field);
+
+    abstract void printMethod(DexEncodedMethod encodedMethod, String typeName);
+
+    void printNameAndReturn(DexEncodedMethod encodedMethod) {
+      if (encodedMethod.accessFlags.isConstructor()) {
+        printConstructorName(encodedMethod);
+      } else {
+        DexMethod method = encodedMethod.method;
+        append(method.proto.returnType.toSourceString());
+        append(" ");
+        append(method.name.toSourceString());
+      }
+    }
+
+    abstract void printTypeHeader(DexClass dexClass);
+
+    abstract void printTypeFooter();
+
+    int print(
+        DexApplication application,
+        Set<DexType> types,
+        Map<DexType, Set<DexMethod>> methods,
+        Map<DexType, Set<DexField>> fields) {
+      int errors = 0;
+      List<DexType> sortedTypes = new ArrayList<>(types);
+      sortedTypes.sort(Comparator.comparing(DexType::toSourceString));
+      for (DexType type : sortedTypes) {
+        DexClass dexClass = application.definitionFor(type);
+        if (dexClass == null) {
+          printError("Could not find definition for type " + type.toSourceString());
+          errors++;
           continue;
         }
-        methodDefinitions.add(getMethodSourceString(encodedMethod));
+        printTypeHeader(dexClass);
+        List<DexEncodedMethod> methodDefinitions = new ArrayList<>(methods.size());
+        for (DexMethod method : methods.get(type)) {
+          DexEncodedMethod encodedMethod = dexClass.lookupMethod(method);
+          if (encodedMethod == null) {
+            printError("Could not find definition for method " + method.toSourceString());
+            errors++;
+            continue;
+          }
+          methodDefinitions.add(encodedMethod);
+        }
+        methodDefinitions.sort(Comparator.comparing(x -> x.method.name.toSourceString()));
+        for (DexEncodedMethod encodedMethod : methodDefinitions) {
+          printMethod(encodedMethod, dexClass.type.toSourceString());
+        }
+        List<DexField> sortedFields = new ArrayList<>(fields.get(type));
+        sortedFields.sort(Comparator.comparing(DexField::toSourceString));
+        for (DexField field : sortedFields) {
+          printField(dexClass, field);
+        }
+        printTypeFooter();
       }
-      methodDefinitions.sort(Comparator.naturalOrder());
-      for (String encodedMethod : methodDefinitions) {
-        out.println(typeName + ": " + encodedMethod);
-      }
-      List<DexField> fields = new ArrayList<>(this.fields.get(type));
-      fields.sort(Comparator.comparing(DexField::toSourceString));
-      for (DexField field : fields) {
-        out.println(
-            typeName + ": " + field.type.toSourceString() + " " + field.name.toSourceString());
-      }
+      return errors;
     }
   }
 
-  private void error(String message) {
-    out.println("# Error: " + message);
-    errors += 1;
-  }
+  private static class DefaultPrinter extends Printer {
 
-  private static String getMethodSourceString(DexEncodedMethod encodedMethod) {
-    DexMethod method = encodedMethod.method;
-    StringBuilder builder = new StringBuilder();
-    if (encodedMethod.accessFlags.isConstructor()) {
+    @Override
+    public void printConstructorName(DexEncodedMethod encodedMethod) {
       if (encodedMethod.accessFlags.isStatic()) {
-        builder.append("<clinit>");
+        append("<clinit>");
       } else {
-        String holderName = method.holder.toSourceString();
+        String holderName = encodedMethod.method.holder.toSourceString();
         String constructorName = holderName.substring(holderName.lastIndexOf('.') + 1);
-        builder.append(constructorName);
+        append(constructorName);
       }
-    } else {
-      builder
-          .append(method.proto.returnType.toSourceString())
-          .append(" ")
-          .append(method.name.toSourceString());
     }
-    builder.append("(");
-    for (int i = 0; i < method.getArity(); i++) {
-      if (i != 0) {
-        builder.append(",");
+
+    @Override
+    void printMethod(DexEncodedMethod encodedMethod, String typeName) {
+      append(typeName + ": ");
+      printNameAndReturn(encodedMethod);
+      printArguments(encodedMethod.method);
+      appendLine("");
+    }
+
+    @Override
+    void printTypeHeader(DexClass dexClass) {
+      appendLine(dexClass.type.toSourceString());
+    }
+
+    @Override
+    void printTypeFooter() {}
+
+    @Override
+    void printField(DexClass dexClass, DexField field) {
+      appendLine(
+          dexClass.type.toSourceString()
+              + ": "
+              + field.type.toSourceString()
+              + " "
+              + field.name.toString());
+    }
+  }
+
+  private static class KeepPrinter extends Printer {
+
+    @Override
+    public void printTypeHeader(DexClass dexClass) {
+      if (dexClass.isInterface()) {
+        append("-keep interface " + dexClass.type.toSourceString() + " {\n");
+      } else if (dexClass.accessFlags.isEnum()) {
+        append("-keep enum " + dexClass.type.toSourceString() + " {\n");
+      } else {
+        append("-keep class " + dexClass.type.toSourceString() + " {\n");
       }
-      builder.append(method.proto.parameters.values[i].toSourceString());
     }
-    builder.append(")");
-    return builder.toString();
+
+    @Override
+    public void printConstructorName(DexEncodedMethod encodedMethod) {
+      append("<init>");
+    }
+
+    @Override
+    public void printField(DexClass dexClass, DexField field) {
+      append("  " + field.type.toSourceString() + " " + field.name.toString() + ";\n");
+    }
+
+    @Override
+    public void printMethod(DexEncodedMethod encodedMethod, String typeName) {
+      append("  ");
+      if (encodedMethod.isPublicMethod()) {
+        append("public ");
+      } else if (encodedMethod.isPrivateMethod()) {
+        append("private ");
+      }
+      if (encodedMethod.isStatic()) {
+        append("static ");
+      }
+      printNameAndReturn(encodedMethod);
+      printArguments(encodedMethod.method);
+      appendLine(";");
+    }
+
+    @Override
+    public void printTypeFooter() {
+      appendLine("}");
+    }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 05e7359..70c0f40 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -290,7 +290,9 @@
                 options.getProguardConfiguration().getDontWarnPatterns(),
                 executorService,
                 timing));
-        // assert rootSet.verifyKeptMethodsAreTargetedAndLive(appView.appInfo().withLiveness());
+        assert rootSet.verifyKeptFieldsAreAccessedAndLive(appView.appInfo().withLiveness());
+        assert rootSet.verifyKeptMethodsAreTargetedAndLive(appView.appInfo().withLiveness());
+        assert rootSet.verifyKeptTypesAreLive(appView.appInfo().withLiveness());
 
         if (options.getProguardConfiguration().isPrintSeeds()) {
           ByteArrayOutputStream bytes = new ByteArrayOutputStream();
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 6ee0f6e..0434f66 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -41,7 +41,6 @@
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.ir.desugar.CovariantReturnTypeAnnotationTransformer;
 import com.android.tools.r8.ir.desugar.InterfaceMethodRewriter;
-import com.android.tools.r8.ir.desugar.Java8MethodRewriter;
 import com.android.tools.r8.ir.desugar.LambdaRewriter;
 import com.android.tools.r8.ir.desugar.StringConcatRewriter;
 import com.android.tools.r8.ir.desugar.TwrCloseResourceRewriter;
@@ -120,7 +119,6 @@
   private final LambdaRewriter lambdaRewriter;
   private final InterfaceMethodRewriter interfaceMethodRewriter;
   private final TwrCloseResourceRewriter twrCloseResourceRewriter;
-  private final Java8MethodRewriter java8MethodRewriter;
   private final LambdaMerger lambdaMerger;
   private final ClassInliner classInliner;
   private final ClassStaticizer classStaticizer;
@@ -185,8 +183,6 @@
     this.twrCloseResourceRewriter =
         (options.enableDesugaring && enableTwrCloseResourceDesugaring())
             ? new TwrCloseResourceRewriter(this) : null;
-    this.java8MethodRewriter =
-        options.enableDesugaring ? new Java8MethodRewriter(this) : null;
     this.lambdaMerger =
         options.enableLambdaMerging ? new LambdaMerger(appInfo, options.reporter) : null;
     this.covariantReturnTypeAnnotationTransformer =
@@ -371,12 +367,6 @@
     }
   }
 
-  private void synthesizeJava8UtilityClass(Builder<?> builder) {
-    if (java8MethodRewriter != null) {
-      java8MethodRewriter.synthesizeUtilityClass(builder, options);
-    }
-  }
-
   private void processCovariantReturnTypeAnnotations(Builder<?> builder) {
     if (covariantReturnTypeAnnotationTransformer != null) {
       covariantReturnTypeAnnotationTransformer.process(builder);
@@ -397,7 +387,6 @@
     synthesizeLambdaClasses(builder, executor);
     desugarInterfaceMethods(builder, ExcludeDexResources, executor);
     synthesizeTwrCloseResourceUtilityClass(builder);
-    synthesizeJava8UtilityClass(builder);
     processCovariantReturnTypeAnnotations(builder);
 
     handleSynthesizedClassMapping(builder);
@@ -584,10 +573,11 @@
 
     printPhase("Interface method desugaring");
     desugarInterfaceMethods(builder, IncludeAllResources, executorService);
+
     printPhase("Twr close resource utility class synthesis");
     synthesizeTwrCloseResourceUtilityClass(builder);
-    synthesizeJava8UtilityClass(builder);
     handleSynthesizedClassMapping(builder);
+
     printPhase("Lambda merging finalization");
     finalizeLambdaMerging(application, feedback, builder, executorService);
 
@@ -1020,9 +1010,6 @@
     if (options.enableDesugaring && enableTryWithResourcesDesugaring()) {
       codeRewriter.rewriteThrowableAddAndGetSuppressed(code);
     }
-    if (java8MethodRewriter != null) {
-      java8MethodRewriter.desugar(code);
-    }
 
     stringConcatRewriter.desugarStringConcats(method.method, code);
 
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/Java8MethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/Java8MethodRewriter.java
deleted file mode 100644
index 3cb741a..0000000
--- a/src/main/java/com/android/tools/r8/ir/desugar/Java8MethodRewriter.java
+++ /dev/null
@@ -1,262 +0,0 @@
-// Copyright (c) 2018, 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.ir.desugar;
-
-import com.android.tools.r8.dex.Constants;
-import com.android.tools.r8.graph.ClassAccessFlags;
-import com.android.tools.r8.graph.DexAnnotationSet;
-import com.android.tools.r8.graph.DexApplication.Builder;
-import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexProto;
-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.MethodAccessFlags;
-import com.android.tools.r8.graph.ParameterAnnotationsList;
-import com.android.tools.r8.ir.code.IRCode;
-import com.android.tools.r8.ir.code.Instruction;
-import com.android.tools.r8.ir.code.InstructionIterator;
-import com.android.tools.r8.ir.code.InvokeStatic;
-import com.android.tools.r8.ir.conversion.IRConverter;
-import com.android.tools.r8.ir.desugar.Java8MethodRewriter.RewritableMethods.MethodGenerator;
-import com.android.tools.r8.ir.synthetic.TemplateMethodCode;
-import com.android.tools.r8.origin.SynthesizedOrigin;
-import com.android.tools.r8.utils.DescriptorUtils;
-import com.android.tools.r8.utils.InternalOptions;
-import com.google.common.collect.Sets;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.function.BiFunction;
-
-public final class Java8MethodRewriter {
-  private static final String UTILITY_CLASS_DESCRIPTOR_PREFIX = "L$r8$java8methods$utility";
-  private final Set<DexProgramClass> referencingClasses = Sets.newConcurrentHashSet();
-
-  private final IRConverter converter;
-  private final DexItemFactory factory;
-  private final RewritableMethods rewritableMethods;
-
-  private Map<DexMethod, MethodGenerator> methodGenerators = new ConcurrentHashMap<>();
-
-  public Java8MethodRewriter(IRConverter converter) {
-    this.converter = converter;
-    this.factory = converter.appInfo.dexItemFactory;
-    this.rewritableMethods = new RewritableMethods(factory);
-  }
-
-  public void desugar(IRCode code) {
-    InstructionIterator iterator = code.instructionIterator();
-    while (iterator.hasNext()) {
-      Instruction instruction = iterator.next();
-      if (!instruction.isInvokeStatic()) {
-        continue;
-      }
-      InvokeStatic invoke = instruction.asInvokeStatic();
-
-      MethodGenerator generator = getMethodGeneratorOrNull(converter, invoke.getInvokedMethod());
-      if (generator == null) {
-        continue;
-      }
-      iterator.replaceCurrentInstruction(
-            new InvokeStatic(generator.generateMethod(factory),
-                invoke.outValue(), invoke.inValues()));
-      methodGenerators.putIfAbsent(generator.generateMethod(factory), generator);
-      referencingClasses.add(
-          converter.appInfo.definitionFor(code.method.method.holder).asProgramClass());
-    }
-  }
-
-  public void synthesizeUtilityClass(Builder<?> builder, InternalOptions options) {
-    if (referencingClasses.isEmpty()) {
-      return;
-    }
-
-    MethodAccessFlags flags = MethodAccessFlags.fromSharedAccessFlags(
-        Constants.ACC_PUBLIC | Constants.ACC_STATIC | Constants.ACC_SYNTHETIC, false);
-    ClassAccessFlags classAccessFlags =
-        ClassAccessFlags.fromSharedAccessFlags(Constants.ACC_PUBLIC | Constants.ACC_SYNTHETIC);
-
-    for (MethodGenerator generator : methodGenerators.values()) {
-      DexMethod method = generator.generateMethod(factory);
-      TemplateMethodCode code = generator.generateTemplateMethod(options, method);
-      DexEncodedMethod dexEncodedMethod= new DexEncodedMethod(method,
-          flags, DexAnnotationSet.empty(), ParameterAnnotationsList.empty(), code);
-      DexProgramClass utilityClass =
-          new DexProgramClass(
-              method.holder,
-              null,
-              new SynthesizedOrigin("java8 methods utility class", getClass()),
-              classAccessFlags,
-              factory.objectType,
-              DexTypeList.empty(),
-              null,
-              null,
-              Collections.emptyList(),
-              DexAnnotationSet.empty(),
-              DexEncodedField.EMPTY_ARRAY,
-              DexEncodedField.EMPTY_ARRAY,
-              new DexEncodedMethod[]{dexEncodedMethod},
-              DexEncodedMethod.EMPTY_ARRAY,
-              factory.getSkipNameValidationForTesting(),
-              referencingClasses);
-      code.setUpContext(utilityClass);
-      boolean addToMainDexList = referencingClasses.stream()
-          .anyMatch(clazz -> converter.appInfo.isInMainDexList(clazz.type));
-      converter.optimizeSynthesizedClass(utilityClass);
-      builder.addSynthesizedClass(utilityClass, addToMainDexList);
-    }
-  }
-
-
-  private MethodGenerator getMethodGeneratorOrNull(IRConverter converter, DexMethod method) {
-    DexMethod original = converter.graphLense().getOriginalMethodSignature(method);
-    assert original != null;
-    return rewritableMethods.getGenerator(
-        original.holder.descriptor, original.name, original.proto);
-  }
-
-
-  private static final class IntegerMethods extends TemplateMethodCode {
-    IntegerMethods(InternalOptions options, DexMethod method, String methodName, String desc) {
-      super(options, method, methodName, desc);
-    }
-
-    public static IntegerMethods hashCodeCode(InternalOptions options, DexMethod method) {
-      return new IntegerMethods(options, method, "hashCodeImpl", "(I)I");
-    }
-
-    public static IntegerMethods maxCode(InternalOptions options, DexMethod method) {
-      return new IntegerMethods(options, method, "maxImpl", "(II)I");
-    }
-
-    public static int hashCodeImpl(int value) {
-      return Integer.valueOf(value).hashCode();
-    }
-
-     public static int maxImpl(int a, int b) {
-       return java.lang.Math.max(a, b);
-     }
-  }
-
-  private static final class DoubleMethods extends TemplateMethodCode {
-    DoubleMethods(InternalOptions options, DexMethod method, String methodName, String desc) {
-      super(options, method, methodName, desc);
-    }
-
-    public static DoubleMethods hashCodeCode(InternalOptions options, DexMethod method) {
-      return new DoubleMethods(options, method, "hashCodeImpl", "(D)I");
-    }
-
-    public static DoubleMethods maxCode(InternalOptions options, DexMethod method) {
-      return new DoubleMethods(options, method, "maxImpl", "(DD)D");
-    }
-
-    public static int hashCodeImpl(double value) {
-      return Double.valueOf(value).hashCode();
-    }
-
-     public static double maxImpl(double a, double b) {
-       return java.lang.Math.max(a, b);
-     }
-  }
-
-  public static final class RewritableMethods {
-    // Map class, method, proto to a generator for creating the code and method.
-    private final Map<DexString, Map<DexString, Map<DexProto, MethodGenerator>>> rewritable;
-
-
-    public RewritableMethods(DexItemFactory factory) {
-      rewritable = new HashMap<>();
-      // Integer
-      DexString clazz = factory.boxedIntDescriptor;
-      // int Integer.hashCode(int i)
-
-      DexString method = factory.createString("hashCode");
-      DexProto proto = factory.createProto(factory.intType, factory.intType);
-      addOrGetMethod(clazz, method)
-          .put(proto, new MethodGenerator(IntegerMethods::hashCodeCode, clazz, method, proto));
-
-      // int Integer.max(int a, int b)
-      method = factory.createString("max");
-      proto = factory.createProto(factory.intType, factory.intType, factory.intType);
-      addOrGetMethod(clazz, method)
-          .put(proto, new MethodGenerator(IntegerMethods::maxCode, clazz, method, proto));
-
-      // Double
-      clazz = factory.boxedDoubleDescriptor;
-      // int Double.hashCode(double d)
-      method = factory.createString("hashCode");
-      proto = factory.createProto(factory.intType, factory.doubleType);
-      addOrGetMethod(clazz, method)
-          .put(proto, new MethodGenerator(DoubleMethods::hashCodeCode, clazz, method, proto));
-
-      // double Double.max(double a, double b)
-      method = factory.createString("max");
-      proto = factory.createProto(factory.doubleType, factory.doubleType, factory.doubleType);
-      addOrGetMethod(clazz, method)
-          .put(proto, new MethodGenerator(DoubleMethods::maxCode, clazz, method, proto));
-
-    }
-
-    private Map<DexString, Map<DexProto, MethodGenerator>> addOrGetClass(DexString clazz) {
-      return rewritable.computeIfAbsent(clazz, k -> new HashMap<>());
-    }
-
-    private Map<DexProto, MethodGenerator> addOrGetMethod(
-        DexString clazz, DexString method) {
-      return addOrGetClass(clazz).computeIfAbsent(method, k -> new HashMap<>());
-    }
-
-    public MethodGenerator getGenerator(DexString clazz, DexString method, DexProto proto) {
-      Map<DexString, Map<DexProto, MethodGenerator>> classMap = rewritable.get(clazz);
-      if (classMap != null) {
-        Map<DexProto, MethodGenerator> methodMap = classMap.get(method);
-        if (methodMap != null) {
-          return methodMap.get(proto);
-        }
-      }
-      return null;
-    }
-
-    public static class MethodGenerator {
-      private final BiFunction<InternalOptions, DexMethod, TemplateMethodCode> generator;
-      private final DexString clazz;
-      private final DexString method;
-      private final DexProto proto;
-      private DexMethod dexMethod;
-
-      public MethodGenerator(
-          BiFunction<InternalOptions, DexMethod, TemplateMethodCode> generator,
-          DexString clazz, DexString method, DexProto proto) {
-        this.generator = generator;
-        this.clazz = clazz;
-        this.method = method;
-        this.proto = proto;
-      }
-
-      public DexMethod generateMethod(DexItemFactory factory) {
-        if (dexMethod != null) {
-          return dexMethod;
-        }
-        String clazzDescriptor = DescriptorUtils.getSimpleClassNameFromDescriptor(clazz.toString());
-        String postFix = "$" + clazzDescriptor + "$" + method + "$" + proto.shorty.toString();
-        DexType clazz = factory.createType(UTILITY_CLASS_DESCRIPTOR_PREFIX + postFix + ";");
-        dexMethod = factory.createMethod(clazz, proto, method);
-        return dexMethod;
-      }
-
-      public TemplateMethodCode generateTemplateMethod(InternalOptions options, DexMethod method) {
-        return generator.apply(options, method);
-      }
-    }
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index c579500..169f8a3 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -1153,9 +1153,9 @@
   private void markDirectStaticOrConstructorMethodAsLive(
       DexEncodedMethod encodedMethod, KeepReason reason) {
     assert encodedMethod != null;
+    markMethodAsTargeted(encodedMethod, reason);
     if (!liveMethods.contains(encodedMethod)) {
       markTypeAsLive(encodedMethod.method.holder);
-      markMethodAsTargeted(encodedMethod, reason);
       if (Log.ENABLED) {
         Log.verbose(getClass(), "Method `%s` has become live due to direct invoke",
             encodedMethod.method);
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
index 5bf6b4c..374553a 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -1021,6 +1021,24 @@
           .unmodifiableMap(dependentNoShrinking.getOrDefault(item, Collections.emptyMap()));
     }
 
+    public boolean verifyKeptFieldsAreAccessedAndLive(AppInfoWithLiveness appInfo) {
+      for (DexDefinition definition : noShrinking.keySet()) {
+        if (definition.isDexEncodedField()) {
+          DexEncodedField field = definition.asDexEncodedField();
+          if (field.isStatic() || isKeptDirectlyOrIndirectly(field.field.clazz, appInfo)) {
+            // TODO(b/121354886): Enable asserts for reads and writes.
+            /*assert appInfo.fieldsRead.contains(field.field)
+                : "Expected kept field `" + field.field.toSourceString() + "` to be read";
+            assert appInfo.fieldsWritten.contains(field.field)
+                : "Expected kept field `" + field.field.toSourceString() + "` to be written";*/
+            assert appInfo.liveFields.contains(field.field)
+                : "Expected kept field `" + field.field.toSourceString() + "` to be live";
+          }
+        }
+      }
+      return true;
+    }
+
     public boolean verifyKeptMethodsAreTargetedAndLive(AppInfoWithLiveness appInfo) {
       for (DexDefinition definition : noShrinking.keySet()) {
         if (definition.isDexEncodedMethod()) {
@@ -1040,6 +1058,17 @@
       return true;
     }
 
+    public boolean verifyKeptTypesAreLive(AppInfoWithLiveness appInfo) {
+      for (DexDefinition definition : noShrinking.keySet()) {
+        if (definition.isDexClass()) {
+          DexClass clazz = definition.asDexClass();
+          assert appInfo.liveTypes.contains(clazz.type)
+              : "Expected kept type `" + clazz.type.toSourceString() + "` to be live";
+        }
+      }
+      return true;
+    }
+
     private boolean isKeptDirectlyOrIndirectly(DexType type, AppInfoWithLiveness appInfo) {
       DexClass clazz = appInfo.definitionFor(type);
       if (clazz == null) {
diff --git a/src/test/java/com/android/tools/r8/Dummy.java b/src/test/java/com/android/tools/r8/Dummy.java
new file mode 100644
index 0000000..caf8bb9
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/Dummy.java
@@ -0,0 +1,23 @@
+// Copyright (c) 2018, 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;
+
+import org.junit.Test;
+
+public class Dummy extends TestBase {
+
+  @Test
+  public void testKeep() throws Exception {
+    PrintUses.main(
+        ToolHelper.getJava8RuntimeJar().toString(),
+        "build/libs/r8_without_deps.jar",
+        "build/libs/r8tests.jar");
+    PrintUses.main(
+        "--keeprules",
+        ToolHelper.getJava8RuntimeJar().toString(),
+        "build/libs/r8_without_deps.jar",
+        "build/libs/r8tests.jar");
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/Java8MethodsTest.java b/src/test/java/com/android/tools/r8/desugar/Java8MethodsTest.java
deleted file mode 100644
index c4632d7..0000000
--- a/src/test/java/com/android/tools/r8/desugar/Java8MethodsTest.java
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright (c) 2018, 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.desugar;
-
-import com.android.tools.r8.TestBase;
-import org.junit.Before;
-import org.junit.Test;
-
-public class Java8MethodsTest extends TestBase {
-  static String expectedOutput = "";
-
-  @Before
-  public void testJvm() throws Exception {
-    expectedOutput = testForJvm()
-        .addTestClasspath()
-        .run(Java8Methods.class).getStdOut();
-  }
-
-  @Test
-  public void testD8() throws Exception {
-    testForD8()
-        .addProgramClasses(Java8Methods.class)
-        .run(Java8Methods.class)
-        .assertSuccessWithOutput(expectedOutput);
-  }
-
-  static class Java8Methods {
-    public static void main(String[] args) {
-      System.out.println(Integer.hashCode(42));
-      System.out.println(Integer.max(42, 3));
-      System.out.println(Double.hashCode(42.0));
-      System.out.println(Double.max(42.0, 43.0));
-    }
-  }
-}