Merge "Remove JvmDescriptorUtils's dependency of Types util." into androidx-master-dev
diff --git a/room/compiler/src/main/kotlin/androidx/room/kotlin/JvmDescriptorUtils.kt b/room/compiler/src/main/kotlin/androidx/room/kotlin/JvmDescriptorUtils.kt
index 1020aa2..7757f79 100644
--- a/room/compiler/src/main/kotlin/androidx/room/kotlin/JvmDescriptorUtils.kt
+++ b/room/compiler/src/main/kotlin/androidx/room/kotlin/JvmDescriptorUtils.kt
@@ -21,27 +21,35 @@
 import javax.lang.model.element.NestingKind
 import javax.lang.model.element.QualifiedNameable
 import javax.lang.model.element.TypeElement
+import javax.lang.model.element.VariableElement
 import javax.lang.model.type.ArrayType
 import javax.lang.model.type.DeclaredType
 import javax.lang.model.type.ErrorType
 import javax.lang.model.type.ExecutableType
+import javax.lang.model.type.IntersectionType
 import javax.lang.model.type.NoType
 import javax.lang.model.type.NullType
 import javax.lang.model.type.PrimitiveType
 import javax.lang.model.type.TypeKind
 import javax.lang.model.type.TypeMirror
 import javax.lang.model.type.TypeVariable
+import javax.lang.model.type.UnionType
 import javax.lang.model.type.WildcardType
-import javax.lang.model.util.AbstractTypeVisitor6
-import javax.lang.model.util.Types
+import javax.lang.model.util.AbstractTypeVisitor8
+
+/**
+ * Returns the method descriptor of this [ExecutableElement].
+ *
+ * For reference, see the [JVM specification, section 4.3.2](https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.3.2)
+ */
+fun VariableElement.descriptor() = "$simpleName:${asType().descriptor()}"
 
 /**
  * Returns the method descriptor of this [ExecutableElement].
  *
  * For reference, see the [JVM specification, section 4.3.3](https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.3.3)
  */
-fun ExecutableElement.descriptor(typeUtils: Types) =
-    "$simpleName${asType().descriptor(typeUtils)}"
+fun ExecutableElement.descriptor() = "$simpleName${asType().descriptor()}"
 
 /**
  * Returns the name of this [TypeElement] in its "internal form".
@@ -84,22 +92,25 @@
         else -> error("Unknown primitive type $this")
     }
 
-fun TypeMirror.descriptor(typeUtils: Types): String =
-    accept(JvmDescriptorTypeVisitor, typeUtils)
+fun TypeMirror.descriptor(): String = accept(JvmDescriptorTypeVisitor, Unit)
 
-internal fun WildcardType.descriptor(typeUtils: Types): String =
-    typeUtils.erasure(this).descriptor(typeUtils)
+internal fun WildcardType.descriptor(): String = ""
 
-internal fun TypeVariable.descriptor(typeUtils: Types): String =
-    typeUtils.erasure(this).descriptor(typeUtils)
+// The erasure of a type variable is the erasure of its leftmost bound. - JVM Spec Sec 4.6
+internal fun TypeVariable.descriptor(): String = this.upperBound.descriptor()
 
-internal fun ArrayType.descriptor(typeUtils: Types): String =
-    "[" + componentType.descriptor(typeUtils)
+// For a type variable with multiple bounds: "the erasure of a type variable is determined by
+// the first type in its bound" - JVM Spec Sec 4.4
+internal fun IntersectionType.descriptor(): String =
+    this.bounds[0].descriptor()
 
-internal fun ExecutableType.descriptor(typeUtils: Types): String {
+internal fun ArrayType.descriptor(): String =
+    "[" + componentType.descriptor()
+
+internal fun ExecutableType.descriptor(): String {
     val parameterDescriptors =
-        parameterTypes.joinToString(separator = "") { it.descriptor(typeUtils) }
-    val returnDescriptor = returnType.descriptor(typeUtils)
+        parameterTypes.joinToString(separator = "") { it.descriptor() }
+    val returnDescriptor = returnType.descriptor()
     return "($parameterDescriptors)$returnDescriptor"
 }
 
@@ -108,34 +119,34 @@
  * + a "field descriptor", for example: `Ljava/lang/Object;`
  * + a "method descriptor", for example: `(Ljava/lang/Object;)Z`
  *
- * The easiest way to use this is through [TypeMirror.descriptor][JvmDescriptorUtils.descriptor] in [JvmDescriptorUtils].
+ * The easiest way to use this is through [TypeMirror.descriptor]
  *
  * For reference, see the [JVM specification, section 4.3](http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.3).
  */
 @Suppress("DEPRECATION")
