Merge "Util to find all interfaces implemented in the current type."
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 7daef42..6cbad63 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfo.java
@@ -481,13 +481,14 @@
     return result;
   }
 
-  public boolean canTriggerStaticInitializer(DexType clazz) {
+  public boolean canTriggerStaticInitializer(DexType type) {
     Set<DexType> knownInterfaces = Sets.newIdentityHashSet();
 
     // Process superclass chain.
+    DexType clazz = type;
     while (clazz != null && clazz != dexItemFactory.objectType) {
       DexClass definition = definitionFor(clazz);
-      if (definition == null || definition.hasClassInitializer()) {
+      if (canTriggerStaticInitializer(definition)) {
         return true; // Assume it *may* trigger if we didn't find the definition.
       }
       knownInterfaces.addAll(Arrays.asList(definition.interfaces.values));
@@ -499,10 +500,10 @@
     while (!queue.isEmpty()) {
       DexType iface = queue.remove();
       DexClass definition = definitionFor(iface);
-      if (definition == null || definition.hasClassInitializer()) {
+      if (canTriggerStaticInitializer(definition)) {
         return true; // Assume it *may* trigger if we didn't find the definition.
       }
-      if (!definition.accessFlags.isInterface()) {
+      if (!definition.isInterface()) {
         throw new Unreachable(iface.toSourceString() + " is expected to be an interface");
       }
 
@@ -515,6 +516,10 @@
     return false;
   }
 
+  private static boolean canTriggerStaticInitializer(DexClass clazz) {
+    return clazz == null || clazz.hasClassInitializer();
+  }
+
   public interface ResolutionResult {
 
     DexEncodedMethod asResultOfResolve();
diff --git a/src/main/java/com/android/tools/r8/graph/DexType.java b/src/main/java/com/android/tools/r8/graph/DexType.java
index d178e4a..02d2898 100644
--- a/src/main/java/com/android/tools/r8/graph/DexType.java
+++ b/src/main/java/com/android/tools/r8/graph/DexType.java
@@ -224,6 +224,34 @@
     }
   }
 
+  /**
+   * Collect all interfaces that this type directly or indirectly implements.
+   * @param appInfo where the definition of a certain {@link DexType} is looked up.
+   * @return a set of interfaces of {@link DexType}.
+   */
+  public Set<DexType> implementedInterfaces(AppInfo appInfo) {
+    Set<DexType> interfaces = Sets.newIdentityHashSet();
+    implementedInterfaces(appInfo, interfaces);
+    return interfaces;
+  }
+
+  private void implementedInterfaces(AppInfo appInfo, Set<DexType> interfaces) {
+    DexClass dexClass = appInfo.definitionFor(this);
+    // Loop to traverse the super type hierarchy of the current type.
+    while (dexClass != null) {
+      if (dexClass.isInterface()) {
+        interfaces.add(dexClass.type);
+      }
+      for (DexType itf : dexClass.interfaces.values) {
+        itf.implementedInterfaces(appInfo, interfaces);
+      }
+      if (dexClass.superType == null) {
+        break;
+      }
+      dexClass = appInfo.definitionFor(dexClass.superType);
+    }
+  }
+
   public boolean isSamePackage(DexType other) {
     return getPackageDescriptor().equals(other.getPackageDescriptor());
   }
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 45ec280..68d87c1 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -86,6 +86,7 @@
   public static final String DEFAULT_PROGUARD_MAP_FILE = "proguard.map";
 
   public static final String JAVA_8_RUNTIME = "third_party/openjdk/openjdk-rt-1.8/rt.jar";
+  public static final String KT_RUNTIME = "third_party/kotlin/kotlinc/lib/kotlin-runtime.jar";
   private static final String ANDROID_JAR_PATTERN = "third_party/android_jar/lib-v%d/android.jar";
   private static final AndroidApiLevel DEFAULT_MIN_SDK = AndroidApiLevel.I;
 
@@ -539,9 +540,16 @@
     String jar = String.format(
         ANDROID_JAR_PATTERN,
         (apiLevel == AndroidApiLevel.getDefault() ? DEFAULT_MIN_SDK : apiLevel).getLevel());
-    assert Files.exists(Paths.get(jar))
+    Path path = Paths.get(jar);
+    assert Files.exists(path)
         : "Expected android jar to exist for API level " + apiLevel;
-    return Paths.get(jar);
+    return path;
+  }
+
+  public static Path getKotlinRuntimeJar() {
+    Path path = Paths.get(KT_RUNTIME);
+    assert Files.exists(path) : "Expected kotlin runtime jar";
+    return path;
   }
 
   public static Path getJdwpTestsCfJarPath(AndroidApiLevel minSdk) {
diff --git a/src/test/java/com/android/tools/r8/graph/DexTypeTest.java b/src/test/java/com/android/tools/r8/graph/DexTypeTest.java
new file mode 100644
index 0000000..b32284a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/graph/DexTypeTest.java
@@ -0,0 +1,161 @@
+// 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.graph;
+
+import static org.hamcrest.CoreMatchers.hasItems;
+import static org.hamcrest.CoreMatchers.not;
+import static org.junit.Assert.assertThat;
+
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Timing;
+import java.util.Set;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class DexTypeTest {
+
+  private static DexItemFactory factory;
+  private static AppInfoWithSubtyping appInfo;
+
+  @BeforeClass
+  public static void makeAppInfo() throws Exception {
+    InternalOptions options = new InternalOptions();
+    DexApplication application =
+        new ApplicationReader(
+            AndroidApp.builder()
+                .addLibraryFiles(ToolHelper.getDefaultAndroidJar())
+                .addLibraryFiles(ToolHelper.getKotlinRuntimeJar())
+                .build(),
+            options,
+            new Timing(DexType.class.getName()))
+        .read()
+        .toDirect();
+    factory = options.itemFactory;
+    appInfo = new AppInfoWithSubtyping(application);
+  }
+
+  @Test
+  public void implementedInterfaces_collections_java() {
+    DexType serializable = factory.createType("Ljava/io/Serializable;");
+    DexType iterable = factory.createType("Ljava/lang/Iterable;");
+    // interface Collection extends Iterable
+    DexType collection = factory.createType("Ljava/util/Collection;");
+    // interface List extends Collection
+    DexType list = factory.createType("Ljava/util/List;");
+    // interface Queue extends Collection
+    DexType queue = factory.createType("Ljava/util/Queue;");
+    // interface Deque extends Queue
+    DexType deque = factory.createType("Ljava/util/Deque;");
+
+    // class ArrayList implements List
+    DexType arrayList = factory.createType("Ljava/util/ArrayList;");
+    Set<DexType> interfaces = arrayList.implementedInterfaces(appInfo);
+    assertThat(interfaces, hasItems(serializable));
+    assertThat(interfaces, hasItems(iterable));
+    assertThat(interfaces, hasItems(collection));
+    assertThat(interfaces, hasItems(list));
+    assertThat(interfaces, not(hasItems(queue)));
+
+    // class LinkedList implements List, Deque
+    DexType linkedList = factory.createType("Ljava/util/LinkedList;");
+    interfaces = linkedList.implementedInterfaces(appInfo);
+    assertThat(interfaces, hasItems(serializable));
+    assertThat(interfaces, hasItems(iterable));
+    assertThat(interfaces, hasItems(collection));
+    assertThat(interfaces, hasItems(list));
+    assertThat(interfaces, hasItems(deque));
+    assertThat(interfaces, hasItems(queue));
+  }
+
+  @Test
+  public void implementedInterfaces_collections_kotlin() {
+    DexType iterable = factory.createType("Ljava/lang/Iterable;");
+    // interface Collection extends Iterable
+    DexType collection = factory.createType("Ljava/util/Collection;");
+    // interface List extends Collection
+    DexType list = factory.createType("Ljava/util/List;");
+    // interface Set extends Collection
+    DexType set = factory.createType("Ljava/util/Set;");
+
+    DexType ktAbsList = factory.createType("Lkotlin/collections/AbstractList;");
+    DexType ktAbsSet = factory.createType("Lkotlin/collections/AbstractSet;");
+
+    Set<DexType> interfaces = ktAbsList.implementedInterfaces(appInfo);
+    assertThat(interfaces, hasItems(iterable));
+    assertThat(interfaces, hasItems(collection));
+    assertThat(interfaces, hasItems(list));
+    assertThat(interfaces, not(hasItems(set)));
+
+    interfaces = ktAbsSet.implementedInterfaces(appInfo);
+    assertThat(interfaces, hasItems(iterable));
+    assertThat(interfaces, hasItems(collection));
+    assertThat(interfaces, hasItems(set));
+    assertThat(interfaces, not(hasItems(list)));
+  }
+
+  @Test
+  public void implementedInterfaces_reflect_java() {
+    DexType serializable = factory.createType("Ljava/io/Serializable;");
+    DexType annotatedElement = factory.createType("Ljava/lang/reflect/AnnotatedElement;");
+    // interface GenericDeclaration extends AnnotatedElement
+    DexType genericDeclaration = factory.createType("Ljava/lang/reflect/GenericDeclaration;");
+    DexType type = factory.createType("Ljava/lang/reflect/Type;");
+    DexType pType = factory.createType("Ljava/lang/reflect/ParameterizedType;");
+    DexType klass = factory.createType("Ljava/lang/Class;");
+
+    Set<DexType> interfaces = klass.implementedInterfaces(appInfo);
+    assertThat(interfaces, hasItems(serializable));
+    assertThat(interfaces, hasItems(annotatedElement));
+    assertThat(interfaces, hasItems(genericDeclaration));
+    assertThat(interfaces, hasItems(type));
+    assertThat(interfaces, not(hasItems(pType)));
+  }
+
+  @Test
+  public void implementedInterfaces_reflect_kotlin() {
+    DexType kCallable = factory.createType("Lkotlin/reflect/KCallable;");
+    // interface KProperty : KFunction
+    DexType kFunction = factory.createType("Lkotlin/reflect/KFunction;");
+    // interface KProperty : KCallable
+    DexType kProperty = factory.createType("Lkotlin/reflect/KProperty;");
+    // interface KMutableProperty : KProperty
+    DexType kMutableProperty = factory.createType("Lkotlin/reflect/KMutableProperty;");
+    // interface KMutableProperty0 : KProperty, KMutableProperty
+    DexType kMutableProperty0 = factory.createType("Lkotlin/reflect/KMutableProperty0;");
+    // class MutablePropertyReference0 : KMutableProperty0
+    DexType mutableReference0 =
+        factory.createType("Lkotlin/jvm/internal/MutablePropertyReference0;");
+
+    Set<DexType> interfaces = mutableReference0.implementedInterfaces(appInfo);
+    assertThat(interfaces, hasItems(kCallable));
+    assertThat(interfaces, hasItems(kProperty));
+    assertThat(interfaces, hasItems(kMutableProperty));
+    assertThat(interfaces, hasItems(kMutableProperty0));
+    assertThat(interfaces, not(hasItems(kFunction)));
+  }
+
+  @Test
+  public void implementedInterfaces_lambda_kotlin() {
+    DexType function = factory.createType("Lkotlin/Function;");
+    DexType functionBase = factory.createType("Lkotlin/jvm/internal/FunctionBase;");
+    // class Lambda : Function, FunctionBase
+    DexType lambda = factory.createType("Lkotlin/jvm/internal/Lambda;");
+    // interface Function0 : Function
+    DexType function0 = factory.createType("Lkotlin/jvm/functions/Function0;");
+
+    Set<DexType> interfaces = lambda.implementedInterfaces(appInfo);
+    assertThat(interfaces, not(hasItems(lambda)));
+    assertThat(interfaces, hasItems(function));
+    assertThat(interfaces, hasItems(functionBase));
+
+    interfaces = function0.implementedInterfaces(appInfo);
+    assertThat(interfaces, hasItems(function0));
+    assertThat(interfaces, hasItems(function));
+    assertThat(interfaces, not(hasItems(functionBase)));
+  }
+
+}