Merge commit '7ba844fa7c6f67fcd9a990d375df8deb05ab9137' into dev-release
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfo.java b/src/main/java/com/android/tools/r8/graph/AppInfo.java
index 139a285..6c8d4cf 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfo.java
@@ -12,11 +12,14 @@
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableMap.Builder;
-import com.google.common.collect.ImmutableSet;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 
@@ -402,59 +405,48 @@
    * <a href="https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html#jvms-5.4.3.3">
    * Section 5.4.3.3 of the JVM Spec</a>. As this is the same for interfaces and classes, we share
    * one implementation.
-   * <p>
-   * This method will return all maximally specific default methods if there is more than one. If
-   * there is no default method, any of the found methods is returned.
    */
   private ResolutionResult resolveMethodStep3(DexClass clazz, DexMethod method) {
-    MultiResultBuilder builder = new MultiResultBuilder();
-    DexEncodedMethod anyTarget = resolveMethodStep3Helper(clazz, method, builder);
-    ResolutionResult result = builder.build();
-    if (result != null) {
-      // We have found default methods, return them.
-      return result;
-    }
-    // Return any of the non-default methods.
-    return anyTarget == null ? NoSuchMethodResult.INSTANCE : new SingleResolutionResult(anyTarget);
+    MaximallySpecificMethodsBuilder builder = new MaximallySpecificMethodsBuilder();
+    resolveMethodStep3Helper(clazz, method, builder);
+    return builder.resolve();
   }
 
