Merge "Do not output <init> for static constructors"
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/GenerateMainDexList.java b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
index 48c67fd..7b15c6e 100644
--- a/src/main/java/com/android/tools/r8/GenerateMainDexList.java
+++ b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
@@ -44,7 +44,8 @@
       DexApplication application =
           new ApplicationReader(app, options, timing).read(executor).toDirect();
       AppView<? extends AppInfoWithSubtyping> appView =
-          new AppView<>(new AppInfoWithSubtyping(application), GraphLense.getIdentityLense());
+          new AppView<>(
+              new AppInfoWithSubtyping(application), GraphLense.getIdentityLense(), options);
       RootSet mainDexRootSet =
           new RootSetBuilder(appView, application, options.mainDexKeepRules, options).run(executor);
 
diff --git a/src/main/java/com/android/tools/r8/PrintSeeds.java b/src/main/java/com/android/tools/r8/PrintSeeds.java
index 7bef553..475ca70 100644
--- a/src/main/java/com/android/tools/r8/PrintSeeds.java
+++ b/src/main/java/com/android/tools/r8/PrintSeeds.java
@@ -84,7 +84,8 @@
       DexApplication application =
           new ApplicationReader(command.getInputApp(), options, timing).read(executor).toDirect();
       AppView<? extends AppInfoWithSubtyping> appView =
-          new AppView<>(new AppInfoWithSubtyping(application), GraphLense.getIdentityLense());
+          new AppView<>(
+              new AppInfoWithSubtyping(application), GraphLense.getIdentityLense(), options);
       RootSet rootSet =
           new RootSetBuilder(
                   appView, application, options.getProguardConfiguration().getRules(), options)
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 5b94a6f..70c0f40 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -250,7 +250,8 @@
       inputApp.closeInternalArchiveProviders();
 
       AppView<AppInfoWithSubtyping> appView =
-          new AppView<>(new AppInfoWithSubtyping(application), GraphLense.getIdentityLense());
+          new AppView<>(
+              new AppInfoWithSubtyping(application), GraphLense.getIdentityLense(), options);
       RootSet rootSet;
       String proguardSeedsData = null;
       timing.begin("Strip unused code");
@@ -289,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/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index 2354918..f9a9dde 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -6,18 +6,21 @@
 
 import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.VerticalClassMerger.VerticallyMergedClasses;
