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)));
+ }
+
+}