-internal object JvmDescriptorTypeVisitor : AbstractTypeVisitor6<String, Types>() {
-    override fun visitNoType(t: NoType, typeUtils: Types): String = t.descriptor
+internal object JvmDescriptorTypeVisitor : AbstractTypeVisitor8<String, Unit>() {
 
-    override fun visitDeclared(t: DeclaredType, typeUtils: Types): String = t.descriptor
+    override fun visitNoType(t: NoType, u: Unit): String = t.descriptor
 
-    override fun visitPrimitive(t: PrimitiveType, typeUtils: Types): String = t.descriptor
+    override fun visitDeclared(t: DeclaredType, u: Unit): String = t.descriptor
 
-    override fun visitArray(t: ArrayType, typeUtils: Types): String = t.descriptor(typeUtils)
+    override fun visitPrimitive(t: PrimitiveType, u: Unit): String = t.descriptor
 
-    override fun visitWildcard(t: WildcardType, typeUtils: Types): String = t.descriptor(typeUtils)
+    override fun visitArray(t: ArrayType, u: Unit): String = t.descriptor()
 
-    override fun visitExecutable(t: ExecutableType, typeUtils: Types): String =
-        t.descriptor(typeUtils)
+    override fun visitWildcard(t: WildcardType, u: Unit): String = t.descriptor()
 
-    override fun visitTypeVariable(t: TypeVariable, typeUtils: Types): String =
-        t.descriptor(typeUtils)
+    override fun visitExecutable(t: ExecutableType, u: Unit): String = t.descriptor()
 
-    override fun visitNull(t: NullType, typeUtils: Types): String =
-        visitUnknown(t, typeUtils)
+    override fun visitTypeVariable(t: TypeVariable, u: Unit): String = t.descriptor()
 
-    override fun visitError(t: ErrorType, typeUtils: Types): String =
-        visitUnknown(t, typeUtils)
+    override fun visitNull(t: NullType, u: Unit): String = visitUnknown(t, u)
 
-    override fun visitUnknown(t: TypeMirror, typeUtils: Types): String =
-        error("Unsupported type $t")
+    override fun visitError(t: ErrorType, u: Unit): String = visitUnknown(t, u)
+
+    override fun visitIntersection(t: IntersectionType, u: Unit) = t.descriptor()
+
+    override fun visitUnion(t: UnionType, u: Unit) = visitUnknown(t, u)
+
+    override fun visitUnknown(t: TypeMirror, u: Unit): String = error("Unsupported type $t")
 }
\ No newline at end of file
diff --git a/room/compiler/src/main/kotlin/androidx/room/kotlin/KotlinMetadataElement.kt b/room/compiler/src/main/kotlin/androidx/room/kotlin/KotlinMetadataElement.kt
index 82eb235..6d121bc 100644
--- a/room/compiler/src/main/kotlin/androidx/room/kotlin/KotlinMetadataElement.kt
+++ b/room/compiler/src/main/kotlin/androidx/room/kotlin/KotlinMetadataElement.kt
@@ -35,7 +35,7 @@
     private val constructorList: List<KmConstructor> by lazy { classMetadata.readConstructors() }
 
     private val ExecutableElement.descriptor: String
-        get() = descriptor(context.processingEnv.typeUtils)
+        get() = descriptor()
 
     /**
      * Returns the parameter names of the function or constructor if all have names embedded in the
diff --git a/room/compiler/src/main/kotlin/androidx/room/processor/PojoProcessor.kt b/room/compiler/src/main/kotlin/androidx/room/processor/PojoProcessor.kt
index 745c40f..d787416 100644
--- a/room/compiler/src/main/kotlin/androidx/room/processor/PojoProcessor.kt
+++ b/room/compiler/src/main/kotlin/androidx/room/processor/PojoProcessor.kt
@@ -389,7 +389,7 @@
                 val primaryConstructor =
                     kotlinMetadata?.findPrimaryConstructorSignature()?.let { signature ->
                         goodConstructors.firstOrNull {
-                            it.element.descriptor(context.processingEnv.typeUtils) == signature
+                            it.element.descriptor() == signature
                     }
                 }
                 if (primaryConstructor != null) {
diff --git a/room/compiler/src/test/kotlin/androidx/room/kotlin/JvmDescriptorUtilsTest.kt b/room/compiler/src/test/kotlin/androidx/room/kotlin/JvmDescriptorUtilsTest.kt
index f5f1052..616e42e 100644
--- a/room/compiler/src/test/kotlin/androidx/room/kotlin/JvmDescriptorUtilsTest.kt
+++ b/room/compiler/src/test/kotlin/androidx/room/kotlin/JvmDescriptorUtilsTest.kt
@@ -26,6 +26,7 @@
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
 import toJFO
+import javax.lang.model.element.ElementKind
 import javax.tools.JavaFileObject
 
 @RunWith(JUnit4::class)
@@ -38,7 +39,7 @@
         import java.lang.annotation.ElementType;
         import java.lang.annotation.Target;
 
-        @Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
+        @Target({ElementType.FIELD, ElementType.METHOD, ElementType.CONSTRUCTOR})
         public @interface Describe { }
         """.toJFO("androidx.room.test.Describe")
 
@@ -57,6 +58,97 @@
         ) { descriptors ->
             assertThat(descriptors.first())
                 .isEqualTo("emptyMethod()V")
+        }
+    }
+
+    @Test
+    fun descriptor_field() {
+        singleRun(
+            """
+            package androidx.room.test;
+
+            import java.util.List;
+
+            class DummyClass<T> {
+                @Describe
+                int field1;
+
+                @Describe
+                String field2;
+
+                @Describe
+                T field3;
+
+                @Describe
+                List<String> field4;
+            }
+            """.toJFO("androidx.room.test.DummyClass")
+        ) { descriptors ->
+            assertThat(descriptors).isEqualTo(
+                setOf(
+                    "field1:I",
+                    "field2:Ljava/lang/String;",
+                    "field3:Ljava/lang/Object;",
+                    "field4:Ljava/util/List;"
+                )
+            )
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun descriptor_method_erasured() {
+        singleRun(
+            """
+            package androidx.room.test;
+
+            import java.util.ArrayList;
+            import java.util.Collection;
+            import java.util.List;
+            import java.util.Map;
+
+            class DummyClass<T> {
+                @Describe
+                void method1(T something) { }
+
+                @Describe
+                T method2() { return null; }
+
+                @Describe
+                List<? extends String> method3() { return null; }
+
+                @Describe
+                Map<T, String> method4() { return null; }
+
+                @Describe
+                ArrayList<Map<T, String>> method5() { return null; }
+
+                @Describe
+                static <I, O extends I> O method6(I input) { return null; }
+
+                @Describe
+                static <I, O extends String> O method7(I input) { return null; }
+
+                @Describe
+                static <P extends Collection & Comparable> P method8() { return null; }
+
+                @Describe
+                static <P extends String & List<Character>> P method9() { return null; }
+            }
+            """.toJFO("androidx.room.test.DummyClass")
+        ) { descriptors ->
+            assertThat(descriptors).isEqualTo(
+                setOf(
+                    "method1(Ljava/lang/Object;)V",
+                    "method2()Ljava/lang/Object;",
+                    "method3()Ljava/util/List;",
+                    "method4()Ljava/util/Map;",
+                    "method5()Ljava/util/ArrayList;",
+                    "method6(Ljava/lang/Object;)Ljava/lang/Object;",
+                    "method7(Ljava/lang/Object;)Ljava/lang/String;",
+                    "method8()Ljava/util/Collection;",
+                    "method9()Ljava/lang/String;"
+                )
+            )
         }.compilesWithoutError()
     }
 
@@ -94,6 +186,7 @@
 
             import java.util.ArrayList;
             import java.util.List;
+            import java.util.Map;
 
             class DummyClass {
                 @Describe
@@ -104,6 +197,9 @@
 
                 @Describe
                 List<String> method3(ArrayList<Integer> list) { return null; }
+
+                @Describe
+                Map<String, Object> method4() { return null; }
             }
             """.toJFO("androidx.room.test.DummyClass")
         ) { descriptors ->
@@ -111,7 +207,8 @@
                 setOf(
                     "method1(Ljava/lang/Object;)V",
                     "method2()Ljava/lang/Object;",
-                    "method3(Ljava/util/ArrayList;)Ljava/util/List;"
+                    "method3(Ljava/util/ArrayList;)Ljava/util/List;",
+                    "method4()Ljava/util/Map;"
                 )
             )
         }.compilesWithoutError()
@@ -243,7 +340,13 @@
         .processedWith(TestProcessor.builder()
             .nextRunHandler {
                 it.roundEnv.getElementsAnnotatedWith(it.annotations.first()).map { element ->
-                    MoreElements.asExecutable(element).descriptor(it.processingEnv.typeUtils)
+                    when (element.kind) {
+                        ElementKind.FIELD ->
+                            MoreElements.asVariable(element).descriptor()
+                        ElementKind.CONSTRUCTOR, ElementKind.METHOD ->
+                            MoreElements.asExecutable(element).descriptor()
+                        else -> error("Unsupported element to describe.")
+                    }
                 }.toSet().let(handler)
                 true
             }
diff --git a/room/compiler/src/test/kotlin/androidx/room/kotlin/KotlinMetadataElementTest.kt b/room/compiler/src/test/kotlin/androidx/room/kotlin/KotlinMetadataElementTest.kt
index 0d49c95..5d89402 100644
--- a/room/compiler/src/test/kotlin/androidx/room/kotlin/KotlinMetadataElementTest.kt
+++ b/room/compiler/src/test/kotlin/androidx/room/kotlin/KotlinMetadataElementTest.kt
@@ -55,7 +55,7 @@
             )
             assertThat(
                 ElementFilter.constructorsIn(testClassElement.enclosedElements).map {
-                    val desc = MoreElements.asExecutable(it).descriptor(invocation.typeUtils)
+                    val desc = MoreElements.asExecutable(it).descriptor()
                     desc to (desc == metadataElement.findPrimaryConstructorSignature())
                 }
             ).containsExactly(