+import com.android.tools.r8.utils.InternalOptions;
 
 public class AppView<T extends AppInfo> {
 
   private T appInfo;
   private final DexItemFactory dexItemFactory;
   private GraphLense graphLense;
+  private final InternalOptions options;
   private VerticallyMergedClasses verticallyMergedClasses;
 
-  public AppView(T appInfo, GraphLense graphLense) {
+  public AppView(T appInfo, GraphLense graphLense, InternalOptions options) {
     this.appInfo = appInfo;
     this.dexItemFactory = appInfo != null ? appInfo.dexItemFactory : null;
     this.graphLense = graphLense;
+    this.options = options;
   }
 
   public T appInfo() {
@@ -46,6 +49,10 @@
     this.graphLense = graphLense;
   }
 
+  public InternalOptions options() {
+    return options;
+  }
+
   // Get the result of vertical class merging. Returns null if vertical class merging has not been
   // run.
   public VerticallyMergedClasses verticallyMergedClasses() {
@@ -63,7 +70,7 @@
   private class AppViewWithLiveness extends AppView<AppInfoWithLiveness> {
 
     private AppViewWithLiveness() {
-      super(null, null);
+      super(null, null, null);
     }
 
     @Override
@@ -94,6 +101,11 @@
     }
 
     @Override
+    public InternalOptions options() {
+      return AppView.this.options();
+    }
+
+    @Override
     public AppView<AppInfoWithLiveness> withLiveness() {
       return this;
     }
diff --git a/src/main/java/com/android/tools/r8/graph/JarCode.java b/src/main/java/com/android/tools/r8/graph/JarCode.java
index 9d25704..04e6516 100644
--- a/src/main/java/com/android/tools/r8/graph/JarCode.java
+++ b/src/main/java/com/android/tools/r8/graph/JarCode.java
@@ -21,6 +21,7 @@
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.OffOrAuto;
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.util.Iterator;
@@ -226,12 +227,23 @@
 
   public ConstraintWithTarget computeInliningConstraint(
       DexEncodedMethod encodedMethod,
-      AppInfoWithLiveness appInfo,
+      AppView<? extends AppInfoWithLiveness> appView,
       GraphLense graphLense,
       DexType invocationContext) {
     InliningConstraintVisitor visitor =
         new InliningConstraintVisitor(
-            application, appInfo, graphLense, encodedMethod, invocationContext);
+            application, appView.appInfo(), graphLense, encodedMethod, invocationContext);
+
+    if (appView.options().enableDesugaring
+        && appView.options().interfaceMethodDesugaring == OffOrAuto.Auto
+        && !appView.options().canUseDefaultAndStaticInterfaceMethods()) {
+      // TODO(b/120130831): Conservatively need to say "no" at this point if there are invocations
+      // to static interface methods. This should be fixed by making sure that the desugared
+      // versions of default and static interface methods are present in the application during
+      // IR processing.
+      visitor.disallowStaticInterfaceMethodCalls();
+    }
+
     AbstractInsnNode insn = node.instructions.getFirst();
     while (insn != null) {
       insn.accept(visitor);
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/ir/optimize/InliningConstraints.java b/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
index 4e3f9be..3292704 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
@@ -24,6 +24,8 @@
 
   private AppInfoWithLiveness appInfo;
 
+  private boolean allowStaticInterfaceMethodCalls = true;
+
   // Currently used only by the vertical class merger (in all other cases this is the identity).
   //
   // When merging a type A into its subtype B we need to inline A.<init>() into B.<init>().
@@ -46,6 +48,10 @@
     this.graphLense = graphLense;
   }
 
+  public void disallowStaticInterfaceMethodCalls() {
+    allowStaticInterfaceMethodCalls = false;
+  }
+
   public ConstraintWithTarget forAlwaysMaterializingUser() {
     return ConstraintWithTarget.ALWAYS;
   }
@@ -280,6 +286,11 @@
       DexType methodHolder = graphLense.lookupType(target.method.holder);
       DexClass methodClass = appInfo.definitionFor(methodHolder);
       if (methodClass != null) {
+        if (!allowStaticInterfaceMethodCalls && methodClass.isInterface() && target.hasCode()) {
+          // See b/120121170.
+          return ConstraintWithTarget.NEVER;
+        }
+
         ConstraintWithTarget methodConstraintWithTarget =
             ConstraintWithTarget.deriveConstraint(
                 invocationContext, methodHolder, target.accessFlags, appInfo);
diff --git a/src/main/java/com/android/tools/r8/jar/InliningConstraintVisitor.java b/src/main/java/com/android/tools/r8/jar/InliningConstraintVisitor.java
index c707968..18998c8 100644
--- a/src/main/java/com/android/tools/r8/jar/InliningConstraintVisitor.java
+++ b/src/main/java/com/android/tools/r8/jar/InliningConstraintVisitor.java
@@ -14,6 +14,7 @@
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.GraphLense;
+import com.android.tools.r8.graph.GraphLense.GraphLenseLookupResult;
 import com.android.tools.r8.graph.JarApplicationReader;
 import com.android.tools.r8.ir.code.Invoke;
 import com.android.tools.r8.ir.conversion.JarSourceCode;
@@ -62,6 +63,10 @@
         ? inliningConstraints.forMonitor() : ConstraintWithTarget.ALWAYS;
   }
 
+  public void disallowStaticInterfaceMethodCalls() {
+    inliningConstraints.disallowStaticInterfaceMethodCalls();
+  }
+
   public ConstraintWithTarget getConstraint() {
     return constraint;
   }
@@ -160,12 +165,15 @@
         }
         break;
 
-      case Opcodes.INVOKESTATIC:
-        type = Invoke.Type.STATIC;
-        assert noNeedToUseGraphLense(target, type);
+      case Opcodes.INVOKESTATIC: {
+        // Static invokes may have changed as a result of horizontal class merging.
+        GraphLenseLookupResult lookup = graphLense.lookupMethod(target, null, Invoke.Type.STATIC);
+        target = lookup.getMethod();
+        type = lookup.getType();
         break;
+      }
 
-      case Opcodes.INVOKEVIRTUAL:
+      case Opcodes.INVOKEVIRTUAL: {
         type = Invoke.Type.VIRTUAL;
         // Instructions that target a private method in the same class translates to invoke-direct.
         if (target.holder == method.method.holder) {
@@ -174,8 +182,13 @@
             type = Invoke.Type.DIRECT;
           }
         }
-        assert noNeedToUseGraphLense(target, type);
+
+        // Virtual invokes may have changed to interface invokes as a result of member rebinding.
+        GraphLenseLookupResult lookup = graphLense.lookupMethod(target, null, type);
+        target = lookup.getMethod();
+        type = lookup.getType();
         break;
+      }
 
       default:
         throw new Unreachable("Unexpected opcode " + opcode);
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..7dde660 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) {
@@ -1090,11 +1119,21 @@
       for (DexProgramClass clazz : application.classes()) {
         Set<DexDefinition> requiredDefinitions =
             requiredDefinitionsPerType.getOrDefault(clazz.type, ImmutableSet.of());
+
+        Set<DexField> fields = null;
+        Set<DexMethod> methods = null;
+
         for (DexDefinition requiredDefinition : requiredDefinitions) {
           if (requiredDefinition.isDexEncodedField()) {
             DexEncodedField requiredField = requiredDefinition.asDexEncodedField();
-            assert Streams.stream(clazz.fields())
-                .anyMatch(field -> field.field == requiredField.field);
+            if (fields == null) {
+              // Create a Set of the fields to avoid quadratic behavior.
+              fields =
+                  Streams.stream(clazz.fields())
+                      .map(DexEncodedField::getKey)
+                      .collect(Collectors.toSet());
+            }
+            assert fields.contains(requiredField.field);
           } else if (requiredDefinition.isDexEncodedMethod()) {
             DexEncodedMethod requiredMethod = requiredDefinition.asDexEncodedMethod();
             if (isInterfaceMethodDesugaringEnabled) {
@@ -1104,8 +1143,14 @@
                 continue;
               }
             }
-            assert Streams.stream(clazz.methods())
-                .anyMatch(method -> method.method == requiredMethod.method);
+            if (methods == null) {
+              // Create a Set of the methods to avoid quadratic behavior.
+              methods =
+                  Streams.stream(clazz.methods())
+                      .map(DexEncodedMethod::getKey)
+                      .collect(Collectors.toSet());
+            }
+            assert methods.contains(requiredMethod.method);
           } else {
             assert false;
           }
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index 4abd3e3..0d69178 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -1619,7 +1619,7 @@
         ConstraintWithTarget constraint =
             jarCode.computeInliningConstraint(
                 method,
-                appInfo,
+                appView,
                 new SingleTypeMapperGraphLense(method.method.holder, invocationContext),
                 invocationContext);
         return constraint == ConstraintWithTarget.NEVER;
diff --git a/src/test/java/com/android/tools/r8/classmerging/ForceInliningWithStaticInterfaceMethodTest.java b/src/test/java/com/android/tools/r8/classmerging/ForceInliningWithStaticInterfaceMethodTest.java
new file mode 100644
index 0000000..7c86d1a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/ForceInliningWithStaticInterfaceMethodTest.java
@@ -0,0 +1,62 @@
+// 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.classmerging;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import org.junit.Test;
+
+/** Regression test for b/120121170. */
+public class ForceInliningWithStaticInterfaceMethodTest extends TestBase {
+
+  @Test
+  public void test() throws Exception {
+    String expectedOutput = StringUtils.lines("A.<init>()", "I.m()", "B.<init>()");
+    testForR8(Backend.DEX)
+        .addInnerClasses(ForceInliningWithStaticInterfaceMethodTest.class)
+        .addKeepMainRule(TestClass.class)
+        .setMinApi(AndroidApiLevel.M)
+        .compile()
+        .run(TestClass.class)
+        .assertSuccessWithOutput(expectedOutput);
+  }
+
+  static class TestClass {
+
+    public static void main(String[] args) {
+      new B();
+    }
+  }
+
+  static class A {
+
+    public A() {
+      System.out.println("A.<init>()");
+
+      // By the time the vertical class merger runs, I.m() still exists, so the inlining oracle
+      // concludes that A.<init>() is eligible for inlining. However, by the time A.<init>() will
+      // be force-inlined into B.<init>(), I.m() has been rewritten as a result of interface method
+      // desugaring, but the target method is not yet added to the application. Hence the inlining
+      // oracle concludes that A.<init>() is not eligible for inlining, which leads to an error
+      // "FORCE inlining on non-inlinable".
+      I.m();
+    }
+  }
+
+  static class B extends A {
+
+    public B() {
+      System.out.println("B.<init>()");
+    }
+  }
+
+  interface I {
+
+    static void m() {
+      System.out.println("I.m()");
+    }
+  }
+}
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));
-    }
-  }
-}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTestBase.java b/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTestBase.java
index 855e2ad..58c4237 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTestBase.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTestBase.java
@@ -32,7 +32,8 @@
     DexApplication dexApplication =
         new ApplicationReader(app, TEST_OPTIONS, timing).read().toDirect();
     AppView<AppInfoWithSubtyping> appView =