-  /**
-   * Helper method that performs the actual search and adds all maximally specific default methods
-   * to the builder. Additionally, one of the maximally specific default methods or, if none exist,
-   * any of the found methods, is returned.
-   */
-  private DexEncodedMethod resolveMethodStep3Helper(DexClass clazz, DexMethod method,
-      MultiResultBuilder builder) {
-    // We are looking for the maximally-specific superinterfaces that have a
-    // non-abstract method or any of the abstract methods.
-    DexEncodedMethod result = null;
+  /** Helper method that builds the set of maximally specific methods. */
+  private void resolveMethodStep3Helper(
+      DexClass clazz, DexMethod method, MaximallySpecificMethodsBuilder builder) {
     for (DexType iface : clazz.interfaces.values) {
       DexClass definiton = definitionFor(iface);
       if (definiton == null) {
         // Ignore missing interface definitions.
         continue;
       }
-      DexEncodedMethod localResult = definiton.lookupMethod(method);
-      // Remember the result, if any, as local result.
-      result = selectCandidate(result, localResult);
-      if (localResult != null && localResult.isNonAbstractVirtualMethod()) {
-        // We have found a default method in this class. Remember it and stop the search.
-        builder.add(localResult);
+      assert definiton.isInterface();
+      DexEncodedMethod result = definiton.lookupMethod(method);
+      if (isMaximallySpecificCandidate(result)) {
+        // The candidate is added and doing so will prohibit shadowed methods from being in the set.
+        builder.addCandidate(definiton, result, this);
       } else {
         // Look at the super-interfaces of this class and keep searching.
-        localResult = resolveMethodStep3Helper(definiton, method, builder);
-        result = selectCandidate(result, localResult);
+        resolveMethodStep3Helper(definiton, method, builder);
       }
     }
     // Now look at indirect super interfaces.
     if (clazz.superType != null) {
       DexClass superClass = definitionFor(clazz.superType);
       if (superClass != null) {
-        DexEncodedMethod superResult = resolveMethodStep3Helper(superClass, method, builder);
-        result = selectCandidate(result, superResult);
+        resolveMethodStep3Helper(superClass, method, builder);
       }
     }
-    return result;
+  }
+
+  /**
+   * A candidate for being a maximally specific method must have neither its private, nor its static
+   * flag set. A candidate may still not be maximally specific, which entails that no subinterfaces
+   * from also contribute with a candidate to the type. That is not determined by this method.
+   */
+  private boolean isMaximallySpecificCandidate(DexEncodedMethod method) {
+    return method != null && !method.accessFlags.isPrivate() && !method.accessFlags.isStatic();
   }
 
   /**
@@ -605,22 +597,6 @@
     return null;
   }
 
-  /**
-   * If previous is non-null, selects previous. If current is non-null and a non-private,
-   * non-static method, current is selected. Otherwise null is returned.
-   */
-  private DexEncodedMethod selectCandidate(DexEncodedMethod previous, DexEncodedMethod current) {
-    if (previous != null) {
-      assert !previous.accessFlags.isPrivate();
-      assert !previous.accessFlags.isStatic();
-      return previous;
-    }
-    if (current != null && !current.accessFlags.isPrivate() && !current.accessFlags.isStatic()) {
-      return current;
-    }
-    return null;
-  }
-
   public boolean hasSubtyping() {
     assert checkIfObsolete();
     return false;
@@ -646,31 +622,85 @@
     return app.mainDexList.contains(type);
   }
 
-  private static class MultiResultBuilder {
+  private static class MaximallySpecificMethodsBuilder {
 
-    private ImmutableSet.Builder<DexEncodedMethod> builder;
-    private DexEncodedMethod singleResult;
+    // The set of actual maximally specific methods.
+    // This set is linked map so that in the case where a number of methods remain a deterministic
+    // choice can be made. The map is from definition classes to their maximally specific method, or
+    // in the case that a type has a candidate which is shadowed by a subinterface, the map will
+    // map the class to a null entry, thus any addition to the map must check for key containment
+    // prior to writing.
+    LinkedHashMap<DexClass, DexEncodedMethod> maximallySpecificMethods = new LinkedHashMap<>();
 
-    void add(DexEncodedMethod result) {
-      if (builder != null) {
-        builder.add(result);
-      } else if (singleResult != null && !singleResult.equals(result)) {
-        builder = ImmutableSet.builder();
-        builder.add(singleResult, result);
-        singleResult = null;
-      } else {
-        singleResult = result;
+    void addCandidate(DexClass holder, DexEncodedMethod method, AppInfo appInfo) {
+      // If this candidate is already a candidate or it is shadowed, then no need to continue.
+      if (maximallySpecificMethods.containsKey(holder)) {
+        return;
+      }
+      maximallySpecificMethods.put(holder, method);
+      // Prune exiting candidates and prohibit future candidates in the super hierarchy.
+      assert holder.isInterface();
+      assert holder.superType == appInfo.dexItemFactory.objectType;
+      for (DexType iface : holder.interfaces.values) {
+        markShadowed(iface, appInfo);
       }
     }
 
-    ResolutionResult build() {
-      if (builder != null) {
-        return new MultiResolutionResult(builder.build().asList());
+    private void markShadowed(DexType type, AppInfo appInfo) {
+      if (type == null) {
+        return;
       }
-      if (singleResult != null) {
-        return new SingleResolutionResult(singleResult);
+      DexClass clazz = appInfo.definitionFor(type);
+      if (clazz == null) {
+        return;
       }
-      return null;
+      assert clazz.isInterface();
+      assert clazz.superType == appInfo.dexItemFactory.objectType;
+      // A null entry signifies that the candidate is shadowed blocking future candidates.
+      // If the candidate is already shadowed at this type there is no need to shadow further up.
+      if (maximallySpecificMethods.containsKey(clazz)
+          && maximallySpecificMethods.get(clazz) == null) {
+        return;
+      }
+      maximallySpecificMethods.put(clazz, null);
+      for (DexType iface : clazz.interfaces.values) {
+        markShadowed(iface, appInfo);
+      }
+    }
+
+    ResolutionResult resolve() {
+      if (maximallySpecificMethods.isEmpty()) {
+        return NoSuchMethodResult.INSTANCE;
+      }
+      // Fast path in the common case of a single method.
+      if (false && maximallySpecificMethods.size() == 1) {
+        return new SingleResolutionResult(maximallySpecificMethods.values().iterator().next());
+      }
+      DexEncodedMethod firstMaximallySpecificMethod = null;
+      List<DexEncodedMethod> nonAbstractMethods = new ArrayList<>(maximallySpecificMethods.size());
+      for (DexEncodedMethod method : maximallySpecificMethods.values()) {
+        if (method == null) {
+          // Ignore shadowed candidates.
+          continue;
+        }
+        if (firstMaximallySpecificMethod == null) {
+          firstMaximallySpecificMethod = method;
+        }
+        if (method.isNonAbstractVirtualMethod()) {
+          nonAbstractMethods.add(method);
+        }
+      }
+      // If there are no non-abstract methods, then any candidate will suffice as a target.
+      // For deterministic resolution, we return the first mapped method (of the linked map).
+      if (nonAbstractMethods.isEmpty()) {
+        return new SingleResolutionResult(firstMaximallySpecificMethod);
+      }
+      // If there is exactly one non-abstract method (a default method) it is the resolution target.
+      if (nonAbstractMethods.size() == 1) {
+        return new SingleResolutionResult(nonAbstractMethods.get(0));
+      }
+      // TODO(b/144085169): In the case of multiple non-abstract methods resolution should fail.
+      return new MultiResolutionResult(ImmutableList.copyOf(nonAbstractMethods));
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java b/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
index 6308a16..f8aaa42 100644
--- a/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
@@ -19,7 +19,7 @@
 import java.util.List;
 import java.util.Map;
 
-public class DirectMappedDexApplication extends DexApplication {
+public class DirectMappedDexApplication extends DexApplication implements DexDefinitionSupplier {
 
   // Mapping from code objects to their encoded-method owner. Used for asserting unique ownership
   // and debugging purposes.
@@ -70,12 +70,46 @@
   }
 
   @Override
+  public DexDefinition definitionFor(DexReference reference) {
+    if (reference.isDexType()) {
+      return definitionFor(reference.asDexType());
+    }
+    if (reference.isDexMethod()) {
+      return definitionFor(reference.asDexMethod());
+    }
+    assert reference.isDexField();
+    return definitionFor(reference.asDexField());
+  }
+
+  @Override
+  public DexEncodedField definitionFor(DexField field) {
+    DexClass clazz = definitionFor(field.holder);
+    return clazz != null ? clazz.lookupField(field) : null;
+  }
+
+  @Override
+  public DexEncodedMethod definitionFor(DexMethod method) {
+    DexClass clazz = definitionFor(method.holder);
+    return clazz != null ? clazz.lookupMethod(method) : null;
+  }
+
+  @Override
   public DexClass definitionFor(DexType type) {
     assert type.isClassType() : "Cannot lookup definition for type: " + type;
     return allClasses.get(type);
   }
 
   @Override
+  public DexProgramClass definitionForProgramType(DexType type) {
+    return programDefinitionFor(type);
+  }
+
+  @Override
+  public DexItemFactory dexItemFactory() {
+    return dexItemFactory;
+  }
+
+  @Override
   public DexProgramClass programDefinitionFor(DexType type) {
     DexClass clazz = definitionFor(type);
     return clazz instanceof DexProgramClass ? clazz.asProgramClass() : null;
diff --git a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollectionImpl.java b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollectionImpl.java
index 1478f2d..3a307cf 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollectionImpl.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollectionImpl.java
@@ -36,11 +36,13 @@
     infos.entrySet().removeIf(entry -> predicate.test(entry.getKey(), entry.getValue()));
   }
 
-  public FieldAccessInfoCollectionImpl rewrittenWithLens(GraphLense lens) {
+  public FieldAccessInfoCollectionImpl rewrittenWithLens(
+      DexDefinitionSupplier definitions, GraphLense lens) {
     FieldAccessInfoCollectionImpl collection = new FieldAccessInfoCollectionImpl();
     infos.forEach(
         (field, info) ->
-            collection.infos.put(lens.lookupField(field), info.rewrittenWithLens(lens)));
+            collection.infos.put(
+                lens.lookupField(field), info.rewrittenWithLens(definitions, lens)));
     return collection;
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java
index fcd4f5c..a263cb6 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java
@@ -189,25 +189,31 @@
     writesWithContexts = null;
   }
 
-  public FieldAccessInfoImpl rewrittenWithLens(GraphLense lens) {
+  public FieldAccessInfoImpl rewrittenWithLens(DexDefinitionSupplier definitions, GraphLense lens) {
     FieldAccessInfoImpl rewritten = new FieldAccessInfoImpl(lens.lookupField(field));
     if (readsWithContexts != null) {
       rewritten.readsWithContexts = new IdentityHashMap<>();
       readsWithContexts.forEach(
-          (access, contexts) ->
-              rewritten
-                  .readsWithContexts
-                  .computeIfAbsent(lens.lookupField(access), ignore -> Sets.newIdentityHashSet())
-                  .addAll(contexts));
+          (access, contexts) -> {
+            Set<DexEncodedMethod> newContexts =
+                rewritten.readsWithContexts.computeIfAbsent(
+                    lens.lookupField(access), ignore -> Sets.newIdentityHashSet());
+            for (DexEncodedMethod context : contexts) {
+              newContexts.add(lens.mapDexEncodedMethod(context, definitions));
+            }
+          });
     }
     if (writesWithContexts != null) {
       rewritten.writesWithContexts = new IdentityHashMap<>();
       writesWithContexts.forEach(
-          (access, contexts) ->
-              rewritten
-                  .writesWithContexts
-                  .computeIfAbsent(lens.lookupField(access), ignore -> Sets.newIdentityHashSet())
-                  .addAll(contexts));
+          (access, contexts) -> {
+            Set<DexEncodedMethod> newContexts =
+                rewritten.writesWithContexts.computeIfAbsent(
+                    lens.lookupField(access), ignore -> Sets.newIdentityHashSet());
+            for (DexEncodedMethod context : contexts) {
+              newContexts.add(lens.mapDexEncodedMethod(context, definitions));
+            }
+          });
     }
     return rewritten;
   }
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLense.java b/src/main/java/com/android/tools/r8/graph/GraphLense.java
index 16dab7e..8c5dd71 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLense.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLense.java
@@ -420,6 +420,10 @@
 
   public DexEncodedMethod mapDexEncodedMethod(
       DexEncodedMethod originalEncodedMethod, DexDefinitionSupplier definitions) {
+    assert originalEncodedMethod != DexEncodedMethod.SENTINEL;
+    if (originalEncodedMethod == DexEncodedMethod.ANNOTATION_REFERENCE) {
+      return DexEncodedMethod.ANNOTATION_REFERENCE;
+    }
     DexMethod newMethod = getRenamedMethodSignature(originalEncodedMethod.method);
     // Note that:
     // * Even if `newMethod` is the same as `originalEncodedMethod.method`, we still need to look it
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/FieldValueHelper.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/FieldValueHelper.java
index d06b3f9..832e8b9 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/FieldValueHelper.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/FieldValueHelper.java
@@ -111,6 +111,11 @@
       // we just created for future use we should delay removing trivial
       // phis until we are done with replacing fields reads.
       phi.addOperands(operands, false);
+
+      TypeLatticeElement phiType = phi.computePhiType(appView);
+      assert phiType.lessThanOrEqual(phi.getTypeLattice(), appView);
+      phi.setTypeLattice(phiType);
+
       value = phi;
     }
 
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index f89e220..f5b0de8 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -4,7 +4,6 @@
 package com.android.tools.r8.shaking;
 
 import static com.android.tools.r8.graph.GraphLense.rewriteReferenceKeys;
-import static com.google.common.base.Predicates.not;
 
 import com.android.tools.r8.graph.AppInfoWithSubtyping;
 import com.android.tools.r8.graph.DexApplication;
@@ -35,6 +34,7 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.ImmutableSortedSet;
+import com.google.common.collect.ImmutableSortedSet.Builder;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Sets;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
@@ -81,6 +81,8 @@
    * removed.
    */
   final SortedSet<DexMethod> targetedMethods;
+
+  final Set<DexMethod> targetedMethodsThatMustRemainNonAbstract;
   /**
    * Set of program methods that are used as the bootstrap method for an invoke-dynamic instruction.
    */
@@ -100,16 +102,6 @@
    * each field. The latter is used, for example, during member rebinding.
    */
   private final FieldAccessInfoCollectionImpl fieldAccessInfoCollection;
-  /**
-   * Set of all instance fields that are only written inside the <init>() methods of their enclosing
-   * class.
-   */
-  private Set<DexField> instanceFieldsWrittenOnlyInEnclosingInstanceInitializers;
-  /**
-   * Set of all static fields that are only written inside the <clinit>() method of their enclosing
-   * class.
-   */
-  private Set<DexField> staticFieldsWrittenOnlyInEnclosingStaticInitializer;
   /** Set of all methods referenced in virtual invokes, along with calling context. */
   public final SortedMap<DexMethod, Set<DexEncodedMethod>> virtualInvokes;
   /** Set of all methods referenced in interface invokes, along with calling context. */
@@ -195,13 +187,12 @@
       Set<DexType> instantiatedAppServices,
       Set<DexType> instantiatedTypes,
       SortedSet<DexMethod> targetedMethods,
+      Set<DexMethod> targetedMethodsThatMustRemainNonAbstract,
       SortedSet<DexMethod> bootstrapMethods,
       SortedSet<DexMethod> methodsTargetedByInvokeDynamic,
       SortedSet<DexMethod> virtualMethodsTargetedByInvokeDirect,
       SortedSet<DexMethod> liveMethods,
       FieldAccessInfoCollectionImpl fieldAccessInfoCollection,
-      Set<DexField> instanceFieldsWrittenOnlyInEnclosingInstanceInitializers,
-      Set<DexField> staticFieldsWrittenOnlyInEnclosingStaticInitializer,
       SortedMap<DexMethod, Set<DexEncodedMethod>> virtualInvokes,
       SortedMap<DexMethod, Set<DexEncodedMethod>> interfaceInvokes,
       SortedMap<DexMethod, Set<DexEncodedMethod>> superInvokes,
@@ -234,15 +225,12 @@
     this.instantiatedAppServices = instantiatedAppServices;
     this.instantiatedTypes = instantiatedTypes;
     this.targetedMethods = targetedMethods;
+    this.targetedMethodsThatMustRemainNonAbstract = targetedMethodsThatMustRemainNonAbstract;
     this.bootstrapMethods = bootstrapMethods;
     this.methodsTargetedByInvokeDynamic = methodsTargetedByInvokeDynamic;
     this.virtualMethodsTargetedByInvokeDirect = virtualMethodsTargetedByInvokeDirect;
     this.liveMethods = liveMethods;
     this.fieldAccessInfoCollection = fieldAccessInfoCollection;
-    this.instanceFieldsWrittenOnlyInEnclosingInstanceInitializers =
-        instanceFieldsWrittenOnlyInEnclosingInstanceInitializers;
-    this.staticFieldsWrittenOnlyInEnclosingStaticInitializer =
-        staticFieldsWrittenOnlyInEnclosingStaticInitializer;
     this.pinnedItems = pinnedItems;
     this.mayHaveSideEffects = mayHaveSideEffects;
     this.noSideEffects = noSideEffects;
@@ -278,13 +266,12 @@
       Set<DexType> instantiatedAppServices,
       Set<DexType> instantiatedTypes,
       SortedSet<DexMethod> targetedMethods,
+      Set<DexMethod> targetedMethodsThatMustRemainNonAbstract,
       SortedSet<DexMethod> bootstrapMethods,
       SortedSet<DexMethod> methodsTargetedByInvokeDynamic,
       SortedSet<DexMethod> virtualMethodsTargetedByInvokeDirect,
       SortedSet<DexMethod> liveMethods,
       FieldAccessInfoCollectionImpl fieldAccessInfoCollection,
-      Set<DexField> instanceFieldsWrittenOnlyInEnclosingInstanceInitializers,
-      Set<DexField> staticFieldsWrittenOnlyInEnclosingStaticInitializer,
       SortedMap<DexMethod, Set<DexEncodedMethod>> virtualInvokes,
       SortedMap<DexMethod, Set<DexEncodedMethod>> interfaceInvokes,
       SortedMap<DexMethod, Set<DexEncodedMethod>> superInvokes,
@@ -317,15 +304,12 @@
     this.instantiatedAppServices = instantiatedAppServices;
     this.instantiatedTypes = instantiatedTypes;
     this.targetedMethods = targetedMethods;
+    this.targetedMethodsThatMustRemainNonAbstract = targetedMethodsThatMustRemainNonAbstract;
     this.bootstrapMethods = bootstrapMethods;
     this.methodsTargetedByInvokeDynamic = methodsTargetedByInvokeDynamic;
     this.virtualMethodsTargetedByInvokeDirect = virtualMethodsTargetedByInvokeDirect;
     this.liveMethods = liveMethods;
     this.fieldAccessInfoCollection = fieldAccessInfoCollection;
-    this.instanceFieldsWrittenOnlyInEnclosingInstanceInitializers =
-        instanceFieldsWrittenOnlyInEnclosingInstanceInitializers;
-    this.staticFieldsWrittenOnlyInEnclosingStaticInitializer =
-        staticFieldsWrittenOnlyInEnclosingStaticInitializer;
     this.pinnedItems = pinnedItems;
     this.mayHaveSideEffects = mayHaveSideEffects;
     this.noSideEffects = noSideEffects;
@@ -362,13 +346,12 @@
         previous.instantiatedAppServices,
         previous.instantiatedTypes,
         previous.targetedMethods,
+        previous.targetedMethodsThatMustRemainNonAbstract,
         previous.bootstrapMethods,
         previous.methodsTargetedByInvokeDynamic,
         previous.virtualMethodsTargetedByInvokeDirect,
         previous.liveMethods,
         previous.fieldAccessInfoCollection,
-        previous.instanceFieldsWrittenOnlyInEnclosingInstanceInitializers,
-        previous.staticFieldsWrittenOnlyInEnclosingStaticInitializer,
         previous.virtualInvokes,
         previous.interfaceInvokes,
         previous.superInvokes,
@@ -410,13 +393,12 @@
         previous.instantiatedAppServices,
         previous.instantiatedTypes,
         previous.targetedMethods,
+        previous.targetedMethodsThatMustRemainNonAbstract,
         previous.bootstrapMethods,
         previous.methodsTargetedByInvokeDynamic,
         previous.virtualMethodsTargetedByInvokeDirect,
         previous.liveMethods,
         previous.fieldAccessInfoCollection,
-        previous.instanceFieldsWrittenOnlyInEnclosingInstanceInitializers,
-        previous.staticFieldsWrittenOnlyInEnclosingStaticInitializer,
         previous.virtualInvokes,
         previous.interfaceInvokes,
         previous.superInvokes,
@@ -462,19 +444,16 @@
     this.instantiatedTypes = rewriteItems(previous.instantiatedTypes, lense::lookupType);
     this.instantiatedLambdas = rewriteItems(previous.instantiatedLambdas, lense::lookupType);
     this.targetedMethods = lense.rewriteMethodsConservatively(previous.targetedMethods);
+    this.targetedMethodsThatMustRemainNonAbstract =
+        lense.rewriteMethodsConservatively(previous.targetedMethodsThatMustRemainNonAbstract);
     this.bootstrapMethods = lense.rewriteMethodsConservatively(previous.bootstrapMethods);
     this.methodsTargetedByInvokeDynamic =
         lense.rewriteMethodsConservatively(previous.methodsTargetedByInvokeDynamic);
     this.virtualMethodsTargetedByInvokeDirect =
         lense.rewriteMethodsConservatively(previous.virtualMethodsTargetedByInvokeDirect);
     this.liveMethods = lense.rewriteMethodsConservatively(previous.liveMethods);
-    this.fieldAccessInfoCollection = previous.fieldAccessInfoCollection.rewrittenWithLens(lense);
-    this.instanceFieldsWrittenOnlyInEnclosingInstanceInitializers =
-        rewriteItems(
-            previous.instanceFieldsWrittenOnlyInEnclosingInstanceInitializers, lense::lookupField);
-    this.staticFieldsWrittenOnlyInEnclosingStaticInitializer =
-        rewriteItems(
-            previous.staticFieldsWrittenOnlyInEnclosingStaticInitializer, lense::lookupField);
+    this.fieldAccessInfoCollection =
+        previous.fieldAccessInfoCollection.rewrittenWithLens(application, lense);
     this.pinnedItems = lense.rewriteReferencesConservatively(previous.pinnedItems);
     this.virtualInvokes =
         rewriteKeysConservativelyWhileMergingValues(
@@ -547,15 +526,13 @@
     this.instantiatedTypes = previous.instantiatedTypes;
     this.instantiatedLambdas = previous.instantiatedLambdas;
     this.targetedMethods = previous.targetedMethods;
+    this.targetedMethodsThatMustRemainNonAbstract =
+        previous.targetedMethodsThatMustRemainNonAbstract;
     this.bootstrapMethods = previous.bootstrapMethods;
     this.methodsTargetedByInvokeDynamic = previous.methodsTargetedByInvokeDynamic;
     this.virtualMethodsTargetedByInvokeDirect = previous.virtualMethodsTargetedByInvokeDirect;
     this.liveMethods = previous.liveMethods;
     this.fieldAccessInfoCollection = previous.fieldAccessInfoCollection;
-    this.instanceFieldsWrittenOnlyInEnclosingInstanceInitializers =
-        previous.instanceFieldsWrittenOnlyInEnclosingInstanceInitializers;
-    this.staticFieldsWrittenOnlyInEnclosingStaticInitializer =
-        previous.staticFieldsWrittenOnlyInEnclosingStaticInitializer;
     this.pinnedItems = previous.pinnedItems;
     this.mayHaveSideEffects = previous.mayHaveSideEffects;
     this.noSideEffects = previous.noSideEffects;
@@ -668,10 +645,6 @@
             info.clearWrites();
           }
         });
-    result.staticFieldsWrittenOnlyInEnclosingStaticInitializer =
-        filter(
-            staticFieldsWrittenOnlyInEnclosingStaticInitializer,
-            not(noLongerWrittenFields::contains));
     return result;
   }
 
@@ -785,16 +758,20 @@
     return false;
   }
 
-  public boolean isInstanceFieldWrittenOnlyInEnclosingInstanceInitializers(DexEncodedField field) {
-    assert checkIfObsolete();
-    assert isFieldWritten(field) : "Expected field `" + field.toSourceString() + "` to be written";
-    return instanceFieldsWrittenOnlyInEnclosingInstanceInitializers.contains(field.field);
-  }
-
   public boolean isStaticFieldWrittenOnlyInEnclosingStaticInitializer(DexEncodedField field) {
     assert checkIfObsolete();
     assert isFieldWritten(field) : "Expected field `" + field.toSourceString() + "` to be written";
-    return staticFieldsWrittenOnlyInEnclosingStaticInitializer.contains(field.field);
+    if (!isPinned(field.field)) {
+      DexEncodedMethod staticInitializer =
+          definitionFor(field.field.holder).asProgramClass().getClassInitializer();
+      if (staticInitializer != null) {
+        FieldAccessInfo fieldAccessInfo = fieldAccessInfoCollection.get(field.field);
+        return fieldAccessInfo != null
+            && fieldAccessInfo.isWritten()
+            && !fieldAccessInfo.isWrittenOutside(staticInitializer);
+      }
+    }
+    return false;
   }
 
   public boolean mayPropagateValueFor(DexReference reference) {
@@ -809,8 +786,7 @@
 
   private static <T extends PresortedComparable<T>> ImmutableSortedSet<T> rewriteItems(
       Set<T> original, Function<T, T> rewrite) {
-    ImmutableSortedSet.Builder<T> builder =
-        new ImmutableSortedSet.Builder<>(PresortedComparable::slowCompare);
+    Builder<T> builder = new Builder<>(PresortedComparable::slowCompare);
     for (T item : original) {
       builder.add(rewrite.apply(item));
     }
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 fe19510..400ce93 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -8,7 +8,6 @@
 import static com.android.tools.r8.naming.IdentifierNameStringUtils.isReflectionMethod;
 import static com.android.tools.r8.shaking.AnnotationRemover.shouldKeepAnnotation;
 import static com.android.tools.r8.shaking.EnqueuerUtils.toImmutableSortedMap;
-import static com.google.common.base.Predicates.not;
 
 import com.android.tools.r8.Diagnostic;
 import com.android.tools.r8.dex.IndexedItemCollection;
@@ -146,10 +145,6 @@
   private final Map<DexMethod, Set<DexEncodedMethod>> staticInvokes = new IdentityHashMap<>();
   private final FieldAccessInfoCollectionImpl fieldAccessInfoCollection =
       new FieldAccessInfoCollectionImpl();
-  private final Set<DexField> instanceFieldsWrittenOutsideEnclosingInstanceInitializers =
-      Sets.newIdentityHashSet();
-  private final Set<DexField> staticFieldsWrittenOutsideEnclosingStaticInitializer =
-      Sets.newIdentityHashSet();
   private final Set<DexCallSite> callSites = Sets.newIdentityHashSet();
 
   private final Set<DexReference> identifierNameStrings = Sets.newIdentityHashSet();
@@ -206,6 +201,10 @@
    * its implementation may be removed and it may be marked abstract.
    */
   private final SetWithReason<DexEncodedMethod> targetedMethods;
+
+  /** Subset of 'targetedMethods' for which the method must not be marked abstract. */
+  private final Set<DexEncodedMethod> targetedMethodsThatMustRemainNonAbstract;
+
   /**
    * Set of program methods that are used as the bootstrap method for an invoke-dynamic instruction.
    */
@@ -317,6 +316,10 @@
     liveAnnotations = new SetWithReason<>(graphReporter::registerAnnotation);
     instantiatedTypes = new SetWithReason<>(graphReporter::registerClass);
     targetedMethods = new SetWithReason<>(graphReporter::registerMethod);
+    // This set is only populated in edge cases due to multiple default interface methods.
+    // The set is generally expected to be empty and in the unlikely chance it is not, it will
+    // likely contain two methods. Thus the default capacity of 2.
+    targetedMethodsThatMustRemainNonAbstract = SetUtils.newIdentityHashSet(2);
     liveMethods = new LiveMethodsSet(graphReporter::registerMethod);
     liveFields = new SetWithReason<>(graphReporter::registerField);
     instantiatedInterfaceTypes = new SetWithReason<>(graphReporter::registerInterface);
@@ -375,18 +378,6 @@
     }
   }
 
-  private Set<DexField> instanceFieldsWrittenOnlyInEnclosingInstanceInitializers() {
-    Set<DexField> result = getNonPinnedWrittenFields(not(DexEncodedField::isStatic));
-    result.removeAll(instanceFieldsWrittenOutsideEnclosingInstanceInitializers);
-    return result;
-  }
-
-  private Set<DexField> staticFieldsWrittenOnlyInEnclosingStaticInitializer() {
-    Set<DexField> result = getNonPinnedWrittenFields(DexEncodedField::isStatic);
-    result.removeAll(staticFieldsWrittenOutsideEnclosingStaticInitializer);
-    return result;
-  }
-
   private Set<DexField> getNonPinnedWrittenFields(Predicate<DexEncodedField> predicate) {
     Set<DexField> result = Sets.newIdentityHashSet();
     fieldAccessInfoCollection.forEach(
@@ -960,14 +951,6 @@
       Log.verbose(getClass(), "Register Iput `%s`.", field);
     }
 
-    // If it is written outside of the <init>s of its enclosing class, record it.
-    boolean isWrittenOutsideEnclosingInstanceInitializers =
-        currentMethod.method.holder != encodedField.field.holder
-            || !currentMethod.isInstanceInitializer();
-    if (isWrittenOutsideEnclosingInstanceInitializers) {
-      instanceFieldsWrittenOutsideEnclosingInstanceInitializers.add(encodedField.field);
-    }
-
     // If unused interface removal is enabled, then we won't necessarily mark the actual holder of
     // the field as live, if the holder is an interface.
     if (appView.options().enableUnusedInterfaceRemoval) {
@@ -1062,14 +1045,6 @@
       }
     }
 
-    // If it is written outside of the <clinit> of its enclosing class, record it.
-    boolean isWrittenOutsideEnclosingStaticInitializer =
-        currentMethod.method.holder != encodedField.field.holder
-            || !currentMethod.isClassInitializer();
-    if (isWrittenOutsideEnclosingStaticInitializer) {
-      staticFieldsWrittenOutsideEnclosingStaticInitializer.add(encodedField.field);
-    }
-
     if (encodedField.field != field) {
       // Mark the non-rebound field access as targeted. Note that this should only be done if the
       // field is not a dead proto field (in which case we bail-out above).
@@ -2037,13 +2012,15 @@
 
   private MarkedResolutionTarget findAndMarkResolutionTarget(
       DexMethod method, boolean interfaceInvoke, KeepReason reason) {
-    DexEncodedMethod resolutionTarget =
-        appInfo.resolveMethod(method.holder, method, interfaceInvoke).asResultOfResolve();
-    if (resolutionTarget == null) {
-      reportMissingMethod(method);
+    ResolutionResult resolutionResult =
+        appInfo.resolveMethod(method.holder, method, interfaceInvoke);
+    if (!resolutionResult.hasSingleTarget()) {
+      // If the resolution fails, mark each dependency causing a failure.
+      markFailedResolutionTargets(resolutionResult, reason);
       return MarkedResolutionTarget.unresolved();
     }
 
+    DexEncodedMethod resolutionTarget = resolutionResult.getSingleTarget();
     DexClass resolutionTargetClass = appInfo.definitionFor(resolutionTarget.method.holder);
     if (resolutionTargetClass == null) {
       reportMissingClass(resolutionTarget.method.holder);
@@ -2065,6 +2042,17 @@
     return new MarkedResolutionTarget(resolutionTargetClass, resolutionTarget);
   }
 
+  private void markFailedResolutionTargets(ResolutionResult failedResolution, KeepReason reason) {
+    failedResolution.forEachTarget(
+        method -> {
+          DexProgramClass clazz = getProgramClassOrNull(method.method.holder);
+          if (clazz != null) {
+            targetedMethodsThatMustRemainNonAbstract.add(method);
+            markMethodAsTargeted(clazz, method, reason);
+          }
+        });
+  }
+
   private DexMethod generatedEnumValuesMethod(DexClass enumClass) {
     DexType arrayOfEnumClass =
         appView
@@ -2200,6 +2188,8 @@
             Collections.unmodifiableSet(instantiatedAppServices),
             SetUtils.mapIdentityHashSet(instantiatedTypes.getItems(), DexProgramClass::getType),
             Enqueuer.toSortedDescriptorSet(targetedMethods.getItems()),
+            SetUtils.mapIdentityHashSet(
+                targetedMethodsThatMustRemainNonAbstract, DexEncodedMethod::getKey),
             ImmutableSortedSet.copyOf(DexMethod::slowCompareTo, bootstrapMethods),
             ImmutableSortedSet.copyOf(DexMethod::slowCompareTo, methodsTargetedByInvokeDynamic),
             ImmutableSortedSet.copyOf(
@@ -2207,8 +2197,6 @@
             toSortedDescriptorSet(liveMethods.getItems()),
             // Filter out library fields and pinned fields, because these are read by default.
             fieldAccessInfoCollection,
-            instanceFieldsWrittenOnlyInEnclosingInstanceInitializers(),
-            staticFieldsWrittenOnlyInEnclosingStaticInitializer(),
             // TODO(b/132593519): Do we require these sets to be sorted for determinism?
             toImmutableSortedMap(virtualInvokes, PresortedComparable::slowCompare),
             toImmutableSortedMap(interfaceInvokes, PresortedComparable::slowCompare),
diff --git a/src/main/java/com/android/tools/r8/shaking/TreePruner.java b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
index b7e9923..af0efe5 100644
--- a/src/main/java/com/android/tools/r8/shaking/TreePruner.java
+++ b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
@@ -288,13 +288,15 @@
         }
         // Final classes cannot be abstract, so we have to keep the method in that case.
         // Also some other kinds of methods cannot be abstract, so keep them around.
-        boolean allowAbstract = clazz.accessFlags.isAbstract()
-            && !method.accessFlags.isFinal()
-            && !method.accessFlags.isNative()
-            && !method.accessFlags.isStrict()
-            && !method.accessFlags.isSynchronized()
-            && !method.accessFlags.isPrivate()
-            && !method.accessFlags.isStatic();
+        boolean allowAbstract =
+            clazz.accessFlags.isAbstract()
+                && !method.accessFlags.isFinal()
+                && !method.accessFlags.isNative()
+                && !method.accessFlags.isStrict()
+                && !method.accessFlags.isSynchronized()
+                && !method.accessFlags.isPrivate()
+                && !method.accessFlags.isStatic()
+                && !appInfo.targetedMethodsThatMustRemainNonAbstract.contains(method.method);
         // Private methods and static methods can only be targeted yet non-live as the result of
         // an invalid invoke. They will not actually be called at runtime but we have to keep them
         // as non-abstract (see above) to produce the same failure mode.
diff --git a/src/main/java/com/android/tools/r8/utils/FileUtils.java b/src/main/java/com/android/tools/r8/utils/FileUtils.java
index 7185445..24c35fb 100644
--- a/src/main/java/com/android/tools/r8/utils/FileUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/FileUtils.java
@@ -28,6 +28,7 @@
   public static final String JAR_EXTENSION = ".jar";
   public static final String ZIP_EXTENSION = ".zip";
   public static final String JAVA_EXTENSION = ".java";
+  public static final String KT_EXTENSION = ".kt";
   public static final String MODULE_INFO_CLASS = "module-info.class";
 
   public static final boolean isAndroid =
diff --git a/src/test/java/com/android/tools/r8/KotlinCompilerTestBuilder.java b/src/test/java/com/android/tools/r8/KotlinCompilerTestBuilder.java
new file mode 100644
index 0000000..7fa1ba1
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/KotlinCompilerTestBuilder.java
@@ -0,0 +1,90 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8;
+
+import com.android.tools.r8.R8Command.Builder;
+import com.android.tools.r8.TestBase.Backend;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.InternalOptions;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+public class KotlinCompilerTestBuilder
+    extends TestCompilerBuilder<
+        R8Command,
+        Builder,
+        KotlinTestCompileResult,
+        KotlinTestRunResult,
+        KotlinCompilerTestBuilder> {
+
+  private List<Path> ktPaths;
+  private List<Path> classPaths;
+
+  private KotlinCompilerTestBuilder(TestState state, Builder builder) {
+    super(state, builder, Backend.CF);
+  }
+
+  public static KotlinCompilerTestBuilder create(TestState state) {
+    return new KotlinCompilerTestBuilder(state, R8Command.builder());
+  }
+
+  @Override
+  KotlinCompilerTestBuilder self() {
+    return this;
+  }
+
+  @Override
+  KotlinTestCompileResult internalCompile(
+      Builder builder,
+      Consumer<InternalOptions> optionsConsumer,
+      Supplier<AndroidApp> app)
+      throws CompilationFailedException {
+    try {
+      Path outputFolder = getState().getNewTempFolder();
+      Path outputJar = outputFolder.resolve("output.jar");
+      ProcessResult processResult =
+          ToolHelper.runKotlinc(
+              null,
+              classPaths,
+              outputJar,
+              null, // extra options
+              ktPaths == null ? ToolHelper.EMPTY_PATH : ktPaths.toArray(ToolHelper.EMPTY_PATH)
+          );
+      return new KotlinTestCompileResult(getState(), outputJar, processResult);
+    } catch (IOException e) {
+      throw new CompilationFailedException(e);
+    }
+  }
+
+  @Override
+  public KotlinCompilerTestBuilder addClasspathClasses(Collection<Class<?>> classes) {
+    throw new Unimplemented("No support for adding classpath classes directly");
+  }
+
+  @Override
+  public KotlinCompilerTestBuilder addClasspathFiles(Collection<Path> files) {
+    if (classPaths == null) {
+      classPaths = new ArrayList<>();
+    }
+    classPaths.addAll(files);
+    return self();
+  }
+
+  public KotlinCompilerTestBuilder addSourceFiles(Path... files) {
+    if (ktPaths == null) {
+      ktPaths = new ArrayList<>();
+    }
+    ktPaths.addAll(Arrays.asList(files));
+    return self();
+  }
+
+}
diff --git a/src/test/java/com/android/tools/r8/KotlinTestBase.java b/src/test/java/com/android/tools/r8/KotlinTestBase.java
index a728201..87f946c 100644
--- a/src/test/java/com/android/tools/r8/KotlinTestBase.java
+++ b/src/test/java/com/android/tools/r8/KotlinTestBase.java
@@ -4,17 +4,20 @@
 package com.android.tools.r8;
 
 import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.graph.DexAnnotation;
+import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.utils.FileUtils;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 
 public abstract class KotlinTestBase extends TestBase {
 
-  public static final String checkParameterIsNotNullSignature =
+  protected  static final String checkParameterIsNotNullSignature =
       "void kotlin.jvm.internal.Intrinsics.checkParameterIsNotNull("
           + "java.lang.Object, java.lang.String)";
-  public static final String throwParameterIsNotNullExceptionSignature =
+  protected static final String throwParameterIsNotNullExceptionSignature =
       "void kotlin.jvm.internal.Intrinsics.throwParameterIsNullException(java.lang.String)";
+  protected static final String METADATA_DESCRIPTOR = "Lkotlin/Metadata;";
 
   private static final String RSRC = "kotlinR8TestResources";
 
@@ -24,6 +27,14 @@
     this.targetVersion = targetVersion;
   }
 
+  protected static Path getKotlinFileInTest(String folder, String fileName) {
+    return Paths.get(ToolHelper.TESTS_DIR, "java", folder, fileName + FileUtils.KT_EXTENSION);
+  }
+
+  protected static Path getKotlinFileInResource(String folder, String fileName) {
+    return Paths.get(ToolHelper.TESTS_DIR, RSRC, folder, fileName + FileUtils.KT_EXTENSION);
+  }
+
   protected Path getKotlinJarFile(String folder) {
     return Paths.get(ToolHelper.TESTS_BUILD_DIR, RSRC,
         targetVersion.getFolderName(), folder + FileUtils.JAR_EXTENSION);
@@ -38,4 +49,12 @@
     return Paths.get(ToolHelper.TESTS_DIR, RSRC, folder, mappingFileName);
   }
 
+  protected DexAnnotation retrieveMetadata(DexClass dexClass) {
+    for (DexAnnotation annotation : dexClass.annotations.annotations) {
+      if (annotation.annotation.type.toDescriptorString().equals(METADATA_DESCRIPTOR)) {
+        return annotation;
+      }
+    }
+    return null;
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/KotlinTestCompileResult.java b/src/test/java/com/android/tools/r8/KotlinTestCompileResult.java
new file mode 100644
index 0000000..550e72c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/KotlinTestCompileResult.java
@@ -0,0 +1,52 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8;
+
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.utils.AndroidApp;
+import java.nio.file.Path;
+
+public class KotlinTestCompileResult
+    extends TestCompileResult<KotlinTestCompileResult, KotlinTestRunResult> {
+
+  private final Path outputJar;
+  private final ProcessResult processResult;
+
+  KotlinTestCompileResult(TestState state, Path outputJar, ProcessResult processResult) {
+    super(state, AndroidApp.builder().addProgramFile(outputJar).build(), OutputMode.ClassFile);
+    this.outputJar = outputJar;
+    this.processResult = processResult;
+  }
+
+  public Path outputJar() {
+    return outputJar;
+  }
+
+  public int exitCode() {
+    return processResult.exitCode;
+  }
+
+  public String stdout() {
+    return processResult.stdout;
+  }
+
+  public String stderr() {
+    return processResult.stderr;
+  }
+
+  @Override
+  public KotlinTestCompileResult self() {
+    return this;
+  }
+
+  @Override
+  public TestDiagnosticMessages getDiagnosticMessages() {
+    throw new UnsupportedOperationException("No diagnostics messages from kotlinc");
+  }
+
+  @Override
+  public KotlinTestRunResult createRunResult(TestRuntime runtime, ProcessResult result) {
+    return new KotlinTestRunResult(app, runtime, result);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/KotlinTestRunResult.java b/src/test/java/com/android/tools/r8/KotlinTestRunResult.java
new file mode 100644
index 0000000..a4e413c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/KotlinTestRunResult.java
@@ -0,0 +1,19 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8;
+
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.utils.AndroidApp;
+
+public class KotlinTestRunResult extends TestRunResult<KotlinTestRunResult> {
+
+  KotlinTestRunResult(AndroidApp app, TestRuntime runtime, ProcessResult result) {
+    super(app, runtime, result);
+  }
+
+  @Override
+  protected KotlinTestRunResult self() {
+    return this;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index c927084..1fc315b 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -111,6 +111,10 @@
     return JvmTestBuilder.create(new TestState(temp));
   }
 
+  public static KotlinCompilerTestBuilder testForKotlin(TemporaryFolder temp) {
+    return KotlinCompilerTestBuilder.create(new TestState(temp));
+  }
+
   public static ProguardTestBuilder testForProguard(TemporaryFolder temp) {
     return ProguardTestBuilder.create(new TestState(temp));
   }
@@ -147,6 +151,10 @@
     return testForJvm(temp);
   }
 
+  public KotlinCompilerTestBuilder testForKotlin() {
+    return testForKotlin(temp);
+  }
+
   public TestBuilder<? extends TestRunResult<?>, ?> testForRuntime(
       TestRuntime runtime, AndroidApiLevel apiLevel) {
     if (runtime.isCf()) {
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 66de7d1..a84064a 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -80,6 +80,8 @@
 
 public class ToolHelper {
 
+  static final Path[] EMPTY_PATH = {};
+
   public static final String SOURCE_DIR = "src/main/java/";
   public static final String BUILD_DIR = "build/";
   public static final String GENERATED_TEST_BUILD_DIR = BUILD_DIR + "generated/test/";
@@ -126,6 +128,8 @@
   public static final String RHINO_ANDROID_JAR =
       "third_party/rhino-android-1.1.1/rhino-android-1.1.1.jar";
   public static final String RHINO_JAR = "third_party/rhino-1.7.10/rhino-1.7.10.jar";
+  static final String KT_PRELOADER = "third_party/kotlin/kotlinc/lib/kotlin-preloader.jar";
+  static final String KT_COMPILER = "third_party/kotlin/kotlinc/lib/kotlin-compiler.jar";
   public static final String KT_STDLIB = "third_party/kotlin/kotlinc/lib/kotlin-stdlib.jar";
   public static final String KT_REFLECT = "third_party/kotlin/kotlinc/lib/kotlin-reflect.jar";
   private static final String ANDROID_JAR_PATTERN = "third_party/android_jar/lib-v%d/android.jar";
@@ -1309,6 +1313,42 @@
     return ToolHelper.runProcess(builder);
   }
 
+  public static ProcessResult runKotlinc(
+      CfVm runtime,
+      List<Path> classPaths,
+      Path directoryToCompileInto,
+      List<String> extraOptions,
+      Path... filesToCompile)
+      throws IOException {
+    String jvm = runtime == null ? getSystemJavaExecutable() : getJavaExecutable(runtime);
+    List<String> cmdline = new ArrayList<String>(Arrays.asList(jvm));
+    cmdline.add("-jar");
+    cmdline.add(KT_PRELOADER);
+    cmdline.add("org.jetbrains.kotlin.preloading.Preloader");
+    cmdline.add("-cp");
+    cmdline.add(KT_COMPILER);
+    cmdline.add("org.jetbrains.kotlin.cli.jvm.K2JVMCompiler");
+    String[] strings = Arrays.stream(filesToCompile).map(Path::toString).toArray(String[]::new);
+    Collections.addAll(cmdline, strings);
+    cmdline.add("-d");
+    cmdline.add(directoryToCompileInto.toString());
+    List<String> cp = classPaths == null ? null
+        : classPaths.stream().map(Path::toString).collect(Collectors.toList());
+    if (cp != null) {
+      cmdline.add("-cp");
+      if (isWindows()) {
+        cmdline.add(String.join(";", cp));
+      } else {
+        cmdline.add(String.join(":", cp));
+      }
+    }
+    if (extraOptions != null) {
+      cmdline.addAll(extraOptions);
+    }
+    ProcessBuilder builder = new ProcessBuilder(cmdline);
+    return ToolHelper.runProcess(builder);
+  }
+
   public static ProcessResult runJava(
       CfVm runtime, List<String> vmArgs, List<Path> classpath, String... args) throws IOException {
     String cp =
diff --git a/src/test/java/com/android/tools/r8/internal/CompilationTestBase.java b/src/test/java/com/android/tools/r8/internal/CompilationTestBase.java
index 12fe936..3e9a3cb 100644
--- a/src/test/java/com/android/tools/r8/internal/CompilationTestBase.java
+++ b/src/test/java/com/android/tools/r8/internal/CompilationTestBase.java
@@ -20,7 +20,6 @@
 import com.android.tools.r8.ResourceException;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.ProcessResult;
 import com.android.tools.r8.naming.MemberNaming.FieldSignature;
 import com.android.tools.r8.naming.MemberNaming.MethodSignature;
 import com.android.tools.r8.utils.AndroidApiLevel;
@@ -37,7 +36,6 @@
 import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
 import com.android.tools.r8.utils.codeinspector.FoundFieldSubject;
 import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
-import com.google.common.collect.ImmutableList;
 import com.google.common.io.ByteStreams;
 import com.google.common.io.Closer;
 import java.io.File;
@@ -142,19 +140,13 @@
       if (pgConfs != null) {
         // Sanitize libraries for apps relying on the Proguard behaviour of lookup in program
         // classes before library classes. See tools/sanitize_libraries.py for more information.
-        Path sanitizedLibrary = temp.getRoot().toPath().resolve("sanitized_lib.jar");
-        Path sanitizedPgConf = temp.getRoot().toPath().resolve("sanitized.config");
-        List<String> command =
-            new ImmutableList.Builder<String>()
-                .add("tools/sanitize_libraries.py")
-                .add(sanitizedLibrary.toString())
-                .add(sanitizedPgConf.toString())
-                .addAll(pgConfs)
-                .build();
-        ProcessResult result = ToolHelper.runProcess(new ProcessBuilder(command));
-        assert result.exitCode == 0;
-        builder.addProguardConfigurationFiles(
-            pgConfs.stream().map(Paths::get).collect(Collectors.toList()));
+        LibrarySanitizer librarySanitizer =
+            new LibrarySanitizer(temp)
+                .addProguardConfigurationFiles(
+                    pgConfs.stream().map(Paths::get).collect(Collectors.toList()))
+                .sanitize();
+        builder.addLibraryFiles(librarySanitizer.getSanitizedLibrary());
+        builder.addProguardConfigurationFiles(librarySanitizer.getSanitizedProguardConfiguration());
       } else {
         builder.setDisableTreeShaking(true);
         builder.setDisableMinification(true);
diff --git a/src/test/java/com/android/tools/r8/internal/LibrarySanitizer.java b/src/test/java/com/android/tools/r8/internal/LibrarySanitizer.java
new file mode 100644
index 0000000..c4509c4
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/LibrarySanitizer.java
@@ -0,0 +1,86 @@
+// Copyright (c) 2019, 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.internal;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.utils.FileUtils;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.rules.TemporaryFolder;
+
+class LibrarySanitizer {
+
+  private final Path sanitizedLibrary;
+  private final Path sanitizedPgConf;
+
+  private final List<Path> libraryFiles = new ArrayList<>();
+  private final List<Path> programFiles = new ArrayList<>();
+  private final List<Path> proguardConfigurationFiles = new ArrayList<>();
+
+  LibrarySanitizer(TemporaryFolder temp) {
+    this.sanitizedLibrary = temp.getRoot().toPath().resolve("sanitized_lib.jar");
+    this.sanitizedPgConf = temp.getRoot().toPath().resolve("sanitized.config");
+  }
+
+  LibrarySanitizer assertSanitizedProguardConfigurationIsEmpty() throws IOException {
+    if (sanitizedPgConf.toFile().exists()) {
+      List<String> lines = FileUtils.readAllLines(sanitizedPgConf);
+      for (String line : lines) {
+        assertTrue(line.trim().isEmpty());
+      }
+    }
+    return this;
+  }
+
+  LibrarySanitizer addLibraryFiles(List<Path> libraryFiles) {
+    this.libraryFiles.addAll(libraryFiles);
+    return this;
+  }
+
+  LibrarySanitizer addProgramFiles(List<Path> programFiles) {
+    this.programFiles.addAll(programFiles);
+    return this;
+  }
+
+  LibrarySanitizer addProguardConfigurationFiles(List<Path> proguardConfigurationFiles) {
+    this.proguardConfigurationFiles.addAll(proguardConfigurationFiles);
+    return this;
+  }
+
+  Path getSanitizedLibrary() {
+    return sanitizedLibrary;
+  }
+
+  Path getSanitizedProguardConfiguration() {
+    return sanitizedPgConf;
+  }
+
+  LibrarySanitizer sanitize() throws IOException {
+    ImmutableList.Builder<String> command =
+        new ImmutableList.Builder<String>()
+            .add("tools/sanitize_libraries.py")
+            .add(sanitizedLibrary.toString())
+            .add(sanitizedPgConf.toString());
+    for (Path programFile : programFiles) {
+      command.add("--injar").add(programFile.toString());
+    }
+    for (Path libraryFile : libraryFiles) {
+      command.add("--libraryjar").add(libraryFile.toString());
+    }
+    for (Path proguardConfigurationFile : proguardConfigurationFiles) {
+      command.add("--pgconf").add(proguardConfigurationFile.toString());
+    }
+    ProcessResult result = ToolHelper.runProcess(new ProcessBuilder(command.build()));
+    assertEquals(result.command, 0, result.exitCode);
+    return this;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeCompilationBase.java b/src/test/java/com/android/tools/r8/internal/YouTubeCompilationBase.java
index 9fc88c8..df8bcb0 100644
--- a/src/test/java/com/android/tools/r8/internal/YouTubeCompilationBase.java
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeCompilationBase.java
@@ -34,6 +34,10 @@
         Paths.get(ToolHelper.PROGUARD_SETTINGS_FOR_INTERNAL_APPS).resolve(PG_CONF));
   }
 
+  protected List<Path> getLibraryFiles() {
+    return ImmutableList.of(Paths.get(base, "legacy_YouTubeRelease_combined_library_jars.jar"));
+  }
+
   protected List<Path> getProgramFiles() throws IOException {
     List<Path> result = new ArrayList<>();
     for (Path keepRuleFile : getKeepRuleFiles()) {
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeV1444TreeShakeJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/YouTubeV1444TreeShakeJarVerificationTest.java
index 016427f..49b41aa 100644
--- a/src/test/java/com/android/tools/r8/internal/YouTubeV1444TreeShakeJarVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeV1444TreeShakeJarVerificationTest.java
@@ -13,6 +13,7 @@
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.analysis.ProtoApplicationStats;
 import org.junit.Test;
@@ -43,8 +44,17 @@
     assumeTrue(isLocalDevelopment());
     assumeTrue(shouldRunSlowTests());
 
+    LibrarySanitizer librarySanitizer =
+        new LibrarySanitizer(temp)
+            .addProgramFiles(getProgramFiles())
+            .addLibraryFiles(getLibraryFiles())
+            .sanitize()
+            .assertSanitizedProguardConfigurationIsEmpty();
+
     R8TestCompileResult compileResult =
         testForR8(parameters.getBackend())
+            .addProgramFiles(getProgramFiles())
+            .addLibraryFiles(librarySanitizer.getSanitizedLibrary())
             .addKeepRuleFiles(getKeepRuleFiles())
             .addOptionsModification(
                 options -> {
@@ -63,6 +73,7 @@
                   assert !options.enableStringSwitchConversion;
                   options.enableStringSwitchConversion = true;
                 })
+            .setMinApi(AndroidApiLevel.H_MR2)
             .allowUnusedProguardConfigurationRules()
             .compile();
 
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/sync/InlineStaticSynchronizedMethodTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/sync/InlineStaticSynchronizedMethodTest.java
index a8578e3..8a606a7 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/sync/InlineStaticSynchronizedMethodTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/sync/InlineStaticSynchronizedMethodTest.java
@@ -30,7 +30,7 @@
 
   @Parameters(name = "{0}")
   public static TestParametersCollection data() {
-    return getTestParameters().withAllRuntimes().build();
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
   }
 
   public InlineStaticSynchronizedMethodTest(TestParameters parameters) {
@@ -42,7 +42,7 @@
     testForR8(parameters.getBackend())
         .addInnerClasses(InlineStaticSynchronizedMethodTest.class)
         .addKeepMainRule(TestClass.class)
-        .setMinApi(parameters.getRuntime())
+        .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(this::verifySynchronizedMethodsAreInlined)
         .run(parameters.getRuntime(), TestClass.class)
@@ -73,8 +73,8 @@
     }
 
     static void log(String message) {
-      logs.add(message);
       System.out.println(message);
+      logs.add(message);
     }
 
     static void waitUntil(BooleanSupplier condition) {
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameTest.java
new file mode 100644
index 0000000..160dc3f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameTest.java
@@ -0,0 +1,172 @@
+// Copyright (c) 2019, 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.metadata;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.KotlinTestBase;
+import com.android.tools.r8.KotlinTestCompileResult;
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.graph.DexAnnotation;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import java.nio.file.Path;
+import java.util.Collection;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MetadataRenameTest extends KotlinTestBase {
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0} target: {1}")
+  public static Collection<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
+  }
+
+  public MetadataRenameTest(TestParameters parameters, KotlinTargetVersion targetVersion) {
+    super(targetVersion);
+    this.parameters = parameters;
+  }
+
+  private static final String PKG_PREFIX =
+      DescriptorUtils.getBinaryNameFromJavaType(MetadataRenameTest.class.getPackage().getName());
+  private static Path supertypeLibJar;
+  private static Path extLibJar;
+
+  @BeforeClass
+  public static void createLibJar() throws Exception {
+    String supertypeLibFolder = PKG_PREFIX + "/supertype_lib";
+    supertypeLibJar = getStaticTemp().newFile("supertype_lib.jar").toPath();
+    ProcessResult processResult =
+        ToolHelper.runKotlinc(
+            null,
+            null,
+            supertypeLibJar,
+            null,
+            getKotlinFileInTest(supertypeLibFolder, "impl"),
+            getKotlinFileInTest(supertypeLibFolder + "/internal", "itf")
+        );
+    assertEquals(0, processResult.exitCode);
+
+    String extLibFolder = PKG_PREFIX + "/extension_lib";
+    extLibJar = getStaticTemp().newFile("ext_lib.jar").toPath();
+    processResult =
+        ToolHelper.runKotlinc(
+            null,
+            null,
+            extLibJar,
+            null,
+            getKotlinFileInTest(extLibFolder, "B")
+        );
+    assertEquals(0, processResult.exitCode);
+  }
+
+  @Test
+  public void b143687784() throws Exception {
+    assumeTrue(parameters.getRuntime().isCf());
+
+    R8TestCompileResult compileResult =
+        testForR8(parameters.getBackend())
+            .addProgramFiles(supertypeLibJar)
+            // Keep non-private members except for ones in `internal` definitions.
+            .addKeepRules("-keep public class !**.internal.**, * { !private *; }")
+            .addKeepAttributes("*Annotation*")
+            .compile();
+    String pkg = getClass().getPackage().getName();
+    final String itfClassName = pkg + ".supertype_lib.internal.Itf";
+    final String implClassName = pkg + ".supertype_lib.Impl";
+    compileResult.inspect(inspector -> {
+      ClassSubject itf = inspector.clazz(itfClassName);
+      assertThat(itf, not(isPresent()));
+
+      ClassSubject impl = inspector.clazz(implClassName);
+      assertThat(impl, isPresent());
+      assertThat(impl, not(isRenamed()));
+      // API entry is kept, hence the presence of Metadata.
+      DexAnnotation metadata = retrieveMetadata(impl.getDexClass());
+      assertNotNull(metadata);
+      // TODO(b/143687784): test its metadata doesn't point to shrunken itf.
+    });
+
+    Path r8ProcessedLibZip = temp.newFile("r8-lib.zip").toPath();
+    compileResult.writeToZip(r8ProcessedLibZip);
+
+    String appFolder = PKG_PREFIX + "/supertype_app";
+    KotlinTestCompileResult kotlinTestCompileResult =
+        testForKotlin()
+            .addClasspathFiles(r8ProcessedLibZip)
+            .addSourceFiles(getKotlinFileInTest(appFolder, "main"))
+            .compile();
+    // TODO(b/143687784): should be able to compile!
+    assertNotEquals(0, kotlinTestCompileResult.exitCode());
+    assertThat(
+        kotlinTestCompileResult.stderr(),
+        containsString("unresolved supertypes: " + pkg + ".supertype_lib.internal.Itf"));
+  }
+
+  @Test
+  public void testMetadataInExtension() throws Exception {
+    assumeTrue(parameters.getRuntime().isCf());
+
+    R8TestCompileResult compileResult =
+        testForR8(parameters.getBackend())
+            .addProgramFiles(extLibJar)
+            // Keep the B class and its interface (which has the doStuff method).
+            .addKeepRules("-keep class **.B")
+            .addKeepRules("-keep class **.I { <methods>; }")
+            // Keep the BKt extension method which requires metadata
+            // to be called with Kotlin syntax from other kotlin code.
+            .addKeepRules("-keep class **.BKt { <methods>; }")
+            .addKeepAttributes("*Annotation*")
+            .compile();
+    String pkg = getClass().getPackage().getName();
+    final String superClassName = pkg + ".extension_lib.Super";
+    final String bClassName = pkg + ".extension_lib.B";
+    compileResult.inspect(inspector -> {
+      ClassSubject sup = inspector.clazz(superClassName);
+      assertThat(sup, not(isPresent()));
+
+      ClassSubject impl = inspector.clazz(bClassName);
+      assertThat(impl, isPresent());
+      assertThat(impl, not(isRenamed()));
+      // API entry is kept, hence the presence of Metadata.
+      DexAnnotation metadata = retrieveMetadata(impl.getDexClass());
+      assertNotNull(metadata);
+      // TODO(b/143687784): test its metadata doesn't point to shrunken Super.
+    });
+
+    Path r8ProcessedLibZip = temp.newFile("r8-lib.zip").toPath();
+    compileResult.writeToZip(r8ProcessedLibZip);
+
+    String appFolder = PKG_PREFIX + "/extension_app";
+    KotlinTestCompileResult kotlinTestCompileResult =
+        testForKotlin()
+            .addClasspathFiles(r8ProcessedLibZip)
+            .addSourceFiles(getKotlinFileInTest(appFolder, "main"))
+            .compile();
+    // TODO(b/143687784): should be able to compile!
+    assertNotEquals(0, kotlinTestCompileResult.exitCode());
+    assertThat(
+        kotlinTestCompileResult.stderr(),
+        containsString("unresolved supertypes: " + pkg + ".extension_lib.Super"));
+    assertThat(kotlinTestCompileResult.stderr(), containsString("unresolved reference: doStuff"));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/MetadataStripTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
similarity index 80%
rename from src/test/java/com/android/tools/r8/kotlin/MetadataStripTest.java
rename to src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
index 204901f..155c91c 100644
--- a/src/test/java/com/android/tools/r8/kotlin/MetadataStripTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
@@ -1,7 +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 com.android.tools.r8.kotlin;
+package com.android.tools.r8.kotlin.metadata;
 
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
@@ -26,15 +26,13 @@
 
 @RunWith(Parameterized.class)
 public class MetadataStripTest extends KotlinTestBase {
-  private static final String METADATA_DESCRIPTOR = "Lkotlin/Metadata;";
-  private static final String KEEP_ANNOTATIONS = "-keepattributes *Annotation*";
 
   private final TestParameters parameters;
 
   @Parameterized.Parameters(name = "{0} target: {1}")
   public static Collection<Object[]> data() {
     return buildParameters(
-        getTestParameters().withAllRuntimes().build(), KotlinTargetVersion.values());
+        getTestParameters().withAllRuntimesAndApiLevels().build(), KotlinTargetVersion.values());
   }
 
   public MetadataStripTest(TestParameters parameters, KotlinTargetVersion targetVersion) {
@@ -53,9 +51,9 @@
             .addProgramFiles(getJavaJarFile(folder))
             .addProgramFiles(ToolHelper.getKotlinReflectJar())
             .addKeepMainRule(mainClassName)
-            .addKeepRules(KEEP_ANNOTATIONS)
+            .addKeepAttributes("*Annotation*")
             .addKeepRules("-keep class kotlin.Metadata")
-            .setMinApi(parameters.getRuntime())
+            .setMinApi(parameters.getApiLevel())
             .run(parameters.getRuntime(), mainClassName);
     CodeInspector inspector = result.inspector();
     ClassSubject clazz = inspector.clazz(mainClassName);
@@ -70,13 +68,4 @@
     assertNull(retrieveMetadata(impl1.getDexClass()));
   }
 
-  private DexAnnotation retrieveMetadata(DexClass dexClass) {
-    for (DexAnnotation annotation : dexClass.annotations.annotations) {
-      if (annotation.annotation.type.toDescriptorString().equals(METADATA_DESCRIPTOR)) {
-        return annotation;
-      }
-    }
-    return null;
-  }
-
 }
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/extension_app/main.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/extension_app/main.kt
new file mode 100644
index 0000000..246d3fe
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/extension_app/main.kt
@@ -0,0 +1,12 @@
+// Copyright (c) 2019, 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.metadata.extension_app
+
+import com.android.tools.r8.kotlin.metadata.extension_lib.B
+import com.android.tools.r8.kotlin.metadata.extension_lib.extension
+
+fun main() {
+  B().doStuff()
+  B().extension()
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/extension_lib/B.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/extension_lib/B.kt
new file mode 100644
index 0000000..a6fe9ab
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/extension_lib/B.kt
@@ -0,0 +1,20 @@
+// Copyright (c) 2019, 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.metadata.extension_lib
+
+interface I {
+  fun doStuff()
+}
+
+open class Super : I {
+  override fun doStuff() {
+    println("do stuff")
+  }
+}
+
+class B : Super() { }
+
+fun B.extension() {
+  doStuff()
+}
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/supertype_app/main.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/supertype_app/main.kt
new file mode 100644
index 0000000..4192774
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/supertype_app/main.kt
@@ -0,0 +1,17 @@
+// Copyright (c) 2019, 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.metadata.supertype_app
+
+import com.android.tools.r8.kotlin.metadata.supertype_lib.Impl
+
+class ProgramClass : Impl() {
+  override fun foo() {
+    super.foo()
+    println("Program::foo")
+  }
+}
+
+fun main(args: Array<String>) {
+  ProgramClass().foo()
+}
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/supertype_lib/impl.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/supertype_lib/impl.kt
new file mode 100644
index 0000000..448cb10
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/supertype_lib/impl.kt
@@ -0,0 +1,12 @@
+// Copyright (c) 2019, 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.metadata.supertype_lib
+
+import com.android.tools.r8.kotlin.metadata.supertype_lib.internal.Itf
+
+open class Impl : Itf {
+  override fun foo() {
+    println("Impl::foo")
+  }
+}
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/supertype_lib/internal/itf.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/supertype_lib/internal/itf.kt
new file mode 100644
index 0000000..50e18d4
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/supertype_lib/internal/itf.kt
@@ -0,0 +1,8 @@
+// Copyright (c) 2019, 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.metadata.supertype_lib.internal
+
+interface Itf {
+  fun foo();
+}
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAbstractLeftTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAbstractLeftTest.java
index cddbcfe..38e564f 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAbstractLeftTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAbstractLeftTest.java
@@ -18,6 +18,7 @@
 import com.android.tools.r8.graph.ResolutionResult;
 import com.android.tools.r8.resolution.SingleTargetLookupTest;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.google.common.collect.ImmutableList;
 import java.util.Collection;
@@ -63,8 +64,7 @@
     ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
     List<DexEncodedMethod> resolutionTargets = resolutionResult.asListOfTargets();
     assertEquals(1, resolutionTargets.size());
-    // TODO(b/144085169): This should resolve to L::f as T::f is not maximally specific.
-    assertEquals(T.class.getTypeName(), resolutionTargets.get(0).method.holder.toSourceString());
+    assertEquals(L.class.getTypeName(), resolutionTargets.get(0).method.holder.toSourceString());
   }
 
   @Test
@@ -73,7 +73,7 @@
         .addProgramClasses(CLASSES)
         .addProgramClassFileData(DumpB.dump())
         .run(parameters.getRuntime(), Main.class)
-        .assertFailureWithErrorThatMatches(getExpectedErrorMatcher());
+        .assertFailureWithErrorThatMatches(getExpectedErrorMatcher(false));
   }
 
   @Test
@@ -84,11 +84,15 @@
         .addKeepMainRule(Main.class)
         .setMinApi(parameters.getApiLevel())
         .run(parameters.getRuntime(), Main.class)
-        // TODO(b/144085169): R8 should not cause the code to no longer fail.
-        .assertSuccessWithOutputLines("T::f");
+        .assertFailureWithErrorThatMatches(getExpectedErrorMatcher(true));
   }
 
-  private Matcher<String> getExpectedErrorMatcher() {
+  private Matcher<String> getExpectedErrorMatcher(boolean isR8) {
+    if (isR8
+        && (parameters.isCfRuntime() || parameters.getApiLevel().isLessThan(AndroidApiLevel.L))) {
+      // TODO(b/144085169): R8 replaces the entire main method by 'throw null', why?
+      return containsString("NullPointerException");
+    }
     if (parameters.isDexRuntime()
         && parameters
             .getRuntime()
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAbstractRightTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAbstractRightTest.java
index 2f71762..1601394 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAbstractRightTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAbstractRightTest.java
@@ -18,6 +18,7 @@
 import com.android.tools.r8.graph.ResolutionResult;
 import com.android.tools.r8.resolution.SingleTargetLookupTest;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.google.common.collect.ImmutableList;
 import java.util.Collection;
@@ -63,8 +64,7 @@
     ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
     List<DexEncodedMethod> resolutionTargets = resolutionResult.asListOfTargets();
     assertEquals(1, resolutionTargets.size());
-    // TODO(b/144085169): This should resolve to R::f as T::f is not maximally specific.
-    assertEquals(T.class.getTypeName(), resolutionTargets.get(0).method.holder.toSourceString());
+    assertEquals(R.class.getTypeName(), resolutionTargets.get(0).method.holder.toSourceString());
   }
 
   @Test
@@ -73,7 +73,7 @@
         .addProgramClasses(CLASSES)
         .addProgramClassFileData(DumpB.dump())
         .run(parameters.getRuntime(), Main.class)
-        .assertFailureWithErrorThatMatches(getExpectedErrorMatcher());
+        .assertFailureWithErrorThatMatches(getExpectedErrorMatcher(false));
   }
 
   @Test
@@ -84,11 +84,15 @@
         .addKeepMainRule(Main.class)
         .setMinApi(parameters.getApiLevel())
         .run(parameters.getRuntime(), Main.class)
-        // TODO(b/144085169): R8 should not cause the code to no longer fail.
-        .assertSuccessWithOutputLines("T::f");
+        .assertFailureWithErrorThatMatches(getExpectedErrorMatcher(true));
   }
 
-  private Matcher<String> getExpectedErrorMatcher() {
+  private Matcher<String> getExpectedErrorMatcher(boolean isR8) {
+    if (isR8
+        && (parameters.isCfRuntime() || parameters.getApiLevel().isLessThan(AndroidApiLevel.L))) {
+      // TODO(b/144085169): R8 replaces the entire main method by 'throw null', why?
+      return containsString("NullPointerException");
+    }
     if (parameters.isDexRuntime()
         && parameters
             .getRuntime()
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndLeftTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndLeftTest.java
index 03a8e2b..a860e90 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndLeftTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndLeftTest.java
@@ -4,7 +4,6 @@
 package com.android.tools.r8.resolution.interfacediamonds;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
 import static org.junit.Assume.assumeTrue;
 
 import com.android.tools.r8.TestBase;
@@ -50,11 +49,8 @@
     DexMethod method = SingleTargetLookupTest.buildMethod(B.class, "f", appInfo);
     ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
     List<DexEncodedMethod> resolutionTargets = resolutionResult.asListOfTargets();
-    // TODO(b/144085169): The resolution should not include T::f as it is not maximally specific.
-    assertEquals(2 /* Should be 1 */, resolutionTargets.size());
-    assertTrue(
-        resolutionTargets.stream()
-            .anyMatch(m -> m.method.holder.toSourceString().equals(L.class.getTypeName())));
+    assertEquals(1, resolutionTargets.size());
+    assertEquals(L.class.getTypeName(), resolutionTargets.get(0).method.holder.toSourceString());
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndRightTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndRightTest.java
index fe96ebc..8e9b843 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndRightTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndRightTest.java
@@ -4,7 +4,6 @@
 package com.android.tools.r8.resolution.interfacediamonds;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
 import static org.junit.Assume.assumeTrue;
 
 import com.android.tools.r8.TestBase;
@@ -50,11 +49,8 @@
     DexMethod method = SingleTargetLookupTest.buildMethod(B.class, "f", appInfo);
     ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
     List<DexEncodedMethod> resolutionTargets = resolutionResult.asListOfTargets();
-    // TODO(b/144085169): The resolution should not include T::f as it is not maximally specific.
-    assertEquals(2 /* Should be 1 */, resolutionTargets.size());
-    assertTrue(
-        resolutionTargets.stream()
-            .anyMatch(m -> m.method.holder.toSourceString().equals(R.class.getTypeName())));
+    assertEquals(1, resolutionTargets.size());
+    assertEquals(R.class.getTypeName(), resolutionTargets.get(0).method.holder.toSourceString());
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/TwoDefaultMethodsWithoutTopTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/TwoDefaultMethodsWithoutTopTest.java
new file mode 100644
index 0000000..ae678cd
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/TwoDefaultMethodsWithoutTopTest.java
@@ -0,0 +1,181 @@
+// Copyright (c) 2019, 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.interfacediamonds;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRunResult;
+import com.android.tools.r8.TestRuntime;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.resolution.SingleTargetLookupTest;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.Collections;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.util.ASMifier;
+
+@RunWith(Parameterized.class)
+public class TwoDefaultMethodsWithoutTopTest extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  public TwoDefaultMethodsWithoutTopTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  private static final List<Class<?>> CLASSES =
+      ImmutableList.of(I.class, J.class, A.class, Main.class);
+
+  @Test
+  public void testResolution() throws Exception {
+    // The resolution is runtime independent, so just run it on the default CF VM.
+    assumeTrue(parameters.getRuntime().equals(TestRuntime.getDefaultJavaRuntime()));
+    AppInfoWithLiveness appInfo =
+        SingleTargetLookupTest.createAppInfoWithLiveness(
+            buildClasses(CLASSES, Collections.emptyList())
+                .addClassProgramData(Collections.singletonList(DumpB.dump()))
+                .build(),
+            Main.class);
+    DexMethod method = SingleTargetLookupTest.buildMethod(B.class, "f", appInfo);
+    ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
+    List<DexEncodedMethod> resolutionTargets = resolutionResult.asListOfTargets();
+    assertEquals(2, resolutionTargets.size());
+    assertTrue(
+        resolutionTargets.stream()
+            .anyMatch(m -> m.method.holder.toSourceString().equals(I.class.getTypeName())));
+    assertTrue(
+        resolutionTargets.stream()
+            .anyMatch(m -> m.method.holder.toSourceString().equals(J.class.getTypeName())));
+  }
+
+  @Test
+  public void testReference() throws Exception {
+    testForRuntime(parameters)
+        .addProgramClasses(CLASSES)
+        .addProgramClassFileData(DumpB.dump())
+        .run(parameters.getRuntime(), Main.class)
+        .apply(r -> checkResult(r, false));
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramClasses(CLASSES)
+        .addProgramClassFileData(DumpB.dump())
+        .addKeepMainRule(Main.class)
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), Main.class)
+        .apply(r -> checkResult(r, true));
+  }
+
+  private void checkResult(TestRunResult<?> runResult, boolean isR8) {
+    // TODO(b/144085169): JDK 11 execution produces a different error condition on the R8 output?
+    if (isR8
+        && parameters.getRuntime().isCf()
+        && parameters.getRuntime().asCf().getVm() == CfVm.JDK11) {
+      runResult.assertFailureWithErrorThatMatches(containsString("AbstractMethodError"));
+    } else if (parameters.isDexRuntime()
+        && parameters.getApiLevel().isLessThan(AndroidApiLevel.N)) {
+      if (isR8) {
+        // TODO(b/144085169): Maybe R8 introduces another error due to removal of targets?
+        runResult.assertFailureWithErrorThatMatches(containsString("AbstractMethodError"));
+      } else {
+        // TODO(b/72208584): Desugare changes error result.
+        runResult.assertSuccessWithOutputLines("I::f");
+      }
+    } else {
+      runResult.assertFailureWithErrorThatMatches(containsString("IncompatibleClassChangeError"));
+    }
+  }
+
+  public interface I {
+    default void f() {
+      System.out.println("I::f");
+    }
+  }
+
+  public interface J {
+    default void f() {
+      System.out.println("J::f");
+    }
+  }
+
+  public static class A implements I {}
+
+  public static class B extends A /* implements J via ASM */ {
+    // Intentionally empty.
+  }
+
+  static class Main {
+    public static void main(String[] args) {
+      new B().f();
+    }
+  }
+
+  private static class DumpB implements Opcodes {
+
+    public static void main(String[] args) throws Exception {
+      ASMifier.main(
+          new String[] {"-debug", ToolHelper.getClassFileForTestClass(B.class).toString()});
+    }
+
+    public static byte[] dump() {
+
+      ClassWriter classWriter = new ClassWriter(0);
+      MethodVisitor methodVisitor;
+
+      classWriter.visit(
+          V1_8,
+          ACC_PUBLIC | ACC_SUPER,
+          DescriptorUtils.getBinaryNameFromJavaType(B.class.getTypeName()),
+          null,
+          DescriptorUtils.getBinaryNameFromJavaType(A.class.getTypeName()),
+          new String[] {
+            // Manually added 'implements J'.
+            DescriptorUtils.getBinaryNameFromJavaType(J.class.getTypeName()),
+          });
+
+      {
+        methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
+        methodVisitor.visitCode();
+        methodVisitor.visitVarInsn(ALOAD, 0);
+        methodVisitor.visitMethodInsn(
+            INVOKESPECIAL,
+            DescriptorUtils.getBinaryNameFromJavaType(A.class.getTypeName()),
+            "<init>",
+            "()V",
+            false);
+        methodVisitor.visitInsn(RETURN);
+        methodVisitor.visitMaxs(1, 1);
+        methodVisitor.visitEnd();
+      }
+      classWriter.visitEnd();
+
+      return classWriter.toByteArray();
+    }
+  }
+}
diff --git a/tools/r8_release.py b/tools/r8_release.py
index cf1125d..f312919 100755
--- a/tools/r8_release.py
+++ b/tools/r8_release.py
@@ -15,7 +15,7 @@
 import update_prebuilds_in_android
 import utils
 
-R8_DEV_BRANCH = '1.7'
+R8_DEV_BRANCH = '2.0'
 R8_VERSION_FILE = os.path.join(
     'src', 'main', 'java', 'com', 'android', 'tools', 'r8', 'Version.java')
 THIS_FILE_RELATIVE = os.path.join('tools', 'r8_release.py')
diff --git a/tools/sanitize_libraries.py b/tools/sanitize_libraries.py
index e3e88ce..8ca54a7 100755
--- a/tools/sanitize_libraries.py
+++ b/tools/sanitize_libraries.py
@@ -14,12 +14,14 @@
 # classes by creating a new library jar which have all the provided library
 # classes which are not also in program classes.
 def SanitizeLibrariesInPgconf(
-    sanitized_lib_path, sanitized_pgconf_path, pgconfs):
-
-  injars = []
-  libraryjars = []
-
+    sanitized_lib_path,
+    sanitized_pgconf_path,
+    pgconfs,
+    injars = None,
+    libraryjars = None):
   with open(sanitized_pgconf_path, 'w') as sanitized_pgconf:
+    injars = [] if injars is None else injars
+    libraryjars = [] if libraryjars is None else libraryjars
     for pgconf in pgconfs:
       pgconf_dirname = os.path.abspath(os.path.dirname(pgconf))
       first_library_jar = True
@@ -49,7 +51,6 @@
 
 
 def SanitizeLibraries(sanitized_lib_path, libraryjars, injars):
-
   program_entries = set()
   library_entries = set()
 
@@ -68,14 +69,40 @@
            output_zf.writestr(zipinfo, input_zf.read(zipinfo))
 
 
+def usage(argv, error):
+  print(error)
+  print("Usage: sanitize_libraries.py <sanitized_lib> <sanitized_pgconf> ("
+        + "--injar <existing_injar>"
+        + "|--libraryjar <existing_library_jar>"
+        + "|--pgconf <existing_pgconf>)+")
+  return 1
+
+
 def main(argv):
-  if (len(argv) < 3):
-    print("Wrong number of arguments!")
-    print("Usage: sanitize_libraries.py " +
-          "<sanitized_lib> <sanitized_pgconf> (<existing_pgconf)+")
-    return 1
-  else:
-    SanitizeLibrariesInPgconf(argv[0], argv[1], argv[2:])
+  if (len(argv) < 4):
+    return usage(argv, "Wrong number of arguments!")
+  pgconfs = []
+  injars = []
+  libraryjars = []
+  i = 2
+  while i < len(argv):
+    directive = argv[i]
+    if directive not in ['--pgconf', '--injar', '--libraryjar']:
+      return usage(
+          argv,
+          'Unexpected argument, expected one of --pgconf, --injar, and '
+              + '--libraryjar.')
+    if i + 1 >= len(argv):
+      return usage(argv, 'Expected argument after ' + directive + '.')
+    file = argv[i + 1]
+    if directive == '--pgconf':
+      pgconfs.append(file)
+    elif directive == '--injar':
+      injars.append(file)
+    elif directive == '--libraryjar':
+      libraryjars.append(file)
+    i = i + 2
+  SanitizeLibrariesInPgconf(argv[0], argv[1], pgconfs, injars, libraryjars)
 
 if __name__ == '__main__':
   sys.exit(main(sys.argv[1:]))