Merge "Add script for creating maven package."
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 67add9c..4c30d36 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -160,6 +160,12 @@
    */
   private final Map<DexType, Set<DexAnnotation>> deferredAnnotations = new IdentityHashMap<>();
 
+  /**
+   * Methods that have been seen as not reachable due to related classes not being instantiated.
+   * As more instantiated types are collected this set needs to be re-visited.
+   */
+  private List<DexEncodedMethod> pendingAdditionalInstantiatedTypes = new ArrayList<>();
+
   public Enqueuer(AppInfoWithSubtyping appInfo, InternalOptions options) {
     this.appInfo = appInfo;
     this.options = options;
@@ -679,33 +685,39 @@
       SetWithReason<DexEncodedMethod> reachable = reachableVirtualMethods
           .computeIfAbsent(encodedMethod.method.holder, (ignore) -> new SetWithReason<>());
       if (reachable.add(encodedMethod, reason)) {
-        // If the holder type is instantiated, the method is live. Otherwise check whether we find
-        // a subtype that does not shadow this methods but is instantiated.
-        // Note that library classes are always considered instantiated, as we do not know where
-        // they are instantiated.
-        if (isInstantiatedOrHasInstantiatedSubtype(encodedMethod.method.holder)) {
-          if (instantiatedTypes.contains(encodedMethod.method.holder)) {
-            markVirtualMethodAsLive(encodedMethod,
-                KeepReason.reachableFromLiveType(encodedMethod.method.holder));
-          } else {
-            Deque<DexType> worklist = new ArrayDeque<>();
-            fillWorkList(worklist, encodedMethod.method.holder);
-            while (!worklist.isEmpty()) {
-              DexType current = worklist.pollFirst();
-              DexClass currentHolder = appInfo.definitionFor(current);
-              if (currentHolder == null
-                  || currentHolder.findVirtualTarget(encodedMethod.method) != null) {
-                continue;
-              }
-              if (instantiatedTypes.contains(current)) {
-                markVirtualMethodAsLive(encodedMethod, KeepReason.reachableFromLiveType(current));
-                break;
-              }
-              fillWorkList(worklist, current);
-            }
+        handleIsInstantiatedOrHasInstantiatedSubtype(encodedMethod);
+      }
+    }
+  }
+
+  private void handleIsInstantiatedOrHasInstantiatedSubtype(DexEncodedMethod encodedMethod) {
+    // If the holder type is instantiated, the method is live. Otherwise check whether we find
+    // a subtype that does not shadow this methods but is instantiated.
+    // Note that library classes are always considered instantiated, as we do not know where
+    // they are instantiated.
+    if (isInstantiatedOrHasInstantiatedSubtype(encodedMethod.method.holder)) {
+      if (instantiatedTypes.contains(encodedMethod.method.holder)) {
+        markVirtualMethodAsLive(encodedMethod,
+            KeepReason.reachableFromLiveType(encodedMethod.method.holder));
+      } else {
+        Deque<DexType> worklist = new ArrayDeque<>();
+        fillWorkList(worklist, encodedMethod.method.holder);
+        while (!worklist.isEmpty()) {
+          DexType current = worklist.pollFirst();
+          DexClass currentHolder = appInfo.definitionFor(current);
+          if (currentHolder == null
+              || currentHolder.findVirtualTarget(encodedMethod.method) != null) {
+            continue;
           }
+          if (instantiatedTypes.contains(current)) {
+            markVirtualMethodAsLive(encodedMethod, KeepReason.reachableFromLiveType(current));
+            break;
+          }
+          fillWorkList(worklist, current);
         }
       }
+    } else {
+      pendingAdditionalInstantiatedTypes.add(encodedMethod);
     }
   }
 
@@ -778,38 +790,22 @@
   private AppInfoWithLiveness trace(Timing timing) {
     timing.begin("Grow the tree.");
     try {
-      while (!workList.isEmpty()) {
-        Action action = workList.poll();
-        switch (action.kind) {
-          case MARK_INSTANTIATED:
-            processNewlyInstantiatedClass((DexClass) action.target, action.reason);
-            break;
-          case MARK_REACHABLE_FIELD:
-            markFieldAsReachable((DexField) action.target, action.reason);
-            break;
-          case MARK_REACHABLE_VIRTUAL:
-            markVirtualMethodAsReachable((DexMethod) action.target, false, action.reason);
-            break;
-          case MARK_REACHABLE_INTERFACE:
-            markVirtualMethodAsReachable((DexMethod) action.target, true, action.reason);
-            break;
-          case MARK_REACHABLE_SUPER:
-            markSuperMethodAsReachable((DexMethod) action.target,
-                (DexEncodedMethod) action.context);
-            break;
-          case MARK_METHOD_KEPT:
-            markMethodAsKept((DexEncodedMethod) action.target, action.reason);
-            break;
-          case MARK_FIELD_KEPT:
-            markFieldAsKept((DexEncodedField) action.target, action.reason);
-            break;
-          case MARK_METHOD_LIVE:
-            processNewlyLiveMethod(((DexEncodedMethod) action.target), action.reason);
-            break;
-          default:
-            throw new IllegalArgumentException(action.kind.toString());
+      int instantiatedTypesCount = 0;
+      while (true) {
+        doTrace();
+        // If methods where not considered due to relevant types not being instantiated reconsider
+        // if more instantiated types where collected.
+        if (pendingAdditionalInstantiatedTypes.size() > 0
+            && instantiatedTypes.items.size() > instantiatedTypesCount) {
+          instantiatedTypesCount = instantiatedTypes.items.size();
+          List<DexEncodedMethod> reconsider = pendingAdditionalInstantiatedTypes;
+          pendingAdditionalInstantiatedTypes = new ArrayList<>();
+          reconsider.forEach(this::handleIsInstantiatedOrHasInstantiatedSubtype);
+        } else {
+          break;
         }
       }
+
       if (Log.ENABLED) {
         Set<DexEncodedMethod> allLive = Sets.newIdentityHashSet();
         for (Entry<DexType, SetWithReason<DexEncodedMethod>> entry : reachableVirtualMethods
@@ -837,6 +833,41 @@
     return new AppInfoWithLiveness(appInfo, this);
   }
 
+  private void doTrace() {
+    while (!workList.isEmpty()) {
+      Action action = workList.poll();
+      switch (action.kind) {
+        case MARK_INSTANTIATED:
+          processNewlyInstantiatedClass((DexClass) action.target, action.reason);
+          break;
+        case MARK_REACHABLE_FIELD:
+          markFieldAsReachable((DexField) action.target, action.reason);
+          break;
+        case MARK_REACHABLE_VIRTUAL:
+          markVirtualMethodAsReachable((DexMethod) action.target, false, action.reason);
+          break;
+        case MARK_REACHABLE_INTERFACE:
+          markVirtualMethodAsReachable((DexMethod) action.target, true, action.reason);
+          break;
+        case MARK_REACHABLE_SUPER:
+          markSuperMethodAsReachable((DexMethod) action.target,
+              (DexEncodedMethod) action.context);
+          break;
+        case MARK_METHOD_KEPT:
+          markMethodAsKept((DexEncodedMethod) action.target, action.reason);
+          break;
+        case MARK_FIELD_KEPT:
+          markFieldAsKept((DexEncodedField) action.target, action.reason);
+          break;
+        case MARK_METHOD_LIVE:
+          processNewlyLiveMethod(((DexEncodedMethod) action.target), action.reason);
+          break;
+        default:
+          throw new IllegalArgumentException(action.kind.toString());
+      }
+    }
+  }
+
   private void markMethodAsKept(DexEncodedMethod target, KeepReason reason) {
     DexClass holder = appInfo.definitionFor(target.method.holder);
     // If this method no longer has a corresponding class then we have shaken it away before.