-        new AppView<>(new AppInfoWithSubtyping(dexApplication), GraphLense.getIdentityLense());
+        new AppView<>(
+            new AppInfoWithSubtyping(dexApplication), GraphLense.getIdentityLense(), TEST_OPTIONS);
     ExecutorService executorService = ThreadUtils.getExecutorService(TEST_OPTIONS);
     RootSet rootSet =
         new RootSetBuilder(
diff --git a/src/test/java/com/android/tools/r8/naming/NamingTestBase.java b/src/test/java/com/android/tools/r8/naming/NamingTestBase.java
index 301247d..4f4639a 100644
--- a/src/test/java/com/android/tools/r8/naming/NamingTestBase.java
+++ b/src/test/java/com/android/tools/r8/naming/NamingTestBase.java
@@ -44,7 +44,6 @@
 
   private DexApplication program;
   DexItemFactory dexItemFactory;
-  private AppView<AppInfoWithSubtyping> appView;
 
   NamingTestBase(
       String test,
@@ -61,7 +60,6 @@
   public void readApp() throws IOException, ExecutionException {
     program = ToolHelper.buildApplication(ImmutableList.of(appFileName));
     dexItemFactory = program.dexItemFactory;
-    appView = new AppView<>(new AppInfoWithSubtyping(program), GraphLense.getIdentityLense());
   }
 
   NamingLens runMinifier(List<Path> configPaths) throws ExecutionException {
@@ -71,6 +69,8 @@
 
     ExecutorService executor = ThreadUtils.getExecutorService(1);
 
+    AppView<AppInfoWithSubtyping> appView =
+        new AppView<>(new AppInfoWithSubtyping(program), GraphLense.getIdentityLense(), options);
     RootSet rootSet =
         new RootSetBuilder(appView, program, configuration.getRules(), options).run(executor);
 
diff --git a/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java b/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java
index a84f585..7ec1613 100644
--- a/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java
@@ -105,7 +105,8 @@
     AndroidApp app = readClassesAndAsmDump(CLASSES, ASM_CLASSES);
     DexApplication application = new ApplicationReader(app, options, timing).read().toDirect();
     AppView<? extends AppInfoWithSubtyping> appView =
-        new AppView<>(new AppInfoWithSubtyping(application), GraphLense.getIdentityLense());
+        new AppView<>(
+            new AppInfoWithSubtyping(application), GraphLense.getIdentityLense(), options);
 
     ExecutorService executor = Executors.newSingleThreadExecutor();
     RootSet rootSet =