Merge "Update patch version for release"
diff --git a/build.gradle b/build.gradle
index 2d9e9b4..8237781 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1339,10 +1339,6 @@
 }
 
 task buildPreNJdwpTestsDex(type: Exec, dependsOn: "buildPreNJdwpTestsJar") {
-    onlyIf {
-        // TODO(b/76135355): Update dx.bat on Windows to something that can build this.
-        !OperatingSystem.current().isWindows()
-    }
     def inFile = buildPreNJdwpTestsJar.archivePath
     def outFile = new File(buildPreNJdwpTestsJar.destinationDir, buildPreNJdwpTestsJar.baseName + '-dex.jar')
     inputs.file inFile
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java b/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
index e30d8ce..130362f 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
@@ -16,10 +16,13 @@
 
   private final int opcode;
   private final DexField field;
+  private final DexField declaringField;
 
-  public CfFieldInstruction(int opcode, DexField field) {
+  public CfFieldInstruction(int opcode, DexField field, DexField declaringField) {
     this.opcode = opcode;
     this.field = field;
+    this.declaringField = declaringField;
+    assert field.type == declaringField.type;
   }
 
   public DexField getField() {
@@ -33,7 +36,7 @@
   @Override
   public void write(MethodVisitor visitor, NamingLens lens) {
     String owner = lens.lookupInternalName(field.getHolder());
-    String name = lens.lookupName(field).toString();
+    String name = lens.lookupName(declaringField).toString();
     String desc = lens.lookupDescriptor(field.type).toString();
     visitor.visitFieldInsn(opcode, owner, name, desc);
   }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java b/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java
index ca8f7cb..a681943 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java
@@ -4,9 +4,11 @@
 package com.android.tools.r8.cf.code;
 
 import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.errors.Unimplemented;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.DexCallSite;
 import com.android.tools.r8.graph.DexMethodHandle;
+import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DexValue;
 import com.android.tools.r8.graph.DexValue.DexValueDouble;
@@ -41,11 +43,16 @@
       bsmArgs[i] = decodeBootstrapArgument(bootstrapArgs.get(i), lens);
     }
     Handle bsmHandle = bootstrapMethod.toAsmHandle(lens);
+    DexString methodName;
+    if (lens.isIdentityLens()) {
+      methodName = callSite.methodName;
+    } else if (callSite.interfaceMethod != null) {
+      methodName = lens.lookupName(callSite.interfaceMethod);
+    } else {
+      throw new Unimplemented("Minification of non-lambda InvokeDynamic not supported");
+    }
     visitor.visitInvokeDynamicInsn(
-        callSite.methodName.toString(),
-        callSite.methodProto.toDescriptorString(),
-        bsmHandle,
-        bsmArgs);
+        methodName.toString(), callSite.methodProto.toDescriptorString(lens), bsmHandle, bsmArgs);
   }
 
   private Object decodeBootstrapArgument(DexValue dexValue, NamingLens lens) {
@@ -60,9 +67,9 @@
     } else if (dexValue instanceof DexValueString) {
       return ((DexValueString) dexValue).getValue();
     } else if (dexValue instanceof DexValueType) {
-      return Type.getType(((DexValueType) dexValue).value.toDescriptorString());
+      return Type.getType(lens.lookupDescriptor(((DexValueType) dexValue).value).toString());
     } else if (dexValue instanceof DexValueMethodType) {
-      return Type.getMethodType(((DexValueMethodType) dexValue).value.toDescriptorString());
+      return Type.getMethodType(((DexValueMethodType) dexValue).value.toDescriptorString(lens));
     } else if (dexValue instanceof DexValueMethodHandle) {
       return ((DexValueMethodHandle) dexValue).value.toAsmHandle(lens);
     } else {
diff --git a/src/main/java/com/android/tools/r8/dexsplitter/DexSplitter.java b/src/main/java/com/android/tools/r8/dexsplitter/DexSplitter.java
index 45a62ef..e3753a7 100644
--- a/src/main/java/com/android/tools/r8/dexsplitter/DexSplitter.java
+++ b/src/main/java/com/android/tools/r8/dexsplitter/DexSplitter.java
@@ -14,6 +14,7 @@
 import com.android.tools.r8.utils.FeatureClassMapping.FeatureMappingException;
 import com.android.tools.r8.utils.OptionsParsing;
 import com.android.tools.r8.utils.OptionsParsing.ParseContext;
+import com.google.common.collect.ImmutableList;
 import java.io.IOException;
 import java.nio.file.Paths;
 import java.util.ArrayList;
@@ -27,40 +28,80 @@
   private static final boolean PRINT_ARGS = false;
 
   public static class Options {
-    List<String> inputArchives = new ArrayList<>();
-    List<String> featureJars = new ArrayList<>();
-    String splitBaseName = DEFAULT_OUTPUT_ARCHIVE_FILENAME;
-    String featureSplitMapping;
-    String proguardMap;
+    private List<String> inputArchives = new ArrayList<>();
+    private List<String> featureJars = new ArrayList<>();
+    private String splitBaseName = DEFAULT_OUTPUT_ARCHIVE_FILENAME;
+    private String featureSplitMapping;
+    private String proguardMap;
+
+    public String getSplitBaseName() {
+      return splitBaseName;
+    }
+
+    public void setSplitBaseName(String splitBaseName) {
+      this.splitBaseName = splitBaseName;
+    }
+
+    public String getFeatureSplitMapping() {
+      return featureSplitMapping;
+    }
+
+    public void setFeatureSplitMapping(String featureSplitMapping) {
+      this.featureSplitMapping = featureSplitMapping;
+    }
+
+    public String getProguardMap() {
+      return proguardMap;
+    }
+
+    public void setProguardMap(String proguardMap) {
+      this.proguardMap = proguardMap;
+    }
+
+    public void addInputArchive(String inputArchive) {
+      inputArchives.add(inputArchive);
+    }
+
+    public void addFeatureJar(String featureJar) {
+      featureJars.add(featureJar);
+    }
+
+    public ImmutableList<String> getInputArchives() {
+      return ImmutableList.copyOf(inputArchives);
+    }
+
+    public ImmutableList<String> getFeatureJars() {
+      return ImmutableList.copyOf(featureJars);
+    }
   }
 
   private static Options parseArguments(String[] args) throws IOException {
     Options options = new Options();
     ParseContext context = new ParseContext(args);
     while (context.head() != null) {
-      List<String> input = OptionsParsing.tryParseMulti(context, "--input");
-      if (input != null) {
-        options.inputArchives.addAll(input);
+      List<String> inputs = OptionsParsing.tryParseMulti(context, "--input");
+      if (inputs != null) {
+        inputs.stream().forEach(options::addInputArchive);
         continue;
       }
       List<String> featureJars = OptionsParsing.tryParseMulti(context, "--feature-jar");
       if (featureJars != null) {
-        options.featureJars.addAll(featureJars);
+        featureJars.stream().forEach(options::addFeatureJar);
         continue;
       }
       String output = OptionsParsing.tryParseSingle(context, "--output", "-o");
       if (output != null) {
-        options.splitBaseName = output;
+        options.setSplitBaseName(output);
         continue;
       }
       String proguardMap = OptionsParsing.tryParseSingle(context, "--proguard-map", null);
       if (proguardMap != null) {
-        options.proguardMap = proguardMap;
+        options.setProguardMap(proguardMap);
         continue;
       }
       String featureSplit = OptionsParsing.tryParseSingle(context, "--feature-splits", null);
       if (featureSplit != null) {
-        options.featureSplitMapping = featureSplit;
+        options.setFeatureSplitMapping(featureSplit);
         continue;
       }
       throw new RuntimeException(String.format("Unknown options: '%s'.", context.head()));
@@ -70,8 +111,8 @@
 
   private static FeatureClassMapping createFeatureClassMapping(Options options)
       throws IOException, FeatureMappingException, ResourceException {
-    if (options.featureSplitMapping != null) {
-      return FeatureClassMapping.fromSpecification(Paths.get(options.featureSplitMapping));
+    if (options.getFeatureSplitMapping() != null) {
+      return FeatureClassMapping.fromSpecification(Paths.get(options.getFeatureSplitMapping()));
     }
     assert !options.featureJars.isEmpty();
     return FeatureClassMapping.fromJarFiles(options.featureJars);
@@ -87,13 +128,13 @@
   public static void run(Options options)
       throws IOException, FeatureMappingException, ResourceException, CompilationException,
       ExecutionException, CompilationFailedException {
-    if (options.inputArchives.isEmpty()) {
+    if (options.getInputArchives().isEmpty()) {
       throw new RuntimeException("Need at least one --input");
     }
-    if (options.featureSplitMapping == null && options.featureJars.isEmpty()) {
+    if (options.getFeatureSplitMapping() == null && options.getFeatureJars().isEmpty()) {
       throw new RuntimeException("You must supply a feature split mapping or feature jars");
     }
-    if (options.featureSplitMapping != null && !options.featureJars.isEmpty()) {
+    if (options.getFeatureSplitMapping() != null && !options.getFeatureJars().isEmpty()) {
       throw new RuntimeException("You can't supply both a feature split mapping and feature jars");
     }
 
@@ -108,7 +149,7 @@
     FeatureClassMapping featureClassMapping = createFeatureClassMapping(options);
 
     DexSplitterHelper.run(
-        builder.build(), featureClassMapping, options.splitBaseName, options.proguardMap);
+        builder.build(), featureClassMapping, options.getSplitBaseName(), options.getProguardMap());
   }
 
   public static void main(String[] args) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexCallSite.java b/src/main/java/com/android/tools/r8/graph/DexCallSite.java
index 0c32750..9e22713 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCallSite.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCallSite.java
@@ -29,10 +29,12 @@
   public final DexMethodHandle bootstrapMethod;
   public final List<DexValue> bootstrapArgs;
 
+  public final DexMethod interfaceMethod;
+
   private DexEncodedArray encodedArray = null;
 
   DexCallSite(DexString methodName, DexProto methodProto,
-      DexMethodHandle bootstrapMethod, List<DexValue> bootstrapArgs) {
+      DexMethodHandle bootstrapMethod, List<DexValue> bootstrapArgs, DexMethod interfaceMethod) {
     assert methodName != null;
     assert methodProto != null;
     assert bootstrapMethod != null;
@@ -42,6 +44,7 @@
     this.methodProto = methodProto;
     this.bootstrapMethod = bootstrapMethod;
     this.bootstrapArgs = bootstrapArgs;
+    this.interfaceMethod = interfaceMethod;
   }
 
   public static DexCallSite fromAsmInvokeDynamic(
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 9d3c7a9..75f2ee8 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -15,6 +15,7 @@
 import com.android.tools.r8.graph.DexDebugEvent.SetInlineFrame;
 import com.android.tools.r8.graph.DexDebugEvent.SetPrologueEnd;
 import com.android.tools.r8.graph.DexMethodHandle.MethodHandleType;
+import com.android.tools.r8.graph.DexValue.DexValueMethodType;
 import com.android.tools.r8.ir.code.Position;
 import com.android.tools.r8.kotlin.Kotlin;
 import com.android.tools.r8.naming.NamingLens;
@@ -544,7 +545,20 @@
       DexString methodName, DexProto methodProto,
       DexMethodHandle bootstrapMethod, List<DexValue> bootstrapArgs) {
     assert !sorted;
-    DexCallSite callSite = new DexCallSite(methodName, methodProto, bootstrapMethod, bootstrapArgs);
+    String bootstrapClass = bootstrapMethod.asMethod().holder.toDescriptorString();
+    DexMethod interfaceMethod = null;
+    if (bootstrapClass.equals("Ljava/lang/invoke/LambdaMetafactory;")) {
+      if (methodName.toString().equals("metafactory")) {
+        DexType interfaceType = methodProto.returnType;
+        assert bootstrapArgs.size() == 3;
+        // bootstrapArgs contains samMethodType, implMethod and instantiatedMethodType.
+        DexValueMethodType samMethodType = (DexValueMethodType) bootstrapArgs.get(0);
+        interfaceMethod = createMethod(interfaceType, samMethodType.value, methodName);
+      }
+      // TODO(mathiasr): Support altMetafactory, possibly using ir.desugar.LambdaDescriptor
+    }
+    DexCallSite callSite =
+        new DexCallSite(methodName, methodProto, bootstrapMethod, bootstrapArgs, interfaceMethod);
     return canonicalize(callSites, callSite);
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
index a7bce9d..660a713 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
@@ -153,7 +153,7 @@
 
   @Override
   public void buildCf(CfBuilder builder) {
-    builder.add(new CfFieldInstruction(Opcodes.GETFIELD, field));
+    builder.add(new CfFieldInstruction(Opcodes.GETFIELD, field, builder.resolveField(field)));
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
index ec92b67..9591978 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
@@ -136,7 +136,7 @@
 
   @Override
   public void buildCf(CfBuilder builder) {
-    builder.add(new CfFieldInstruction(Opcodes.PUTFIELD, field));
+    builder.add(new CfFieldInstruction(Opcodes.PUTFIELD, field, builder.resolveField(field)));
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
index 47141bc..6db3b8d 100644
--- a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
@@ -131,7 +131,7 @@
 
   @Override
   public void buildCf(CfBuilder builder) {
-    builder.add(new CfFieldInstruction(Opcodes.GETSTATIC, field));
+    builder.add(new CfFieldInstruction(Opcodes.GETSTATIC, field, builder.resolveField(field)));
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
index 0eca18d..0c78208 100644
--- a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
@@ -130,7 +130,7 @@
 
   @Override
   public void buildCf(CfBuilder builder) {
-    builder.add(new CfFieldInstruction(Opcodes.PUTSTATIC, field));
+    builder.add(new CfFieldInstruction(Opcodes.PUTSTATIC, field, builder.resolveField(field)));
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
index 933f66d..7c04392 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
@@ -16,7 +16,9 @@
 import com.android.tools.r8.graph.CfCode;
 import com.android.tools.r8.graph.CfCode.LocalVariableInfo;
 import com.android.tools.r8.graph.DebugLocalInfo;
+import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.code.Argument;
@@ -73,6 +75,8 @@
   private final Int2ReferenceMap<LocalVariableInfo> openLocalVariables =
       new Int2ReferenceOpenHashMap<>();
 
+  private AppInfoWithSubtyping appInfo;
+
   // Internal abstraction of the stack values and height.
   private static class Stack {
     int maxHeight = 0;
@@ -119,10 +123,16 @@
     int instructionTableCount =
         DexBuilder.instructionNumberToIndex(code.numberRemainingInstructions());
     DexBuilder.removeRedundantDebugPositions(code, instructionTableCount);
+    this.appInfo = appInfo;
     CfCode code = buildCfCode();
     return code;
   }
 
+  public DexField resolveField(DexField field) {
+    DexEncodedField resolvedField = appInfo.resolveFieldOn(field.clazz, field);
+    return resolvedField == null ? field : resolvedField.field;
+  }
+
   // Split all blocks with throwing instructions and exceptional edges such that any non-throwing
   // instructions that might define values prior to the throwing exception are excluded from the
   // try-catch range. Failure to do so will result in code that does not verify on the JVM.
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
index c752fb5..2152364 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
@@ -32,8 +32,6 @@
   private final Set<DexClass> processedClasses = Sets.newIdentityHashSet();
   // Maps already created methods into default methods they were generated based on.
   private final Map<DexEncodedMethod, DexEncodedMethod> createdMethods = new IdentityHashMap<>();
-  // Caches default interface method info for already processed interfaces.
-  private final Map<DexType, DefaultMethodsHelper.Collection> cache = new IdentityHashMap<>();
 
   ClassProcessor(InterfaceMethodRewriter rewriter) {
     this.rewriter = rewriter;
@@ -67,7 +65,7 @@
     if (superClass != null && superType != rewriter.factory.objectType) {
       if (superClass.isInterface()) {
         throw new CompilationError("Interface `" + superClass.toSourceString()
-        + "` used as super class of `" + clazz.toSourceString() + "`.");
+            + "` used as super class of `" + clazz.toSourceString() + "`.");
       }
       process(superClass);
     }
@@ -136,7 +134,7 @@
     // the future.
     while (current.type != rewriter.factory.objectType) {
       for (DexType type : current.interfaces.values) {
-        helper.merge(getOrCreateInterfaceInfo(clazz, current, type));
+        helper.merge(rewriter.getOrCreateInterfaceInfo(clazz, current, type));
       }
 
       accumulatedVirtualMethods.addAll(Arrays.asList(clazz.virtualMethods()));
@@ -168,8 +166,8 @@
         } else {
           String message = "Default method desugaring of `" + clazz.toSourceString() + "` failed";
           if (current == clazz) {
-            message += " because its super class `" + clazz.superType.toSourceString()
-            + "` is missing";
+            message += " because its super class `" +
+                clazz.superType.toSourceString() + "` is missing";
           } else {
             message +=
                 " because it's hierarchy is incomplete. The class `"
@@ -244,62 +242,4 @@
       }
     }
   }
-
-  private DefaultMethodsHelper.Collection getOrCreateInterfaceInfo(
-      DexClass classToDesugar,
-      DexClass implementing,
-      DexType iface) {
-    DefaultMethodsHelper.Collection collection = cache.get(iface);
-    if (collection != null) {
-      return collection;
-    }
-    collection = createInterfaceInfo(classToDesugar, implementing, iface);
-    cache.put(iface, collection);
-    return collection;
-  }
-
-  private DefaultMethodsHelper.Collection createInterfaceInfo(
-      DexClass classToDesugar,
-      DexClass implementing,
-      DexType iface) {
-    DefaultMethodsHelper helper = new DefaultMethodsHelper();
-    DexClass definedInterface = rewriter.findDefinitionFor(iface);
-    if (definedInterface == null) {
-      rewriter.warnMissingInterface(classToDesugar, implementing, iface);
-      return helper.wrapInCollection();
-    }
-
-    if (!definedInterface.isInterface()) {
-      throw new CompilationError(
-          "Type " + iface.toSourceString() + " is referenced as an interface of `"
-          + implementing.toString() + "`.");
-    }
-
-    if (definedInterface.isLibraryClass()) {
-      // NOTE: We intentionally ignore all candidates coming from android.jar
-      // since it is only possible in case v24+ version of android.jar is provided.
-      // WARNING: This may result in incorrect code if something else than Android bootclasspath
-      // classes are given as libraries!
-      return helper.wrapInCollection();
-    }
-
-    // Merge information from all superinterfaces.
-    for (DexType superinterface : definedInterface.interfaces.values) {
-      helper.merge(getOrCreateInterfaceInfo(classToDesugar, definedInterface, superinterface));
-    }
-
-    // Hide by virtual methods of this interface.
-    for (DexEncodedMethod virtual : definedInterface.virtualMethods()) {
-      helper.hideMatches(virtual.method);
-    }
-
-    // Add all default methods of this interface.
-    for (DexEncodedMethod encoded : definedInterface.virtualMethods()) {
-      if (rewriter.isDefaultMethod(encoded)) {
-        helper.addDefaultMethod(encoded);
-      }
-    }
-
-    return helper.wrapInCollection();
-  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DefaultMethodsHelper.java b/src/main/java/com/android/tools/r8/ir/desugar/DefaultMethodsHelper.java
index df65e7e..fb051aa 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DefaultMethodsHelper.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DefaultMethodsHelper.java
@@ -40,6 +40,22 @@
       this.live = live;
       this.hidden = hidden;
     }
+
+    // If there is just one live method having specified
+    // signature return it, otherwise return null.
+    DexMethod getSingleCandidate(DexMethod method) {
+      DexMethod candidate = null;
+      for (DexEncodedMethod encodedMethod : live) {
+        DexMethod current = encodedMethod.method;
+        if (current.proto == method.proto && current.name == method.name) {
+          if (candidate != null) {
+            return null;
+          }
+          candidate = current;
+        }
+      }
+      return candidate;
+    }
   }
 
   final void merge(Collection collection) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
index 9379a83..9b8b8e2 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
@@ -26,6 +26,7 @@
 import com.android.tools.r8.ir.code.InvokeStatic;
 import com.android.tools.r8.ir.code.InvokeSuper;
 import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.ir.desugar.DefaultMethodsHelper.Collection;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.StringDiagnostic;
@@ -33,6 +34,7 @@
 import java.util.ListIterator;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
 
 //
 // Default and static interface method desugaring rewriter (note that lambda
@@ -75,6 +77,9 @@
   // to this collection since it is only filled in ClassProcessor running synchronously.
   private final Set<DexEncodedMethod> forwardingMethods = Sets.newIdentityHashSet();
 
+  // Caches default interface method info for already processed interfaces.
+  private final Map<DexType, DefaultMethodsHelper.Collection> cache = new ConcurrentHashMap<>();
+
   /**
    * A set of dexitems we have reported missing to dedupe warnings.
    */
@@ -166,8 +171,10 @@
             // of android.jar is provided.
             // WARNING: This may result in incorrect code on older platforms!
             // Retarget call to an appropriate method of companion class.
+            DexMethod amendedMethod = amendDefaultMethod(
+                findDefinitionFor(encodedMethod.method.holder), method);
             instructions.replaceCurrentInstruction(
-                new InvokeStatic(defaultAsMethodOfCompanionClass(method),
+                new InvokeStatic(defaultAsMethodOfCompanionClass(amendedMethod),
                     invokeSuper.outValue(), invokeSuper.arguments()));
           }
           continue;
@@ -294,6 +301,15 @@
         factory.createString(prefix + method.name.toString()));
   }
 
+  // It is possible that referenced method actually points to an interface which does
+  // not define this default methods, but inherits it. We are making our best effort
+  // to find an appropriate method, but still use the original one in case we fail.
+  private DexMethod amendDefaultMethod(DexClass classToDesugar, DexMethod method) {
+    DexMethod singleCandidate = getOrCreateInterfaceInfo(
+        classToDesugar, classToDesugar, method.holder).getSingleCandidate(method);
+    return singleCandidate != null ? singleCandidate : method;
+  }
+
   // Represent a default interface method as a method of companion class.
   final DexMethod defaultAsMethodOfCompanionClass(DexMethod method) {
     return instanceAsMethodOfCompanionClass(method, DEFAULT_METHOD_PREFIX);
@@ -329,6 +345,14 @@
     for (DexEncodedMethod method : forwardingMethods) {
       converter.optimizeSynthesizedMethod(method);
     }
+
+    // Cached data is not needed any more.
+    clear();
+  }
+
+  private void clear() {
+    this.cache.clear();
+    this.forwardingMethods.clear();
   }
 
   private static boolean shouldProcess(
@@ -426,4 +450,62 @@
     DexClass clazz = converter.appInfo.definitionFor(holder);
     return clazz == null ? Origin.unknown() : clazz.getOrigin();
   }
+
+  final DefaultMethodsHelper.Collection getOrCreateInterfaceInfo(
+      DexClass classToDesugar,
+      DexClass implementing,
+      DexType iface) {
+    DefaultMethodsHelper.Collection collection = cache.get(iface);
+    if (collection != null) {
+      return collection;
+    }
+    collection = createInterfaceInfo(classToDesugar, implementing, iface);
+    Collection existing = cache.putIfAbsent(iface, collection);
+    return existing != null ? existing : collection;
+  }
+
+  private DefaultMethodsHelper.Collection createInterfaceInfo(
+      DexClass classToDesugar,
+      DexClass implementing,
+      DexType iface) {
+    DefaultMethodsHelper helper = new DefaultMethodsHelper();
+    DexClass definedInterface = findDefinitionFor(iface);
+    if (definedInterface == null) {
+      warnMissingInterface(classToDesugar, implementing, iface);
+      return helper.wrapInCollection();
+    }
+
+    if (!definedInterface.isInterface()) {
+      throw new CompilationError(
+          "Type " + iface.toSourceString() + " is referenced as an interface from `"
+              + implementing.toString() + "`.");
+    }
+
+    if (definedInterface.isLibraryClass()) {
+      // NOTE: We intentionally ignore all candidates coming from android.jar
+      // since it is only possible in case v24+ version of android.jar is provided.
+      // WARNING: This may result in incorrect code if something else than Android bootclasspath
+      // classes are given as libraries!
+      return helper.wrapInCollection();
+    }
+
+    // Merge information from all superinterfaces.
+    for (DexType superinterface : definedInterface.interfaces.values) {
+      helper.merge(getOrCreateInterfaceInfo(classToDesugar, definedInterface, superinterface));
+    }
+
+    // Hide by virtual methods of this interface.
+    for (DexEncodedMethod virtual : definedInterface.virtualMethods()) {
+      helper.hideMatches(virtual.method);
+    }
+
+    // Add all default methods of this interface.
+    for (DexEncodedMethod encoded : definedInterface.virtualMethods()) {
+      if (isDefaultMethod(encoded)) {
+        helper.addDefaultMethod(encoded);
+      }
+    }
+
+    return helper.wrapInCollection();
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
index c8be00e..a7b3f77 100644
--- a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
@@ -276,7 +276,7 @@
       }
       AnnotationVisitor v =
           visitor.visit(
-              dexAnnotation.annotation.type.toDescriptorString(),
+              namingLens.lookupDescriptor(dexAnnotation.annotation.type).toString(),
               dexAnnotation.visibility == DexAnnotation.VISIBILITY_RUNTIME);
       if (v != null) {
         writeAnnotation(v, dexAnnotation.annotation);
@@ -295,7 +295,8 @@
     if (value instanceof DexValueAnnotation) {
       DexValueAnnotation valueAnnotation = (DexValueAnnotation) value;
       AnnotationVisitor innerVisitor =
-          visitor.visitAnnotation(name, valueAnnotation.value.type.toDescriptorString());
+          visitor.visitAnnotation(
+              name, namingLens.lookupDescriptor(valueAnnotation.value.type).toString());
       if (innerVisitor != null) {
         writeAnnotation(innerVisitor, valueAnnotation.value);
         innerVisitor.visitEnd();
@@ -311,7 +312,8 @@
       }
     } else if (value instanceof DexValueEnum) {
       DexValueEnum en = (DexValueEnum) value;
-      visitor.visitEnum(name, en.value.type.toDescriptorString(), en.value.name.toString());
+      visitor.visitEnum(
+          name, namingLens.lookupDescriptor(en.value.type).toString(), en.value.name.toString());
     } else if (value instanceof DexValueField) {
       throw new Unreachable("writeAnnotationElement of DexValueField");
     } else if (value instanceof DexValueMethod) {
@@ -325,7 +327,7 @@
       visitor.visit(name, str.getValue().toString());
     } else if (value instanceof DexValueType) {
       DexValueType ty = (DexValueType) value;
-      visitor.visit(name, Type.getType(ty.value.toDescriptorString()));
+      visitor.visit(name, Type.getType(namingLens.lookupDescriptor(ty.value).toString()));
     } else if (value instanceof UnknownDexValue) {
       throw new Unreachable("writeAnnotationElement of UnknownDexValue");
     } else {
diff --git a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
index ce37763..25a84b6 100644
--- a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
@@ -12,7 +12,6 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
 import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.MethodJavaSignatureEquivalence;
 import com.android.tools.r8.utils.MethodSignatureEquivalence;
 import com.android.tools.r8.utils.Timing;
 import com.google.common.base.Equivalence;
@@ -88,13 +87,10 @@
  */
 class MethodNameMinifier extends MemberNameMinifier<DexMethod, DexProto> {
 
-  private final Equivalence<DexMethod> equivalence;
+  private final Equivalence<DexMethod> equivalence = MethodSignatureEquivalence.get();
 
   MethodNameMinifier(AppInfoWithSubtyping appInfo, RootSet rootSet, InternalOptions options) {
     super(appInfo, rootSet, options);
-    equivalence = overloadAggressively
-        ? MethodSignatureEquivalence.get()
-        : MethodJavaSignatureEquivalence.get();
   }
 
   @Override
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 0b43509..a726391 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -712,12 +712,12 @@
     assert clazz.accessFlags.isInterface();
     SetWithReason<DexEncodedMethod> reachableMethods = reachableVirtualMethods.get(iface);
     if (reachableMethods != null) {
-      seen = seen.newNestedScope();
-      transitionNonAbstractMethodsToLiveAndShadow(reachableMethods.getItems(), instantiatedType,
-          seen);
-      for (DexType subInterface : clazz.interfaces.values) {
-        transitionDefaultMethodsForInstantiatedClass(subInterface, instantiatedType, seen);
-      }
+      transitionNonAbstractMethodsToLiveAndShadow(
+          reachableMethods.getItems(), instantiatedType, seen.newNestedScope());
+    }
+    seen = seen.newNestedScope();
+    for (DexType subInterface : clazz.interfaces.values) {
+      transitionDefaultMethodsForInstantiatedClass(subInterface, instantiatedType, seen);
     }
   }
 
diff --git a/src/test/examples/regress_76025099/Logger.java b/src/test/examples/regress_76025099/Logger.java
new file mode 100644
index 0000000..d6e9f95
--- /dev/null
+++ b/src/test/examples/regress_76025099/Logger.java
@@ -0,0 +1,8 @@
+// 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 regress_76025099;
+
+public interface Logger {
+  String getName();
+}
diff --git a/src/test/examples/regress_76025099/Main.java b/src/test/examples/regress_76025099/Main.java
new file mode 100644
index 0000000..659bdbd
--- /dev/null
+++ b/src/test/examples/regress_76025099/Main.java
@@ -0,0 +1,13 @@
+// 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 regress_76025099;
+
+import regress_76025099.impl.Factory;
+
+public class Main {
+  public static void main(String[] args) {
+    Logger l = Factory.getImpl(Main.class.getCanonicalName());
+    System.out.println(l.getName());
+  }
+}
diff --git a/src/test/examples/regress_76025099/helper/AbstractBase.java b/src/test/examples/regress_76025099/helper/AbstractBase.java
new file mode 100644
index 0000000..046e12f
--- /dev/null
+++ b/src/test/examples/regress_76025099/helper/AbstractBase.java
@@ -0,0 +1,15 @@
+// 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 regress_76025099.helper;
+
+import regress_76025099.Logger;
+
+abstract class AbstractBase implements Logger {
+  protected String name;
+
+  @Override
+  public String getName() {
+    return name;
+  }
+}
diff --git a/src/test/examples/regress_76025099/helper/AbstractSub.java b/src/test/examples/regress_76025099/helper/AbstractSub.java
new file mode 100644
index 0000000..9240e4a
--- /dev/null
+++ b/src/test/examples/regress_76025099/helper/AbstractSub.java
@@ -0,0 +1,7 @@
+// 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 regress_76025099.helper;
+
+public abstract class AbstractSub extends AbstractBase {
+}
diff --git a/src/test/examples/regress_76025099/impl/Factory.java b/src/test/examples/regress_76025099/impl/Factory.java
new file mode 100644
index 0000000..0cc1cc9
--- /dev/null
+++ b/src/test/examples/regress_76025099/impl/Factory.java
@@ -0,0 +1,10 @@
+// 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 regress_76025099.impl;
+
+public class Factory {
+  public static Impl getImpl(String name) {
+    return new Impl(name);
+  }
+}
diff --git a/src/test/examples/regress_76025099/impl/Impl.java b/src/test/examples/regress_76025099/impl/Impl.java
new file mode 100644
index 0000000..74eefcf
--- /dev/null
+++ b/src/test/examples/regress_76025099/impl/Impl.java
@@ -0,0 +1,12 @@
+// 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 regress_76025099.impl;
+
+import regress_76025099.helper.AbstractSub;
+
+public class Impl extends AbstractSub {
+  Impl(String name) {
+    this.name = name;
+  }
+}
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/cf/BaseDefaultMethodTest.java b/src/test/java/com/android/tools/r8/cf/BaseDefaultMethodTest.java
new file mode 100644
index 0000000..a0d6122
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/BaseDefaultMethodTest.java
@@ -0,0 +1,32 @@
+// 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.cf;
+
+public class BaseDefaultMethodTest {
+
+  public static final Class[] CLASSES = {
+      BaseDefaultMethodTest.class,
+      Base.class,
+      Derived.class,
+      Impl.class,
+  };
+
+  interface Base {
+    default void bar() {}
+  }
+
+  interface Derived extends Base {}
+
+  static class Impl implements Derived {}
+
+  static Derived foo() {
+    return new Impl();
+  }
+
+  public static void main(String[] args) {
+    Derived d = foo();
+    d.bar();
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/cf/BaseDefaultMethodTestRunner.java b/src/test/java/com/android/tools/r8/cf/BaseDefaultMethodTestRunner.java
new file mode 100644
index 0000000..414752f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/BaseDefaultMethodTestRunner.java
@@ -0,0 +1,54 @@
+// 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.cf;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.ClassFileConsumer.ArchiveConsumer;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.R8Command.Builder;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.origin.Origin;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.List;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+public class BaseDefaultMethodTestRunner {
+  static final Class CLASS = BaseDefaultMethodTest.class;
+  static final Class[] CLASSES = BaseDefaultMethodTest.CLASSES;
+
+  @Rule
+  public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+
+  @Test
+  public void test() throws Exception {
+    ProcessResult runInput =
+        ToolHelper.runJava(ToolHelper.getClassPathForTests(), CLASS.getCanonicalName());
+    assertEquals(0, runInput.exitCode);
+    List<String> config =
+        Arrays.asList(
+            "-keep public class " + CLASS.getCanonicalName() + " {",
+            "  public static void main(...);",
+            "}");
+    Path out = temp.getRoot().toPath().resolve("out.jar");
+    Builder builder =
+        R8Command.builder()
+            .setMode(CompilationMode.DEBUG)
+            .addLibraryFiles(ToolHelper.getAndroidJar(ToolHelper.getMinApiLevelForDexVm()))
+            .setProgramConsumer(new ArchiveConsumer(out))
+            .addProguardConfiguration(config, Origin.unknown());
+    for (Class<?> c : CLASSES) {
+      builder.addClassProgramData(ToolHelper.getClassAsBytes(c), Origin.unknown());
+    }
+    // TODO(b/75997473): Enable inlining when supported
+    ToolHelper.runR8(builder.build(), options -> options.enableInlining = false);
+    ProcessResult runOutput = ToolHelper.runJava(out, CLASS.getCanonicalName());
+    assertEquals(runInput.toString(), runOutput.toString());
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debug/MinificationTest.java b/src/test/java/com/android/tools/r8/debug/MinificationTest.java
index 414fbc3..2f995db 100644
--- a/src/test/java/com/android/tools/r8/debug/MinificationTest.java
+++ b/src/test/java/com/android/tools/r8/debug/MinificationTest.java
@@ -185,11 +185,6 @@
       String innerClassName,
       MethodSignature innerMethod)
       throws Throwable {
-    if (ToolHelper.isWindows()) {
-      // TODO(b/76135355): Update dx.bat on Windows to something that can build
-      // jdwp-tests-preN-dex.jar.
-      return;
-    }
     Path proguardMap = config.getProguardMap();
     String mappingFile = proguardMap == null ? null : proguardMap.toString();
     DexInspector inspector = new DexInspector(config.getPaths(), mappingFile);
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InterfaceMethodDesugaringTests.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InterfaceMethodDesugaringTests.java
index e07f319..d2cf708 100644
--- a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InterfaceMethodDesugaringTests.java
+++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InterfaceMethodDesugaringTests.java
@@ -4,8 +4,7 @@
 
 package com.android.tools.r8.desugaring.interfacemethods;
 
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
 
 import com.android.tools.r8.AsmTestBase;
 import com.android.tools.r8.ToolHelper;
@@ -33,7 +32,7 @@
         ToolHelper.getMinApiLevelForDexVm(),
         ToolHelper.getClassAsBytes(
             com.android.tools.r8.desugaring.interfacemethods.test0.TestMain.class),
-        introduceInvokeSpecial(ToolHelper.getClassAsBytes(
+        patchInterfaceWithDefaults(ToolHelper.getClassAsBytes(
             com.android.tools.r8.desugaring.interfacemethods.test0.InterfaceWithDefaults.class)));
   }
 
@@ -47,16 +46,33 @@
         ToolHelper.getMinApiLevelForDexVm(),
         ToolHelper.getClassAsBytes(
             com.android.tools.r8.desugaring.interfacemethods.test1.TestMain.class),
-        introduceInvokeSpecial(ToolHelper.getClassAsBytes(
+        patchInterfaceWithDefaults(ToolHelper.getClassAsBytes(
             com.android.tools.r8.desugaring.interfacemethods.test1.InterfaceWithDefaults.class)));
   }
 
-  private class MutableBoolean {
-    boolean value;
+  @Test
+  public void testInvokeSpecialToInheritedDefaultMethod() throws Exception {
+    ensureSameOutput(
+        com.android.tools.r8.desugaring.interfacemethods.test2.TestMain.class.getCanonicalName(),
+        ToolHelper.getMinApiLevelForDexVm(),
+        ToolHelper.getClassAsBytes(
+            com.android.tools.r8.desugaring.interfacemethods.test2.TestMain.class),
+        ToolHelper.getClassAsBytes(
+            com.android.tools.r8.desugaring.interfacemethods.test2.Test.class),
+        ToolHelper.getClassAsBytes(
+            com.android.tools.r8.desugaring.interfacemethods.test2.LeftTest.class),
+        ToolHelper.getClassAsBytes(
+            com.android.tools.r8.desugaring.interfacemethods.test2.RightTest.class),
+        ToolHelper.getClassAsBytes(
+            com.android.tools.r8.desugaring.interfacemethods.test2.Test2.class));
   }
 
-  private byte[] introduceInvokeSpecial(byte[] classBytes) throws IOException {
-    MutableBoolean patched = new MutableBoolean();
+  private static class MutableInteger {
+    int value;
+  }
+
+  private byte[] patchInterfaceWithDefaults(byte[] classBytes) throws IOException {
+    MutableInteger patched = new MutableInteger();
     try (InputStream input = new ByteArrayInputStream(classBytes)) {
       ClassReader cr = new ClassReader(input);
       ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
@@ -73,9 +89,9 @@
                   if (opcode == Opcodes.INVOKEINTERFACE &&
                       owner.endsWith("InterfaceWithDefaults") &&
                       name.equals("foo")) {
-                    assertFalse(patched.value);
+                    assertEquals(0, patched.value);
                     super.visitMethodInsn(Opcodes.INVOKESPECIAL, owner, name, desc, itf);
-                    patched.value = true;
+                    patched.value++;
 
                   } else {
                     super.visitMethodInsn(opcode, owner, name, desc, itf);
@@ -84,7 +100,7 @@
               };
             }
           }, 0);
-      assertTrue(patched.value);
+      assertEquals(1, patched.value);
       return cw.toByteArray();
     }
   }
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test2/LeftTest.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test2/LeftTest.java
new file mode 100644
index 0000000..4a5b2de
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test2/LeftTest.java
@@ -0,0 +1,8 @@
+// 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.desugaring.interfacemethods.test2;
+
+public interface LeftTest extends Test {
+}
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test2/RightTest.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test2/RightTest.java
new file mode 100644
index 0000000..6c6ea7e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test2/RightTest.java
@@ -0,0 +1,8 @@
+// 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.desugaring.interfacemethods.test2;
+
+public interface RightTest extends Test {
+}
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test2/Test.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test2/Test.java
new file mode 100644
index 0000000..1bf3295
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test2/Test.java
@@ -0,0 +1,11 @@
+// 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.desugaring.interfacemethods.test2;
+
+public interface Test {
+  default String foo(String a) {
+    return "Test::foo(" + a + ")";
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test2/Test2.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test2/Test2.java
new file mode 100644
index 0000000..b076cde
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test2/Test2.java
@@ -0,0 +1,11 @@
+// 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.desugaring.interfacemethods.test2;
+
+public interface Test2 extends LeftTest, RightTest {
+  default String bar(String a) {
+    return "Test2::bar(" + LeftTest.super.foo(a) + " + " + RightTest.super.foo(a) + ")";
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test2/TestMain.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test2/TestMain.java
new file mode 100644
index 0000000..ca86963
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test2/TestMain.java
@@ -0,0 +1,19 @@
+// 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.desugaring.interfacemethods.test2;
+
+public class TestMain implements Test2 {
+  public static void main(String... args) {
+    TestMain m = new TestMain();
+    System.out.println(m.bar("first"));
+    System.out.println(m.foo("second"));
+    System.out.println(m.fooDelegate("third"));
+  }
+
+  private String fooDelegate(String a) {
+    return "TestMain::fooDelegate(" + Test2.super.foo(a) + ")";
+  }
+}
+
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterTests.java b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterTests.java
index 19a5ee2..cf87d46 100644
--- a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterTests.java
+++ b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterTests.java
@@ -89,9 +89,9 @@
 
     if (useOptions) {
       Options options = new Options();
-      options.inputArchives.add(inputZip.toString());
-      options.featureSplitMapping = splitSpec.toString();
-      options.splitBaseName = output.toString();
+      options.addInputArchive(inputZip.toString());
+      options.setFeatureSplitMapping(splitSpec.toString());
+      options.setSplitBaseName(output.toString());
       DexSplitter.run(options);
     } else {
       DexSplitter.main(
@@ -235,12 +235,12 @@
     featureStream.close();
     if (useOptions) {
       Options options = new Options();
-      options.inputArchives.add(inputZip.toString());
-      options.splitBaseName = output.toString();
+      options.addInputArchive(inputZip.toString());
+      options.setSplitBaseName(output.toString());
       if (explicitBase) {
-        options.featureJars.add(baseJar.toString());
+        options.addFeatureJar(baseJar.toString());
       }
-      options.featureJars.add(featureJar.toString());
+      options.addFeatureJar(featureJar.toString());
       DexSplitter.run(options);
     } else {
       List<String> args = Lists.newArrayList(
diff --git a/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java b/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
index 3e46ff8..93f4249 100644
--- a/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
+++ b/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
@@ -47,7 +47,7 @@
   // invoke the tested method.
   private static final String JASMIN_MAIN_CLASS = "TestMain";
 
-  @Parameters(name = "{0}_{1}")
+  @Parameters(name = "allowAccessModification: {0} target: {1}")
   public static Collection<Object[]> data() {
     ImmutableList.Builder<Object[]> builder = new ImmutableList.Builder<>();
     for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) {
@@ -171,9 +171,16 @@
     return proguardRules.toString();
   }
 
+  protected String keepAllMembers(String className) {
+    return "-keep class " + className + " {" + System.lineSeparator()
+        + "  *;" + System.lineSeparator()
+        + "}";
+  }
+
   protected String keepClassMethod(String className, MethodSignature methodSignature) {
-    return "-keep class " + className + " {" + System.lineSeparator() +
-        methodSignature.toString() + ";" + System.lineSeparator() + "}";
+    return "-keep class " + className + " {" + System.lineSeparator()
+        + methodSignature.toString() + ";" + System.lineSeparator()
+        + "}";
   }
 
   protected void runTest(String folder, String mainClass, AndroidAppInspector inspector)
@@ -199,7 +206,7 @@
     // Build with R8
     AndroidApp.Builder builder = AndroidApp.builder();
     builder.addProgramFiles(classpath);
-    AndroidApp app = compileWithR8(builder.build(), proguardRules.toString());
+    AndroidApp app = compileWithR8(builder.build(), proguardRules);
 
     // Materialize file for execution.
     Path generatedDexFile = temp.getRoot().toPath().resolve("classes.jar");
diff --git a/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java b/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java
new file mode 100644
index 0000000..e324dee
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java
@@ -0,0 +1,83 @@
+// 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.kotlin;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.code.Format21t;
+import com.android.tools.r8.code.Format22t;
+import com.android.tools.r8.code.Instruction;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
+import java.util.Arrays;
+import org.junit.Test;
+
+public class SimplifyIfNotNullKotlinTest extends AbstractR8KotlinTestBase {
+  private static final String FOLDER = "non_null";
+  private static final String STRING = "java.lang.String";
+
+  private static boolean isIf(Instruction instruction) {
+    return instruction instanceof Format21t || instruction instanceof Format22t;
+  }
+
+  @Test
+  public void test_example1() throws Exception {
+    final TestKotlinClass ex1 = new TestKotlinClass("non_null.Example1Kt");
+    final MethodSignature testMethodSignature =
+        new MethodSignature("forMakeAndModel", "java.util.SortedMap",
+            ImmutableList.of("java.util.Collection", STRING, STRING, "java.lang.Integer"));
+
+    final String mainClassName = ex1.getClassName();
+    final String extraRules = keepAllMembers(mainClassName);
+    runTest(FOLDER, mainClassName, extraRules, app -> {
+      DexInspector dexInspector = new DexInspector(app);
+      ClassSubject clazz = checkClassExists(dexInspector, ex1.getClassName());
+
+      MethodSubject testMethod = checkMethodIsPresent(clazz, testMethodSignature);
+      DexCode dexCode = getDexCode(testMethod);
+      long count = Arrays.stream(dexCode.instructions)
+          .filter(SimplifyIfNotNullKotlinTest::isIf).count();
+      if (allowAccessModification) {
+        // TODO(b/76200247): 6 -> 5
+        // Three null-check's from inlined checkParameterIsNotNull for receiver and two arguments.
+        assertEquals(6, count);
+      } else {
+        // One after Iterator#hasNext, and another in the filter predicate: sinceYear != null.
+        assertEquals(2, count);
+      }
+    });
+  }
+
+  @Test
+  public void test_example2() throws Exception {
+    final TestKotlinClass ex2 = new TestKotlinClass("non_null.Example2Kt");
+    final MethodSignature testMethodSignature =
+        new MethodSignature("aOrDefault", STRING, ImmutableList.of(STRING, STRING));
+
+    final String mainClassName = ex2.getClassName();
+    final String extraRules = keepAllMembers(mainClassName);
+    runTest(FOLDER, mainClassName, extraRules, app -> {
+      DexInspector dexInspector = new DexInspector(app);
+      ClassSubject clazz = checkClassExists(dexInspector, ex2.getClassName());
+
+      MethodSubject testMethod = checkMethodIsPresent(clazz, testMethodSignature);
+      DexCode dexCode = getDexCode(testMethod);
+      long count = Arrays.stream(dexCode.instructions)
+          .filter(SimplifyIfNotNullKotlinTest::isIf).count();
+      if (allowAccessModification) {
+        // TODO(b/76202537): 3 -> 2,
+        //   Yet another null-check from checkParameterIsNotNull should subsume another from ?:
+        assertEquals(3, count);
+      } else {
+        // One null-check from force inlined coalesce and another from ?:
+        assertEquals(2, count);
+      }
+    });
+  }
+
+}
diff --git a/src/test/java/com/android/tools/r8/naming/InterfaceRenamingTest.java b/src/test/java/com/android/tools/r8/naming/InterfaceRenamingTest.java
new file mode 100644
index 0000000..d42800a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/InterfaceRenamingTest.java
@@ -0,0 +1,79 @@
+// 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.naming;
+
+public class InterfaceRenamingTest {
+
+  public static final Class[] CLASSES = {
+    InterfaceRenamingTest.class,
+    InterfaceA.class,
+    InterfaceB.class,
+    ImplementationA1.class,
+    ImplementationB1.class,
+    ImplementationA2.class,
+    ImplementationB2.class,
+  };
+
+  // Since the names and parameter lists of the two methods in the two interfaces are equal,
+  // non-aggressive minification gives the methods the same minified name.
+  // However, the return types are different, so looking up the renamed name by prototype
+  // only gives a result for one of the two interfaces.
+  interface InterfaceA {
+    Boolean interfaceMethod();
+  }
+
+  interface InterfaceB {
+    Integer interfaceMethod();
+  }
+
+  // Two implementations of each interface are required
+  // to avoid the Devirtualizer hiding the buggy behavior.
+  static class ImplementationA1 implements InterfaceA {
+    @Override
+    public Boolean interfaceMethod() {
+      System.out.println("interfaceMethod1");
+      return true;
+    }
+  }
+
+  static class ImplementationA2 implements InterfaceA {
+    @Override
+    public Boolean interfaceMethod() {
+      System.out.println("interfaceMethod1 dummy");
+      return false;
+    }
+  }
+
+  static class ImplementationB1 implements InterfaceB {
+    @Override
+    public Integer interfaceMethod() {
+      System.out.println("interfaceMethod2");
+      return 10;
+    }
+  }
+
+  static class ImplementationB2 implements InterfaceB {
+    @Override
+    public Integer interfaceMethod() {
+      System.out.println("interfaceMethod2 dummy");
+      return 20;
+    }
+  }
+
+  public static void main(String[] args) {
+    invokeA(new ImplementationA1());
+    invokeB(new ImplementationB1());
+    invokeA(new ImplementationA2());
+    invokeB(new ImplementationB2());
+  }
+
+  private static void invokeA(InterfaceA instance) {
+    System.out.println("invokeA: " + instance.interfaceMethod());
+  }
+
+  private static void invokeB(InterfaceB instance) {
+    System.out.println("invokeB: " + instance.interfaceMethod());
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/InterfaceRenamingTestRunner.java b/src/test/java/com/android/tools/r8/naming/InterfaceRenamingTestRunner.java
new file mode 100644
index 0000000..6c054e5
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/InterfaceRenamingTestRunner.java
@@ -0,0 +1,111 @@
+// 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.naming;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import com.android.tools.r8.ClassFileConsumer;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.DexIndexedConsumer;
+import com.android.tools.r8.ProgramConsumer;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.R8Command.Builder;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.origin.Origin;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.List;
+import org.junit.Test;
+
+public class InterfaceRenamingTestRunner extends TestBase {
+  static final Class CLASS = InterfaceRenamingTest.class;
+  static final Class[] CLASSES = InterfaceRenamingTest.CLASSES;
+
+  @Test
+  public void testCfNoMinify() throws Exception {
+    testCf(MinifyMode.NONE);
+  }
+
+  @Test
+  public void testCfMinify() throws Exception {
+    testCf(MinifyMode.JAVA);
+  }
+
+  @Test
+  public void testCfMinifyAggressive() throws Exception {
+    testCf(MinifyMode.AGGRESSIVE);
+  }
+
+  @Test
+  public void testDexNoMinify() throws Exception {
+    testDex(MinifyMode.NONE);
+  }
+
+  @Test
+  public void testDexMinify() throws Exception {
+    testDex(MinifyMode.JAVA);
+  }
+
+  @Test
+  public void testDexMinifyAggressive() throws Exception {
+    testDex(MinifyMode.AGGRESSIVE);
+  }
+
+  private void testCf(MinifyMode minify) throws Exception {
+    ProcessResult runInput =
+        ToolHelper.runJava(ToolHelper.getClassPathForTests(), CLASS.getCanonicalName());
+    assertEquals(0, runInput.exitCode);
+    Path outCf = temp.getRoot().toPath().resolve("cf.zip");
+    build(new ClassFileConsumer.ArchiveConsumer(outCf), minify);
+    ProcessResult runCf = ToolHelper.runJava(outCf, CLASS.getCanonicalName());
+    assertEquals(runInput.toString(), runCf.toString());
+  }
+
+  private void testDex(MinifyMode minify) throws Exception {
+    ProcessResult runInput =
+        ToolHelper.runJava(ToolHelper.getClassPathForTests(), CLASS.getCanonicalName());
+    assertEquals(0, runInput.exitCode);
+    Path outDex = temp.getRoot().toPath().resolve("dex.zip");
+    build(new DexIndexedConsumer.ArchiveConsumer(outDex), minify);
+    ProcessResult runDex =
+        ToolHelper.runArtNoVerificationErrorsRaw(outDex.toString(), CLASS.getCanonicalName());
+    assertEquals(runInput.stdout, runDex.stdout);
+    assertEquals(runInput.exitCode, runDex.exitCode);
+  }
+
+  private void build(ProgramConsumer consumer, MinifyMode minify) throws Exception {
+    List<String> config =
+        Arrays.asList(
+            "-keep public class " + CLASS.getCanonicalName() + " {",
+            "  public static void main(...);",
+            "}");
+
+    Builder builder =
+        ToolHelper.addProguardConfigurationConsumer(
+                R8Command.builder(),
+                pgConfig -> {
+                  pgConfig.setPrintMapping(true);
+                  pgConfig.setOverloadAggressively(minify == MinifyMode.AGGRESSIVE);
+                  if (!minify.isMinify()) {
+                    pgConfig.disableObfuscation();
+                  }
+                })
+            .setMode(CompilationMode.DEBUG)
+            .addLibraryFiles(ToolHelper.getAndroidJar(ToolHelper.getMinApiLevelForDexVm()))
+            .setProgramConsumer(consumer)
+            .addProguardConfiguration(config, Origin.unknown());
+    for (Class<?> c : CLASSES) {
+      builder.addClassProgramData(ToolHelper.getClassAsBytes(c), Origin.unknown());
+    }
+    if (consumer instanceof ClassFileConsumer) {
+      // TODO(b/75997473): Enable inlining when supported by CF backend
+      ToolHelper.runR8(builder.build(), options -> options.enableInlining = false);
+    } else {
+      ToolHelper.runR8(builder.build());
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/regress/B76025099.java b/src/test/java/com/android/tools/r8/regress/B76025099.java
new file mode 100644
index 0000000..9fa694d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/regress/B76025099.java
@@ -0,0 +1,63 @@
+// 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.regress;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.OutputMode;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.VmTestRunner;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.FileUtils;
+import com.google.common.collect.ImmutableList;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import regress_76025099.Main;
+
+@RunWith(VmTestRunner.class)
+public class B76025099 extends TestBase {
+
+  private static final String PRG =
+      ToolHelper.EXAMPLES_BUILD_DIR + "regress_76025099" + FileUtils.JAR_EXTENSION;
+
+  private AndroidApp runR8(AndroidApp app, Class main, Path out) throws Exception {
+     R8Command command =
+        ToolHelper.addProguardConfigurationConsumer(
+            ToolHelper.prepareR8CommandBuilder(app),
+            pgConfig -> {
+              pgConfig.setPrintMapping(true);
+              pgConfig.setPrintMappingFile(out.resolve(ToolHelper.DEFAULT_PROGUARD_MAP_FILE));
+            })
+        .addProguardConfiguration(
+            ImmutableList.of(keepMainProguardConfiguration(main)),
+            Origin.unknown())
+        .setOutput(out, OutputMode.DexIndexed)
+        .build();
+    return ToolHelper.runR8(command, o -> {
+      o.enableMinification = false;
+    });
+  }
+
+  @Ignore("b/76025099")
+  @Test
+  public void test() throws Exception {
+    Path out = temp.getRoot().toPath();
+    Path jarPath = Paths.get(PRG);
+    String mainName = Main.class.getCanonicalName();
+    ProcessResult jvmOutput = ToolHelper.runJava(ImmutableList.of(jarPath), mainName);
+    assertEquals(0, jvmOutput.exitCode);
+    AndroidApp processedApp = runR8(readJar(jarPath), Main.class, out);
+    ProcessResult artOutput = runOnArtRaw(processedApp, mainName);
+    assertEquals(0, artOutput.exitCode);
+    assertEquals(jvmOutput.stdout, artOutput.stdout);
+  }
+
+}
diff --git a/src/test/java/com/android/tools/r8/resolution/PublicFieldInnerClassTest.java b/src/test/java/com/android/tools/r8/resolution/PublicFieldInnerClassTest.java
new file mode 100644
index 0000000..7733339
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/resolution/PublicFieldInnerClassTest.java
@@ -0,0 +1,70 @@
+// 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.resolution;
+
+public class PublicFieldInnerClassTest {
+  public static final Class<?>[] CLASSES = {
+      PrivateBase.class,
+      PrivateSubclass.class,
+      PackageBase.class,
+      PackageSubclass.class,
+      ProtectedBase.class,
+      ProtectedSubclass.class,
+      PublicBase.class,
+      PublicSubclass.class,
+      PublicFieldInnerClassTest.class
+  };
+
+  private static class PrivateBase {
+    public int value;
+  }
+
+  private static class PrivateSubclass extends PrivateBase {
+  }
+
+  static class PackageBase {
+    public int value;
+  }
+
+  private static class PackageSubclass extends PackageBase {
+  }
+
+  protected static class ProtectedBase {
+    public int value;
+  }
+
+  private static class ProtectedSubclass extends ProtectedBase {
+  }
+
+  public static class PublicBase {
+    public int value;
+  }
+
+  private static class PublicSubclass extends PublicBase {
+  }
+
+  private static int getPrivate(PrivateSubclass instance) {
+    return instance.value;
+  }
+
+  private static int getPackage(PackageSubclass instance) {
+    return instance.value;
+  }
+
+  private static int getProtected(ProtectedSubclass instance) {
+    return instance.value;
+  }
+
+  private static int getPublic(PublicSubclass instance) {
+    return instance.value;
+  }
+
+  public static void main(String[] args) {
+    System.out.println(getPrivate(new PrivateSubclass()));
+    System.out.println(getPackage(new PackageSubclass()));
+    System.out.println(getProtected(new ProtectedSubclass()));
+    System.out.println(getPublic(new PublicSubclass()));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/resolution/PublicFieldInnerClassTestRunner.java b/src/test/java/com/android/tools/r8/resolution/PublicFieldInnerClassTestRunner.java
new file mode 100644
index 0000000..b13a08e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/resolution/PublicFieldInnerClassTestRunner.java
@@ -0,0 +1,80 @@
+// 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.resolution;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import com.android.tools.r8.ClassFileConsumer;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.DexIndexedConsumer;
+import com.android.tools.r8.ProgramConsumer;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.R8Command.Builder;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.origin.Origin;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.List;
+import org.junit.Test;
+
+public class PublicFieldInnerClassTestRunner extends TestBase {
+  static final Class CLASS = PublicFieldInnerClassTest.class;
+  static final Class<?>[] CLASSES = PublicFieldInnerClassTest.CLASSES;
+
+  @Test
+  public void testCf() throws Exception {
+    ProcessResult runInput =
+        ToolHelper.runJava(ToolHelper.getClassPathForTests(), CLASS.getCanonicalName());
+    assertEquals(0, runInput.exitCode);
+    Path outCf = temp.getRoot().toPath().resolve("cf.jar");
+    build(new ClassFileConsumer.ArchiveConsumer(outCf));
+    ProcessResult runCf = ToolHelper.runJava(outCf, CLASS.getCanonicalName());
+    assertEquals(runInput.toString(), runCf.toString());
+    assertEquals(
+        -1,
+        runCf.stderr.indexOf("java.lang.NoSuchFieldError"));
+  }
+
+  @Test
+  public void testDex() throws Exception {
+    ProcessResult runInput =
+        ToolHelper.runJava(ToolHelper.getClassPathForTests(), CLASS.getCanonicalName());
+    assertEquals(0, runInput.exitCode);
+    Path outDex = temp.getRoot().toPath().resolve("dex.zip");
+    build(new DexIndexedConsumer.ArchiveConsumer(outDex));
+    // TODO(b/76191597): Change to runArtNoVerificationErrors + assertEquals when bug is fixed
+    ProcessResult runDex = ToolHelper.runArtRaw(
+        outDex.toString(), CLASS.getCanonicalName());
+    assertNotEquals(runInput.stdout, runDex.stdout);
+    assertNotEquals(runInput.exitCode, runDex.exitCode);
+    assertNotEquals(
+        -1,
+        runDex.stderr.indexOf("java.lang.NoSuchFieldError"));
+  }
+
+  private void build(ProgramConsumer consumer) throws Exception {
+    List<String> config = Arrays.asList(
+        "-keep public class " + CLASS.getCanonicalName() + " {",
+        "  public static void main(...);",
+        "}"
+    );
+    Builder builder = R8Command.builder()
+        .setMode(CompilationMode.DEBUG)
+        .addLibraryFiles(ToolHelper.getAndroidJar(ToolHelper.getMinApiLevelForDexVm()))
+        .setProgramConsumer(consumer)
+        .addProguardConfiguration(config, Origin.unknown());
+    for (Class<?> c : CLASSES) {
+      builder.addClassProgramData(ToolHelper.getClassAsBytes(c), Origin.unknown());
+    }
+    if (consumer instanceof ClassFileConsumer) {
+      // TODO(b/75997473): Enable inlining when supported by CF.
+      ToolHelper.runR8(builder.build(), options -> options.enableInlining = false);
+    } else {
+      ToolHelper.runR8(builder.build());
+    }
+  }
+}
diff --git a/src/test/kotlinR8TestResources/non_null/example1.kt b/src/test/kotlinR8TestResources/non_null/example1.kt
new file mode 100644
index 0000000..02a3fcf
--- /dev/null
+++ b/src/test/kotlinR8TestResources/non_null/example1.kt
@@ -0,0 +1,29 @@
+// 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 non_null
+
+data class Car(
+        val make: String,
+        val model: String,
+        val year: Int,
+        val plateNumber: String)
+
+fun Collection<Car>.forMakeAndModel(
+        make: String, model: String, sinceYear: Int?
+) = this.asSequence()
+        .filter { it.make == make }
+        .filter { it.model == model }
+        .filter { sinceYear != null && it.year >= sinceYear }
+        .groupBy { it.year }
+        .toSortedMap()
+
+fun main(args: Array<String>) {
+    val leaf = Car("Nissan", "Leaf", 2015, "  LEAF  ")
+    val ms1 = Car("Tesla", "Model S", 2015, "  LGTM1 ")
+    val ms2 = Car("Tesla", "Model S", 2017, "  LGTM2 ")
+    val m3 = Car("Tesla", "Model 3", 2018, "  LGTM3 ")
+    val cars: List<Car> = mutableListOf(leaf, ms1, ms2, m3)
+    println(cars.forMakeAndModel("Tesla", "Model S", null))
+}
diff --git a/src/test/kotlinR8TestResources/non_null/example2.kt b/src/test/kotlinR8TestResources/non_null/example2.kt
new file mode 100644
index 0000000..e35b2e8
--- /dev/null
+++ b/src/test/kotlinR8TestResources/non_null/example2.kt
@@ -0,0 +1,14 @@
+// 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 non_null
+
+inline fun coalesce(a: String?, b: String?): String? = a ?: b
+fun aOrDefault(a: String?, default: String): String =
+        coalesce(a, default) ?: throw AssertionError()
+
+fun main(args: Array<String>) {
+    println(aOrDefault(null, "null"))
+    println(aOrDefault("null", "non-null"))
+}
\ No newline at end of file