Start generating Kotlin for FieldReadWriteWriter.readFromCursor()

To enable partial migration to XPoet this CL makes it so that the Pojo cursor reading code is generated in a separate Kotlin class that is then called from the Java Dao impl. This partial migration moves PrimitiveColumnTypeAdapter to XPoet, other type adapters will follow.

Additionally, this CL moves various JavaPoet TypeName usages in Room's value objects to XTypeName, mainly Pojo.typeName, most of the move is a refactor using toJavaPoet() that is a temporary compatibility API to be removed.

Due to the amount of type converters used in the integration test app this change only validates that the Java runtime behaves as expected and that the Kotlin generated code compiles (via KotlinCodeGenTest) but does not validate the runtime behaviour of the Kotlin codegen. We need to have all type converters and cursor readers migrated to run the integration tests.

As more of Room is migrated more XPoet APIs are added, in this CL we now have:
* begin, next and end control flow
* code block of a new instance, using the 'new' keyword in Java, but not in Kotlin
* code block of an unsafe cast, using the parenthesis syntax in Java and the 'as' keyword in Kotlin
* addLocalVariable is fixed (now that is being used)

Test: Existing tests + KotlinCodeGenTest
Change-Id: Id25a6e874fe9a03768ceca7e2c2a606ac2f469a2
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/PoetExt.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/PoetExt.kt
index a425abc..03d4446 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/PoetExt.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/PoetExt.kt
@@ -18,7 +18,9 @@
 
 import androidx.room.compiler.processing.XNullability
 import com.squareup.kotlinpoet.javapoet.JClassName
+import com.squareup.kotlinpoet.javapoet.JTypeName
 import com.squareup.kotlinpoet.javapoet.toKClassName
+import com.squareup.kotlinpoet.javapoet.toKTypeName
 
 typealias JCodeBlock = com.squareup.javapoet.CodeBlock
 typealias JCodeBlockBuilder = com.squareup.javapoet.CodeBlock.Builder
@@ -31,11 +33,12 @@
 typealias JArrayTypeName = com.squareup.javapoet.ArrayTypeName
 
 // TODO(b/127483380): Recycle to room-compiler?
-val L = "\$L"
-val T = "\$T"
-val N = "\$N"
-val S = "\$S"
-val W = "\$W"
+internal val L = "\$L"
+internal val T = "\$T"
+internal val N = "\$N"
+internal val S = "\$S"
+internal val W = "\$W"
 
 // TODO(b/247247366): Temporary migration API, delete me plz!
+fun JTypeName.toXTypeName() = XTypeName(this, this.toKTypeName(), XNullability.NONNULL)
 fun JClassName.toXClassName() = XClassName(this, this.toKClassName(), XNullability.NONNULL)
\ No newline at end of file
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/XCodeBlock.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/XCodeBlock.kt
index 4b16c2b6..c681bed 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/XCodeBlock.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/XCodeBlock.kt
@@ -31,11 +31,15 @@
 
         fun addLocalVariable(
             name: String,
-            type: XTypeName,
+            typeName: XTypeName,
             isMutable: Boolean = false,
-            assignExpr: XCodeBlock
+            assignExpr: XCodeBlock? = null
         ): Builder
 
+        fun beginControlFlow(controlFlow: String, vararg args: Any?): Builder
+        fun nextControlFlow(controlFlow: String, vararg args: Any?): Builder
+        fun endControlFlow(): Builder
+
         fun build(): XCodeBlock
 
         companion object {
@@ -68,5 +72,45 @@
         fun of(language: CodeLanguage, format: String, vararg args: Any?): XCodeBlock {
             return builder(language).add(format, *args).build()
         }
+
+        /**
+         * Convenience code block of a new instantiation expression.
+         *
+         * Shouldn't contain parenthesis.
+         */
+        fun ofNewInstance(
+            language: CodeLanguage,
+            typeName: XTypeName,
+            argsFormat: String = "",
+            vararg args: Any?
+        ): XCodeBlock {
+            return builder(language).apply {
+                val newKeyword = when (language) {
+                    CodeLanguage.JAVA -> "new "
+                    CodeLanguage.KOTLIN -> ""
+                }
+                add("$newKeyword%T($argsFormat)", typeName, *args)
+            }.build()
+        }
+
+        /**
+         * Convenience code block of an unsafe cast expression.
+         */
+        fun ofCast(
+            language: CodeLanguage,
+            typeName: XTypeName,
+            expressionBlock: XCodeBlock
+        ): XCodeBlock {
+            return builder(language).apply {
+                when (language) {
+                    CodeLanguage.JAVA -> {
+                        add("(%T) (%L)", typeName, expressionBlock)
+                    }
+                    CodeLanguage.KOTLIN -> {
+                        add("(%L) as %T", expressionBlock, typeName)
+                    }
+                }
+            }.build()
+        }
     }
 }
\ No newline at end of file
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/XTypeName.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/XTypeName.kt
index 5eae6bfc..67c01fc 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/XTypeName.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/XTypeName.kt
@@ -39,6 +39,8 @@
     internal open val kotlin: KTypeName,
     internal val nullability: XNullability
 ) {
+    val isPrimitive: Boolean
+        get() = java.isPrimitive
 
     override fun equals(other: Any?): Boolean {
         if (this === other) return true
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/java/JavaCodeBlock.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/java/JavaCodeBlock.kt
index a5e9cff..937afb5 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/java/JavaCodeBlock.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/java/JavaCodeBlock.kt
@@ -50,13 +50,38 @@
 
         override fun addLocalVariable(
             name: String,
-            type: XTypeName,
+            typeName: XTypeName,
             isMutable: Boolean,
-            assignExpr: XCodeBlock
+            assignExpr: XCodeBlock?
         ) = apply {
-            require(assignExpr is JavaCodeBlock)
-            val finalKeyword = if (isMutable) "final " else ""
-            actual.addStatement("$finalKeyword\$T \$L = \$L", type, name, assignExpr.actual)
+            val finalKeyword = if (isMutable) "" else "final "
+            if (assignExpr != null) {
+                require(assignExpr is JavaCodeBlock)
+                actual.addStatement(
+                    "$finalKeyword\$T \$L = \$L",
+                    typeName.java,
+                    name,
+                    assignExpr.actual
+                )
+            } else {
+                actual.addStatement("$finalKeyword\$T \$L", typeName.java, name)
+            }
+        }
+
+        override fun beginControlFlow(controlFlow: String, vararg args: Any?) = apply {
+            val processedControlFlow = processFormatString(controlFlow)
+            val processedArgs = processArgs(args)
+            actual.beginControlFlow(processedControlFlow, *processedArgs)
+        }
+
+        override fun nextControlFlow(controlFlow: String, vararg args: Any?) = apply {
+            val processedControlFlow = processFormatString(controlFlow)
+            val processedArgs = processArgs(args)
+            actual.nextControlFlow(processedControlFlow, *processedArgs)
+        }
+
+        override fun endControlFlow() = apply {
+            actual.endControlFlow()
         }
 
         override fun build(): XCodeBlock {
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/kotlin/KotlinCodeBlock.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/kotlin/KotlinCodeBlock.kt
index ede67d2..1daab89 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/kotlin/KotlinCodeBlock.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/kotlin/KotlinCodeBlock.kt
@@ -39,37 +39,70 @@
         }
 
         override fun add(format: String, vararg args: Any?) = apply {
-            // No need to process 'format' since we use '%' as placeholders.
+            val processedFormat = processFormatString(format)
             val processedArgs = processArgs(args)
-            actual.add(format, *processedArgs)
+            actual.add(processedFormat, *processedArgs)
         }
 
         override fun addStatement(format: String, vararg args: Any?) = apply {
-            // No need to process 'format' since we use '%' as placeholders.
+            val processedFormat = processFormatString(format)
             val processedArgs = processArgs(args)
-            actual.addStatement(format, *processedArgs)
+            actual.addStatement(processedFormat, *processedArgs)
         }
 
         override fun addLocalVariable(
             name: String,
-            type: XTypeName,
+            typeName: XTypeName,
             isMutable: Boolean,
-            assignExpr: XCodeBlock
+            assignExpr: XCodeBlock?
         ) = apply {
-            require(assignExpr is KotlinCodeBlock)
             val varOrVal = if (isMutable) "var" else "val"
-            actual.addStatement(
-                "$varOrVal %L: %T = %L",
-                type.kotlin,
-                name,
-                assignExpr.actual
-            )
+            if (assignExpr != null) {
+                require(assignExpr is KotlinCodeBlock)
+                actual.addStatement(
+                    "$varOrVal %L: %T = %L",
+                    name,
+                    typeName.kotlin,
+                    assignExpr.actual
+                )
+            } else {
+                actual.addStatement(
+                    "$varOrVal %L: %T",
+                    name,
+                    typeName.kotlin,
+                )
+            }
+        }
+
+        override fun beginControlFlow(controlFlow: String, vararg args: Any?) = apply {
+            val processedControlFlow = processFormatString(controlFlow)
+            val processedArgs = processArgs(args)
+            actual.beginControlFlow(processedControlFlow, *processedArgs)
+        }
+
+        override fun nextControlFlow(controlFlow: String, vararg args: Any?) = apply {
+            val processedControlFlow = processFormatString(controlFlow)
+            val processedArgs = processArgs(args)
+            actual.nextControlFlow(processedControlFlow, *processedArgs)
+        }
+
+        override fun endControlFlow() = apply {
+            actual.endControlFlow()
         }
 
         override fun build(): XCodeBlock {
             return KotlinCodeBlock(actual.build())
         }
 
+        // No need to really process 'format' since we use '%' as placeholders, but check for
+        // JavaPoet placeholders to hunt down bad migrations to XPoet.
+        private fun processFormatString(format: String): String {
+            JAVA_POET_PLACEHOLDER_REGEX.find(format)?.let {
+                error("Bad JavaPoet placeholder in XPoet at range ${it.range} of input: '$format'")
+            }
+            return format
+        }
+
         // Unwraps room.compiler.codegen types to their KotlinPoet actual
         // TODO(b/247242375): Consider improving by wrapping args.
         private fun processArgs(args: Array<out Any?>): Array<Any?> {
@@ -88,4 +121,9 @@
             }
         }
     }
+
+    companion object {
+        private val JAVA_POET_PLACEHOLDER_REGEX =
+            "(\\\$L)|(\\\$T)|(\\\$N)|(\\\$S)|(\\\$W)".toRegex()
+    }
 }
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XProcessingEnv.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XProcessingEnv.kt
index ff411fc..1f71ce4 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XProcessingEnv.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XProcessingEnv.kt
@@ -16,6 +16,7 @@
 
 package androidx.room.compiler.processing
 
+import androidx.room.compiler.codegen.XTypeName
 import androidx.room.compiler.processing.javac.JavacProcessingEnv
 import androidx.room.compiler.processing.ksp.KspProcessingEnv
 import com.google.devtools.ksp.processing.CodeGenerator
@@ -112,6 +113,16 @@
         "cannot find required type $typeName"
     }
 
+    fun requireType(typeName: XTypeName): XType {
+        if (typeName.isPrimitive) {
+            return requireType(typeName.java)
+        }
+        return when (backend) {
+            Backend.JAVAC -> requireType(typeName.java)
+            Backend.KSP -> requireType(typeName.kotlin.toString())
+        }
+    }
+
     fun requireType(klass: KClass<*>) = requireType(klass.java.canonicalName!!)
 
     fun findType(typeName: TypeName): XType? {
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingEnvTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingEnvTest.kt
index daad3ba..4e4fe97 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingEnvTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingEnvTest.kt
@@ -17,6 +17,8 @@
 package androidx.room.compiler.processing
 
 import androidx.room.compiler.codegen.XClassName
+import androidx.room.compiler.codegen.XTypeName
+import androidx.room.compiler.codegen.asClassName
 import androidx.room.compiler.processing.util.Source
 import androidx.room.compiler.processing.util.runProcessorTest
 import com.google.common.truth.Truth.assertThat
@@ -283,6 +285,35 @@
         }
     }
 
+    @Test
+    fun requireTypeWithXTypeName() {
+        runProcessorTest { invocation ->
+            invocation.processingEnv.requireType(String::class.asClassName()).let {
+                val name = it.typeElement!!.qualifiedName
+                if (invocation.isKsp) {
+                    assertThat(name).isEqualTo("kotlin.String")
+                } else {
+                    assertThat(name).isEqualTo("java.lang.String")
+                }
+            }
+            invocation.processingEnv.requireType(Int::class.asClassName()).let {
+                val name = it.typeElement!!.qualifiedName
+                if (invocation.isKsp) {
+                    assertThat(name).isEqualTo("kotlin.Int")
+                } else {
+                    assertThat(name).isEqualTo("java.lang.Integer")
+                }
+            }
+            invocation.processingEnv.requireType(XTypeName.PRIMITIVE_INT).let {
+                assertThat(it.typeElement).isNull() // No element is an indicator of primitive type
+                assertThat(it.asTypeName().java.toString()).isEqualTo("int")
+                if (invocation.isKsp) {
+                    assertThat(it.asTypeName().kotlin.toString()).isEqualTo("kotlin.Int")
+                }
+            }
+        }
+    }
+
     companion object {
         val PRIMITIVE_TYPES = listOf(
             TypeName.BOOLEAN,
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/ext/javapoet_ext.kt b/room/room-compiler/src/main/kotlin/androidx/room/ext/javapoet_ext.kt
index a93964d..dbabd96 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/ext/javapoet_ext.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/ext/javapoet_ext.kt
@@ -16,6 +16,7 @@
 
 package androidx.room.ext
 
+import androidx.room.compiler.codegen.XClassName
 import com.squareup.javapoet.ArrayTypeName
 import com.squareup.javapoet.ClassName
 import com.squareup.javapoet.CodeBlock
@@ -132,7 +133,7 @@
 }
 
 object AndroidTypeNames {
-    val CURSOR: ClassName = ClassName.get("android.database", "Cursor")
+    val CURSOR: XClassName = XClassName.get("android.database", "Cursor")
     val BUILD: ClassName = ClassName.get("android.os", "Build")
     val CANCELLATION_SIGNAL: ClassName = ClassName.get("android.os", "CancellationSignal")
 }
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/processor/DatabaseProcessor.kt b/room/room-compiler/src/main/kotlin/androidx/room/processor/DatabaseProcessor.kt
index 473928d..52f0e4a 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/processor/DatabaseProcessor.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/processor/DatabaseProcessor.kt
@@ -319,12 +319,14 @@
             .filter { it.value.size > 1 } // get the ones with duplicate names
             .forEach {
                 // do not report duplicates from the same entity
-                if (it.value.distinctBy { it.second.typeName }.size > 1) {
+                if (it.value.distinctBy { it.second.typeName.toJavaPoet() }.size > 1) {
                     context.logger.e(
                         element,
                         ProcessorErrors.duplicateIndexInDatabase(
                             it.key,
-                            it.value.map { "${it.second.typeName} > ${it.first}" }
+                            it.value.map {
+                                "${it.second.typeName.toJavaPoet()} > ${it.first}"
+                            }
                         )
                     )
                 }
@@ -336,7 +338,7 @@
         daoMethods: List<DaoMethod>,
         entities: List<Entity>
     ) {
-        val entityTypeNames = entities.map { it.typeName }.toSet()
+        val entityTypeNames = entities.map { it.typeName.toJavaPoet() }.toSet()
         daoMethods.groupBy { it.dao.typeName }
             .forEach {
                 if (it.value.size > 1) {
@@ -391,10 +393,18 @@
         views: List<DatabaseView>
     ) {
         val entitiesInfo = entities.map {
-            Triple(it.tableName.lowercase(Locale.US), it.typeName.toString(), it.element)
+            Triple(
+                it.tableName.lowercase(Locale.US),
+                it.typeName.toJavaPoet().toString(),
+                it.element
+            )
         }
         val viewsInfo = views.map {
-            Triple(it.viewName.lowercase(Locale.US), it.typeName.toString(), it.element)
+            Triple(
+                it.viewName.lowercase(Locale.US),
+                it.typeName.toJavaPoet().toString(),
+                it.element
+            )
         }
         (entitiesInfo + viewsInfo)
             .groupBy { (name, _, _) -> name }
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/processor/EntityOrViewProcessor.kt b/room/room-compiler/src/main/kotlin/androidx/room/processor/EntityOrViewProcessor.kt
index 51956a5..f9a26ea 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/processor/EntityOrViewProcessor.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/processor/EntityOrViewProcessor.kt
@@ -18,10 +18,10 @@
 
 import androidx.room.DatabaseView
 import androidx.room.Entity
+import androidx.room.compiler.codegen.XTypeName
 import androidx.room.compiler.processing.XTypeElement
 import androidx.room.vo.EntityOrView
 import androidx.room.vo.Fields
-import com.squareup.javapoet.TypeName
 
 interface EntityOrViewProcessor {
     fun process(): EntityOrView
@@ -51,8 +51,8 @@
             override val fields: Fields = Fields()
             override val tableName: String
                 get() = typeName.toString()
-            override val typeName: TypeName
-                get() = element.type.typeName
+            override val typeName: XTypeName
+                get() = element.type.asTypeName()
         }
     }
 }
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/processor/InsertionMethodProcessor.kt b/room/room-compiler/src/main/kotlin/androidx/room/processor/InsertionMethodProcessor.kt
index e05cf9e..f75be97 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/processor/InsertionMethodProcessor.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/processor/InsertionMethodProcessor.kt
@@ -20,6 +20,7 @@
 
 import androidx.room.Insert
 import androidx.room.OnConflictStrategy
+import androidx.room.compiler.codegen.toJavaPoet
 import androidx.room.compiler.processing.XMethodElement
 import androidx.room.compiler.processing.XType
 import androidx.room.vo.InsertionMethod
@@ -63,7 +64,7 @@
                     entity.primaryKey.autoGenerateId || !missingPrimaryKeys,
                     executableElement,
                     ProcessorErrors.missingPrimaryKeysInPartialEntityForInsert(
-                        partialEntityName = pojo.typeName.toString(),
+                        partialEntityName = pojo.typeName.toJavaPoet().toString(),
                         primaryKeyNames = entity.primaryKey.fields.columnNames
                     )
                 )
@@ -78,7 +79,7 @@
                     missingRequiredFields.isEmpty(),
                     executableElement,
                     ProcessorErrors.missingRequiredColumnsInPartialEntity(
-                        partialEntityName = pojo.typeName.toString(),
+                        partialEntityName = pojo.typeName.toJavaPoet().toString(),
                         missingColumnNames = missingRequiredFields.map { it.columnName }
                     )
                 )
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/processor/PojoProcessor.kt b/room/room-compiler/src/main/kotlin/androidx/room/processor/PojoProcessor.kt
index 30f1f9c..90189d4 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/processor/PojoProcessor.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/processor/PojoProcessor.kt
@@ -22,6 +22,7 @@
 import androidx.room.Junction
 import androidx.room.PrimaryKey
 import androidx.room.Relation
+import androidx.room.compiler.codegen.toJavaPoet
 import androidx.room.compiler.processing.XExecutableElement
 import androidx.room.compiler.processing.XFieldElement
 import androidx.room.compiler.processing.XType
@@ -511,7 +512,7 @@
             context.logger.e(
                 relationElement,
                 ProcessorErrors.relationCannotFindEntityField(
-                    entityName = entity.typeName.toString(),
+                    entityName = entity.typeName.toJavaPoet().toString(),
                     columnName = annotation.value.entityColumn,
                     availableColumns = entity.columnNames
                 )
@@ -557,7 +558,7 @@
                         context.logger.w(
                             Warning.MISSING_INDEX_ON_JUNCTION, field.element,
                             ProcessorErrors.junctionColumnWithoutIndex(
-                                entityName = entityOrView.typeName.toString(),
+                                entityName = entityOrView.typeName.toJavaPoet().toString(),
                                 columnName = columnName
                             )
                         )
@@ -577,7 +578,7 @@
                     context.logger.e(
                         junctionElement,
                         ProcessorErrors.relationCannotFindJunctionParentField(
-                            entityName = entityOrView.typeName.toString(),
+                            entityName = entityOrView.typeName.toJavaPoet().toString(),
                             columnName = junctionParentColumn,
                             availableColumns = entityOrView.columnNames
                         )
@@ -596,7 +597,7 @@
                     context.logger.e(
                         junctionElement,
                         ProcessorErrors.relationCannotFindJunctionEntityField(
-                            entityName = entityOrView.typeName.toString(),
+                            entityName = entityOrView.typeName.toJavaPoet().toString(),
                             columnName = junctionEntityColumn,
                             availableColumns = entityOrView.columnNames
                         )
@@ -653,7 +654,7 @@
             context.logger.e(
                 relationElement,
                 ProcessorErrors.relationBadProject(
-                    entity.typeName.toString(),
+                    entity.typeName.toJavaPoet().toString(),
                     missingColumns, entity.columnNames
                 )
             )
@@ -676,7 +677,7 @@
         entityField: Field,
         typeArgElement: XTypeElement
     ): List<String> {
-        return if (inferEntity || typeArg.typeName == entity.typeName) {
+        return if (inferEntity || typeArg.typeName == entity.typeName.toJavaPoet()) {
             entity.columnNames
         } else {
             val columnAdapter = context.typeAdapterStore.findCursorValueReader(typeArg, null)
@@ -772,7 +773,7 @@
                     fieldName = field.name,
                     ownerType = element.type.typeName,
                     getterType = field.getter.type.typeName,
-                    fieldType = field.typeName
+                    fieldType = field.typeName.toJavaPoet()
                 )
             )
             field.statementBinder = context.typeAdapterStore.findStatementValueBinder(
@@ -848,7 +849,7 @@
                     fieldName = field.name,
                     ownerType = element.type.typeName,
                     setterType = field.setter.type.typeName,
-                    fieldType = field.typeName
+                    fieldType = field.typeName.toJavaPoet()
                 )
             )
             field.cursorValueReader = context.typeAdapterStore.findCursorValueReader(
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/processor/QueryMethodProcessor.kt b/room/room-compiler/src/main/kotlin/androidx/room/processor/QueryMethodProcessor.kt
index a3f5b03..7c6a68e 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/processor/QueryMethodProcessor.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/processor/QueryMethodProcessor.kt
@@ -19,6 +19,7 @@
 import androidx.room.Query
 import androidx.room.SkipQueryVerification
 import androidx.room.Transaction
+import androidx.room.compiler.codegen.toJavaPoet
 import androidx.room.compiler.processing.XAnnotationBox
 import androidx.room.compiler.processing.XMethodElement
 import androidx.room.compiler.processing.XType
@@ -250,10 +251,10 @@
             val pojoMappings = mappings.filterIsInstance<PojoRowAdapter.PojoMapping>()
             val pojoUnusedFields = pojoMappings
                 .filter { it.unusedFields.isNotEmpty() }
-                .associate { it.pojo.typeName to it.unusedFields }
+                .associate { it.pojo.typeName.toJavaPoet() to it.unusedFields }
             if (unusedColumns.isNotEmpty() || pojoUnusedFields.isNotEmpty()) {
                 val warningMsg = ProcessorErrors.cursorPojoMismatch(
-                    pojoTypeNames = pojoMappings.map { it.pojo.typeName },
+                    pojoTypeNames = pojoMappings.map { it.pojo.typeName.toJavaPoet() },
                     unusedColumns = unusedColumns,
                     allColumns = columnNames,
                     pojoUnusedFields = pojoUnusedFields,
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/processor/ShortcutMethodProcessor.kt b/room/room-compiler/src/main/kotlin/androidx/room/processor/ShortcutMethodProcessor.kt
index 879a7b3..8d558de 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/processor/ShortcutMethodProcessor.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/processor/ShortcutMethodProcessor.kt
@@ -15,11 +15,12 @@
  */
 package androidx.room.processor
 
-import androidx.room.ext.isEntityElement
+import androidx.room.compiler.codegen.toJavaPoet
 import androidx.room.compiler.processing.XAnnotationBox
 import androidx.room.compiler.processing.XMethodElement
 import androidx.room.compiler.processing.XType
 import androidx.room.compiler.processing.XTypeElement
+import androidx.room.ext.isEntityElement
 import androidx.room.vo.Entity
 import androidx.room.vo.Pojo
 import androidx.room.vo.ShortcutEntity
@@ -138,7 +139,7 @@
                                 context.logger.e(
                                     it.element,
                                     ProcessorErrors.cannotFindAsEntityField(
-                                        targetEntity.typeName.toString()
+                                        targetEntity.typeName.toJavaPoet().toString()
                                     )
 
                                 )
@@ -156,7 +157,7 @@
                             context.logger.e(
                                 executableElement,
                                 ProcessorErrors.noColumnsInPartialEntity(
-                                    partialEntityName = pojo.typeName.toString()
+                                    partialEntityName = pojo.typeName.toJavaPoet().toString()
                                 )
                             )
                         }
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/processor/TableEntityProcessor.kt b/room/room-compiler/src/main/kotlin/androidx/room/processor/TableEntityProcessor.kt
index 1c17c65..1edbcda 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/processor/TableEntityProcessor.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/processor/TableEntityProcessor.kt
@@ -16,11 +16,12 @@
 
 package androidx.room.processor
 
-import androidx.room.parser.SQLTypeAffinity
-import androidx.room.parser.SqlParser
+import androidx.room.compiler.codegen.toJavaPoet
 import androidx.room.compiler.processing.XType
 import androidx.room.compiler.processing.XTypeElement
 import androidx.room.ext.isNotNone
+import androidx.room.parser.SQLTypeAffinity
+import androidx.room.parser.SqlParser
 import androidx.room.processor.EntityProcessor.Companion.createIndexName
 import androidx.room.processor.EntityProcessor.Companion.extractForeignKeys
 import androidx.room.processor.EntityProcessor.Companion.extractIndices
@@ -514,7 +515,7 @@
                         Warning.INDEX_FROM_EMBEDDED_ENTITY_IS_DROPPED,
                         embedded.field.element,
                         ProcessorErrors.droppedEmbeddedIndex(
-                            entityName = embedded.pojo.typeName.toString(),
+                            entityName = embedded.pojo.typeName.toJavaPoet().toString(),
                             fieldPath = embedded.field.getPath(),
                             grandParent = element.qualifiedName
                         )
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/processor/UpdateMethodProcessor.kt b/room/room-compiler/src/main/kotlin/androidx/room/processor/UpdateMethodProcessor.kt
index ff259c2..3b442a4 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/processor/UpdateMethodProcessor.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/processor/UpdateMethodProcessor.kt
@@ -18,6 +18,7 @@
 
 import androidx.room.OnConflictStrategy
 import androidx.room.Update
+import androidx.room.compiler.codegen.toJavaPoet
 import androidx.room.compiler.processing.XMethodElement
 import androidx.room.compiler.processing.XType
 import androidx.room.vo.UpdateMethod
@@ -51,7 +52,7 @@
                 context.checker.check(
                     missingPrimaryKeys.isEmpty(), executableElement,
                     ProcessorErrors.missingPrimaryKeysInPartialEntityForUpdate(
-                        partialEntityName = pojo.typeName.toString(),
+                        partialEntityName = pojo.typeName.toJavaPoet().toString(),
                         primaryKeyNames = missingPrimaryKeys.map { it.columnName }
                     )
                 )
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/processor/UpsertionMethodProcessor.kt b/room/room-compiler/src/main/kotlin/androidx/room/processor/UpsertionMethodProcessor.kt
index 4e07a9c..4098bb4 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/processor/UpsertionMethodProcessor.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/processor/UpsertionMethodProcessor.kt
@@ -17,6 +17,7 @@
 package androidx.room.processor
 
 import androidx.room.Upsert
+import androidx.room.compiler.codegen.toJavaPoet
 import androidx.room.compiler.processing.XMethodElement
 import androidx.room.compiler.processing.XType
 import androidx.room.vo.UpsertionMethod
@@ -55,7 +56,7 @@
                     entity.primaryKey.autoGenerateId || !missingPrimaryKeys,
                     executableElement,
                     ProcessorErrors.missingPrimaryKeysInPartialEntityForUpsert(
-                        partialEntityName = pojo.typeName.toString(),
+                        partialEntityName = pojo.typeName.toJavaPoet().toString(),
                         primaryKeyNames = entity.primaryKey.fields.columnNames
                     )
                 )
@@ -70,7 +71,7 @@
                     missingRequiredFields.isEmpty(),
                     executableElement,
                     ProcessorErrors.missingRequiredColumnsInPartialEntity(
-                        partialEntityName = pojo.typeName.toString(),
+                        partialEntityName = pojo.typeName.toJavaPoet().toString(),
                         missingColumnNames = missingRequiredFields.map { it.columnName }
                     )
                 )
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/CodeGenScope.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/CodeGenScope.kt
index 0bf6717..c9ffebb 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/CodeGenScope.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/CodeGenScope.kt
@@ -28,7 +28,8 @@
 class CodeGenScope(
     val writer: TypeWriter
 ) {
-    val builder by lazy { XCodeBlock.builder(writer.codeLanguage) }
+    val language = writer.codeLanguage
+    val builder by lazy { XCodeBlock.builder(language) }
     private val tmpVarIndices = mutableMapOf<String, Int>()
 
     companion object {
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/binderprovider/CursorQueryResultBinderProvider.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/binderprovider/CursorQueryResultBinderProvider.kt
index fb889c4..009c521 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/binderprovider/CursorQueryResultBinderProvider.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/binderprovider/CursorQueryResultBinderProvider.kt
@@ -16,8 +16,9 @@
 
 package androidx.room.solver.binderprovider
 
+import androidx.room.compiler.codegen.toJavaPoet
 import androidx.room.compiler.processing.XType
-import androidx.room.ext.AndroidTypeNames
+import androidx.room.ext.AndroidTypeNames.CURSOR
 import androidx.room.parser.ParsedQuery
 import androidx.room.processor.Context
 import androidx.room.solver.QueryResultBinderProvider
@@ -35,5 +36,5 @@
     }
 
     override fun matches(declared: XType): Boolean =
-        declared.typeArguments.isEmpty() && declared.typeName == AndroidTypeNames.CURSOR
+        declared.typeArguments.isEmpty() && declared.typeName == CURSOR.toJavaPoet()
 }
\ No newline at end of file
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/BaseObservableQueryResultBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/BaseObservableQueryResultBinder.kt
index 887fec8..903180c 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/BaseObservableQueryResultBinder.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/BaseObservableQueryResultBinder.kt
@@ -16,7 +16,8 @@
 
 package androidx.room.solver.query.result
 
-import androidx.room.ext.AndroidTypeNames
+import androidx.room.compiler.codegen.toJavaPoet
+import androidx.room.ext.AndroidTypeNames.CURSOR
 import androidx.room.ext.L
 import androidx.room.ext.N
 import androidx.room.ext.RoomTypeNames
@@ -61,7 +62,7 @@
         builder.apply {
             addStatement(
                 "final $T $L = $T.query($N, $L, $L, $L)",
-                AndroidTypeNames.CURSOR,
+                CURSOR.toJavaPoet(),
                 cursorVar,
                 RoomTypeNames.DB_UTIL,
                 dbField,
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/CoroutineResultBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/CoroutineResultBinder.kt
index 844aa5d..74142bc 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/CoroutineResultBinder.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/CoroutineResultBinder.kt
@@ -16,14 +16,16 @@
 
 package androidx.room.solver.query.result
 
+import androidx.room.compiler.codegen.toJavaPoet
+import androidx.room.compiler.processing.XType
 import androidx.room.ext.AndroidTypeNames
+import androidx.room.ext.AndroidTypeNames.CURSOR
 import androidx.room.ext.CallableTypeSpecBuilder
 import androidx.room.ext.L
 import androidx.room.ext.N
 import androidx.room.ext.RoomCoroutinesTypeNames
 import androidx.room.ext.RoomTypeNames
 import androidx.room.ext.T
-import androidx.room.compiler.processing.XType
 import androidx.room.solver.CodeGenScope
 import com.squareup.javapoet.FieldSpec
 import com.squareup.javapoet.MethodSpec
@@ -98,7 +100,7 @@
         builder.apply {
             addStatement(
                 "final $T $L = $T.query($N, $L, $L, $L)",
-                AndroidTypeNames.CURSOR,
+                CURSOR.toJavaPoet(),
                 cursorVar,
                 RoomTypeNames.DB_UTIL,
                 dbField,
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/CursorQueryResultBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/CursorQueryResultBinder.kt
index 38d5132..48fed6b 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/CursorQueryResultBinder.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/CursorQueryResultBinder.kt
@@ -16,7 +16,8 @@
 
 package androidx.room.solver.query.result
 
-import androidx.room.ext.AndroidTypeNames
+import androidx.room.compiler.codegen.toJavaPoet
+import androidx.room.ext.AndroidTypeNames.CURSOR
 import androidx.room.ext.L
 import androidx.room.ext.N
 import androidx.room.ext.T
@@ -40,7 +41,7 @@
         transactionWrapper?.beginTransactionWithControlFlow()
         val resultName = scope.getTmpVar("_tmpResult")
         builder.addStatement(
-            "final $T $L = $N.query($L)", AndroidTypeNames.CURSOR, resultName,
+            "final $T $L = $N.query($L)", CURSOR.toJavaPoet(), resultName,
             dbField, roomSQLiteQueryVar
         )
         transactionWrapper?.commitTransaction()
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/EntityRowAdapter.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/EntityRowAdapter.kt
index 5b79e64..258ddf2 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/EntityRowAdapter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/EntityRowAdapter.kt
@@ -16,7 +16,8 @@
 
 package androidx.room.solver.query.result
 
-import androidx.room.ext.AndroidTypeNames
+import androidx.room.compiler.codegen.toJavaPoet
+import androidx.room.ext.AndroidTypeNames.CURSOR
 import androidx.room.ext.CommonTypeNames
 import androidx.room.ext.L
 import androidx.room.ext.N
@@ -88,7 +89,7 @@
             )
             scope.builder().addStatement(
                 "final $T $N = $T.wrapMappedColumns($N, $L, $L)",
-                AndroidTypeNames.CURSOR,
+                CURSOR.toJavaPoet(),
                 cursorDelegateVarName,
                 RoomTypeNames.CURSOR_UTIL,
                 cursorVarName,
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/InstantQueryResultBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/InstantQueryResultBinder.kt
index 89e143e..d9abfaf 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/InstantQueryResultBinder.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/InstantQueryResultBinder.kt
@@ -15,7 +15,8 @@
  */
 package androidx.room.solver.query.result
 
-import androidx.room.ext.AndroidTypeNames
+import androidx.room.compiler.codegen.toJavaPoet
+import androidx.room.ext.AndroidTypeNames.CURSOR
 import androidx.room.ext.L
 import androidx.room.ext.N
 import androidx.room.ext.RoomTypeNames
@@ -50,7 +51,7 @@
             val cursorVar = scope.getTmpVar("_cursor")
             addStatement(
                 "final $T $L = $T.query($N, $L, $L, $L)",
-                AndroidTypeNames.CURSOR,
+                CURSOR.toJavaPoet(),
                 cursorVar,
                 RoomTypeNames.DB_UTIL,
                 dbField,
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/MultiTypedPagingSourceQueryResultBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/MultiTypedPagingSourceQueryResultBinder.kt
index e88f895..5daba2a 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/MultiTypedPagingSourceQueryResultBinder.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/MultiTypedPagingSourceQueryResultBinder.kt
@@ -16,7 +16,8 @@
 
 package androidx.room.solver.query.result
 
-import androidx.room.ext.AndroidTypeNames
+import androidx.room.compiler.codegen.toJavaPoet
+import androidx.room.ext.AndroidTypeNames.CURSOR
 import androidx.room.ext.CommonTypeNames
 import androidx.room.ext.L
 import androidx.room.ext.N
@@ -75,7 +76,7 @@
             addAnnotation(Override::class.java)
             addModifiers(Modifier.PROTECTED)
             returns(ParameterizedTypeName.get(CommonTypeNames.LIST, itemTypeName))
-            val cursorParam = ParameterSpec.builder(AndroidTypeNames.CURSOR, "cursor")
+            val cursorParam = ParameterSpec.builder(CURSOR.toJavaPoet(), "cursor")
                 .build()
             addParameter(cursorParam)
             val resultVar = scope.getTmpVar("_result")
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/MultimapQueryResultAdapter.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/MultimapQueryResultAdapter.kt
index fb94243..3b2c0f3 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/MultimapQueryResultAdapter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/MultimapQueryResultAdapter.kt
@@ -16,6 +16,7 @@
 
 package androidx.room.solver.query.result
 
+import androidx.room.compiler.codegen.toJavaPoet
 import androidx.room.compiler.processing.XType
 import androidx.room.ext.L
 import androidx.room.ext.W
@@ -24,6 +25,9 @@
 import androidx.room.parser.ParsedQuery
 import androidx.room.processor.Context
 import androidx.room.processor.ProcessorErrors
+import androidx.room.processor.ProcessorErrors.AmbiguousColumnLocation.ENTITY
+import androidx.room.processor.ProcessorErrors.AmbiguousColumnLocation.MAP_INFO
+import androidx.room.processor.ProcessorErrors.AmbiguousColumnLocation.POJO
 import androidx.room.solver.types.CursorValueReader
 import androidx.room.vo.ColumnIndexVar
 import androidx.room.vo.MapInfo
@@ -72,11 +76,11 @@
                 val ambiguousColumnName = it.usedColumns.first()
                 val (location, objectTypeName) = when (it) {
                     is SingleNamedColumnRowAdapter.SingleNamedColumnRowMapping ->
-                        ProcessorErrors.AmbiguousColumnLocation.MAP_INFO to null
+                        MAP_INFO to null
                     is PojoRowAdapter.PojoMapping ->
-                        ProcessorErrors.AmbiguousColumnLocation.POJO to it.pojo.typeName
+                        POJO to it.pojo.typeName.toJavaPoet()
                     is EntityRowAdapter.EntityMapping ->
-                        ProcessorErrors.AmbiguousColumnLocation.ENTITY to it.entity.typeName
+                        ENTITY to it.entity.typeName.toJavaPoet()
                     else -> error("Unknown mapping type: $it")
                 }
                 context.logger.w(
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/PojoRowAdapter.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/PojoRowAdapter.kt
index ffff340f..14b74ce 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/PojoRowAdapter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/PojoRowAdapter.kt
@@ -16,8 +16,18 @@
 
 package androidx.room.solver.query.result
 
+import androidx.room.compiler.codegen.CodeLanguage
+import androidx.room.compiler.codegen.VisibilityModifier
+import androidx.room.compiler.codegen.XClassName
+import androidx.room.compiler.codegen.XFunSpec
+import androidx.room.compiler.codegen.XTypeName
+import androidx.room.compiler.codegen.XTypeSpec
+import androidx.room.compiler.codegen.addOriginatingElement
+import androidx.room.compiler.codegen.toJavaPoet
 import androidx.room.compiler.processing.XType
+import androidx.room.ext.AndroidTypeNames
 import androidx.room.ext.L
+import androidx.room.ext.T
 import androidx.room.parser.ParsedQuery
 import androidx.room.processor.Context
 import androidx.room.processor.ProcessorErrors
@@ -29,6 +39,8 @@
 import androidx.room.vo.Pojo
 import androidx.room.vo.RelationCollector
 import androidx.room.writer.FieldReadWriteWriter
+import androidx.room.writer.TypeWriter
+import kotlin.math.abs
 
 /**
  * Creates the entity from the given info.
@@ -36,9 +48,9 @@
  * The info comes from the query processor so we know about the order of columns in the result etc.
  */
 class PojoRowAdapter(
-    context: Context,
+    private val context: Context,
     private val info: QueryResultInfo?,
-    query: ParsedQuery?,
+    private val query: ParsedQuery?,
     val pojo: Pojo,
     out: XType
 ) : QueryMappedRowAdapter(out) {
@@ -69,7 +81,7 @@
             if (nonNulls.isNotEmpty()) {
                 context.logger.e(
                     ProcessorErrors.pojoMissingNonNull(
-                        pojoTypeName = pojo.typeName,
+                        pojoTypeName = pojo.typeName.toJavaPoet(),
                         missingPojoFields = nonNulls.map { it.name },
                         allQueryColumns = info.columns.map { it.name }
                     )
@@ -134,7 +146,7 @@
     }
 
     override fun convert(outVarName: String, cursorVarName: String, scope: CodeGenScope) {
-        scope.builder().apply {
+        fun doReadFromCursor(outVarName: String, scope: CodeGenScope) {
             FieldReadWriteWriter.readFromCursor(
                 outVar = outVarName,
                 outPojo = pojo,
@@ -144,6 +156,52 @@
                 scope = scope
             )
         }
+        // TODO(b/127483380): Inline in code gen scope once Kotlin code gen progresses.
+        if (relationCollectors.isEmpty() && context.codeLanguage == CodeLanguage.KOTLIN) {
+            // The name of the class is based on the query, possible to be collisions, but good,
+            // enough for now.
+            val nameHash = abs(query?.original?.hashCode() ?: out.asTypeName().hashCode())
+            val className = XClassName.get("androidx.room.temp", "PojoRowAdapter_$nameHash")
+            object : TypeWriter(CodeLanguage.KOTLIN) {
+                override fun createTypeSpecBuilder(): XTypeSpec.Builder {
+                    val readFunction = XFunSpec.builder(
+                        CodeLanguage.KOTLIN,
+                        "readFromCursor",
+                        VisibilityModifier.PUBLIC
+                    )
+                        .returns(out.asTypeName())
+                        .addParameter(AndroidTypeNames.CURSOR, cursorVarName)
+                        .apply {
+                            fieldsWithIndices.forEach {
+                                addParameter(XTypeName.PRIMITIVE_INT, it.indexVar)
+                            }
+                        }
+                        .addCode(
+                            CodeGenScope(this).apply {
+                                builder.addLocalVariable(outVarName, out.asTypeName())
+                                doReadFromCursor(outVarName, this)
+                                builder.addStatement("return %L", outVarName)
+                            }.generate()
+                        )
+                        .build()
+                    return XTypeSpec.classBuilder(codeLanguage, className)
+                        .addOriginatingElement(pojo.element)
+                        .addFunction(readFunction)
+                }
+            }.write(context.processingEnv)
+            scope.builder().apply {
+                addStatement("$L = new $T().readFromCursor($L, $L)",
+                    outVarName,
+                    className.toJavaPoet(),
+                    cursorVarName,
+                    fieldsWithIndices.joinToString { it.indexVar }
+                )
+            }
+        } else {
+            scope.builder().apply {
+                doReadFromCursor(outVarName, scope)
+            }
+        }
     }
 
     override fun getDefaultIndexAdapter() = indexAdapter
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/PositionalDataSourceQueryResultBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/PositionalDataSourceQueryResultBinder.kt
index 6e92eea..9c8df06 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/PositionalDataSourceQueryResultBinder.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/PositionalDataSourceQueryResultBinder.kt
@@ -16,7 +16,8 @@
 
 package androidx.room.solver.query.result
 
-import androidx.room.ext.AndroidTypeNames
+import androidx.room.compiler.codegen.toJavaPoet
+import androidx.room.ext.AndroidTypeNames.CURSOR
 import androidx.room.ext.CommonTypeNames
 import androidx.room.ext.L
 import androidx.room.ext.N
@@ -71,7 +72,7 @@
             addAnnotation(Override::class.java)
             addModifiers(Modifier.PROTECTED)
             returns(ParameterizedTypeName.get(CommonTypeNames.LIST, itemTypeName))
-            val cursorParam = ParameterSpec.builder(AndroidTypeNames.CURSOR, "cursor")
+            val cursorParam = ParameterSpec.builder(CURSOR.toJavaPoet(), "cursor")
                 .build()
             addParameter(cursorParam)
             val resultVar = scope.getTmpVar("_res")
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/RxCallableQueryResultBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/RxCallableQueryResultBinder.kt
index 5d96bb9..919397e 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/RxCallableQueryResultBinder.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/RxCallableQueryResultBinder.kt
@@ -16,14 +16,15 @@
 
 package androidx.room.solver.query.result
 
-import androidx.room.ext.AndroidTypeNames
+import androidx.room.compiler.codegen.toJavaPoet
+import androidx.room.compiler.processing.XType
+import androidx.room.ext.AndroidTypeNames.CURSOR
 import androidx.room.ext.CallableTypeSpecBuilder
 import androidx.room.ext.L
 import androidx.room.ext.N
 import androidx.room.ext.RoomTypeNames
 import androidx.room.ext.S
 import androidx.room.ext.T
-import androidx.room.compiler.processing.XType
 import androidx.room.solver.CodeGenScope
 import androidx.room.solver.RxType
 import com.squareup.javapoet.FieldSpec
@@ -84,7 +85,7 @@
         val cursorVar = scope.getTmpVar("_cursor")
         addStatement(
             "final $T $L = $T.query($N, $L, $L, $L)",
-            AndroidTypeNames.CURSOR,
+            CURSOR.toJavaPoet(),
             cursorVar,
             RoomTypeNames.DB_UTIL,
             dbField,
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/types/PrimitiveColumnTypeAdapter.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/types/PrimitiveColumnTypeAdapter.kt
index 9700977..4f4e610 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/types/PrimitiveColumnTypeAdapter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/types/PrimitiveColumnTypeAdapter.kt
@@ -16,71 +16,77 @@
 
 package androidx.room.solver.types
 
+import androidx.room.compiler.codegen.CodeLanguage
+import androidx.room.compiler.codegen.XCodeBlock
+import androidx.room.compiler.codegen.XTypeName
+import androidx.room.compiler.codegen.XTypeName.Companion.PRIMITIVE_BYTE
+import androidx.room.compiler.codegen.XTypeName.Companion.PRIMITIVE_CHAR
+import androidx.room.compiler.codegen.XTypeName.Companion.PRIMITIVE_DOUBLE
+import androidx.room.compiler.codegen.XTypeName.Companion.PRIMITIVE_FLOAT
+import androidx.room.compiler.codegen.XTypeName.Companion.PRIMITIVE_INT
+import androidx.room.compiler.codegen.XTypeName.Companion.PRIMITIVE_LONG
+import androidx.room.compiler.codegen.XTypeName.Companion.PRIMITIVE_SHORT
 import androidx.room.compiler.processing.XProcessingEnv
 import androidx.room.compiler.processing.XType
-import androidx.room.ext.L
-import androidx.room.ext.capitalize
 import androidx.room.parser.SQLTypeAffinity
-import androidx.room.parser.SQLTypeAffinity.REAL
 import androidx.room.solver.CodeGenScope
-import com.squareup.javapoet.TypeName.BYTE
-import com.squareup.javapoet.TypeName.CHAR
-import com.squareup.javapoet.TypeName.DOUBLE
-import com.squareup.javapoet.TypeName.FLOAT
-import com.squareup.javapoet.TypeName.INT
-import com.squareup.javapoet.TypeName.LONG
-import com.squareup.javapoet.TypeName.SHORT
-import java.util.Locale
 
 /**
  * Adapters for all primitives that has direct cursor mappings.
  */
-open class PrimitiveColumnTypeAdapter(
+class PrimitiveColumnTypeAdapter(
     out: XType,
-    val cursorGetter: String,
-    val stmtSetter: String,
-    typeAffinity: SQLTypeAffinity
+    typeAffinity: SQLTypeAffinity,
+    val primitive: Primitive,
 ) : ColumnTypeAdapter(out, typeAffinity) {
-    val cast = if (cursorGetter == "get${out.typeName.toString().capitalize(Locale.US)}")
-        ""
-    else
-        "(${out.typeName}) "
 
     companion object {
+
+        enum class Primitive(
+            val typeName: XTypeName,
+            val cursorGetter: String,
+            val stmtSetter: String,
+        ) {
+            INT(PRIMITIVE_INT, "getInt", "bindLong"),
+            SHORT(PRIMITIVE_SHORT, "getShort", "bindLong"),
+            BYTE(PRIMITIVE_BYTE, "getShort", "bindLong"),
+            LONG(PRIMITIVE_LONG, "getLong", "bindLong"),
+            CHAR(PRIMITIVE_CHAR, "getInt", "bindLong"),
+            FLOAT(PRIMITIVE_FLOAT, "getFloat", "bindDouble"),
+            DOUBLE(PRIMITIVE_DOUBLE, "getDouble", "bindDouble"),
+        }
+
+        private fun getAffinity(primitive: Primitive) = when (primitive) {
+            Primitive.INT, Primitive.SHORT, Primitive.BYTE, Primitive.LONG, Primitive.CHAR ->
+                SQLTypeAffinity.INTEGER
+            Primitive.FLOAT, Primitive.DOUBLE ->
+                SQLTypeAffinity.REAL
+        }
+
         fun createPrimitiveAdapters(
             processingEnvironment: XProcessingEnv
         ): List<PrimitiveColumnTypeAdapter> {
-            return listOf(
-                Triple(INT, "getInt", "bindLong"),
-                Triple(SHORT, "getShort", "bindLong"),
-                Triple(BYTE, "getShort", "bindLong"),
-                Triple(LONG, "getLong", "bindLong"),
-                Triple(CHAR, "getInt", "bindLong"),
-                Triple(FLOAT, "getFloat", "bindDouble"),
-                Triple(DOUBLE, "getDouble", "bindDouble")
-            ).map {
+            return Primitive.values().map {
                 PrimitiveColumnTypeAdapter(
-                    out = processingEnvironment.requireType(it.first),
-                    cursorGetter = it.second,
-                    stmtSetter = it.third,
-                    typeAffinity = when (it.first) {
-                        INT, SHORT, BYTE, LONG, CHAR -> SQLTypeAffinity.INTEGER
-                        FLOAT, DOUBLE -> REAL
-                        else -> throw IllegalArgumentException("invalid type")
-                    }
+                    out = processingEnvironment.requireType(it.typeName),
+                    typeAffinity = getAffinity(it),
+                    primitive = it
                 )
             }
         }
     }
 
+    private val cursorGetter = primitive.cursorGetter
+    private val stmtSetter = primitive.stmtSetter
+
     override fun bindToStmt(
         stmtName: String,
         indexVarName: String,
         valueVarName: String,
         scope: CodeGenScope
     ) {
-        scope.builder()
-            .addStatement("$L.$L($L, $L)", stmtName, stmtSetter, indexVarName, valueVarName)
+        scope.builder
+            .addStatement("%L.%L(%L, %L)", stmtName, stmtSetter, indexVarName, valueVarName)
     }
 
     override fun readFromCursor(
@@ -89,10 +95,33 @@
         indexVarName: String,
         scope: CodeGenScope
     ) {
-        scope.builder()
-            .addStatement(
-                "$L = $L$L.$L($L)", outVarName, cast, cursorVarName,
-                cursorGetter, indexVarName
-            )
+        scope.builder.addStatement(
+            "%L = %L",
+            outVarName,
+            XCodeBlock.of(
+                scope.language,
+                "%L.%L(%L)",
+                cursorVarName,
+                cursorGetter,
+                indexVarName
+            ).let {
+                // These primitives don't have an exact cursor getter.
+                val castFunction = when (primitive) {
+                    Primitive.BYTE -> "toByte"
+                    Primitive.CHAR -> "toChar"
+                    else -> null
+                } ?: return@let it
+                when (it.language) {
+                    // For Java a cast will suffice
+                    CodeLanguage.JAVA -> {
+                        XCodeBlock.ofCast(it.language, out.asTypeName(), it)
+                    }
+                    // For Kotlin a converter function is emitted
+                    CodeLanguage.KOTLIN -> {
+                        XCodeBlock.of(it.language, "%L.%L()", it, castFunction)
+                    }
+                }
+            }
+        )
     }
 }
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/vo/Constructor.kt b/room/room-compiler/src/main/kotlin/androidx/room/vo/Constructor.kt
index 566d845..8cba86e 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/vo/Constructor.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/vo/Constructor.kt
@@ -16,15 +16,13 @@
 
 package androidx.room.vo
 
-import androidx.room.ext.L
-import androidx.room.ext.T
+import androidx.room.compiler.codegen.XCodeBlock
 import androidx.room.compiler.processing.XExecutableElement
 import androidx.room.compiler.processing.isConstructor
 import androidx.room.compiler.processing.isMethod
-import com.squareup.javapoet.CodeBlock
 
 /**
- * For each Entity / Pojo we process has a constructor. It might be the empty constructor or a
+ * Each Entity / Pojo we process has a constructor. It might be the empty constructor or a
  * constructor with fields. It can also be a static factory method, such as in the case of an
  * AutoValue Pojo.
  */
@@ -40,21 +38,28 @@
         }
     }
 
-    fun writeConstructor(outVar: String, args: String, builder: CodeBlock.Builder) {
+    fun writeConstructor(outVar: String, args: String, builder: XCodeBlock.Builder) {
         when {
             element.isConstructor() -> {
                 builder.addStatement(
-                    "$L = new $T($L)", outVar,
-                    element.enclosingElement.className, args
+                    "%L = %L",
+                    outVar,
+                    XCodeBlock.ofNewInstance(
+                        builder.language,
+                        element.enclosingElement.asClassName(),
+                        args
+                    )
                 )
             }
             element.isMethod() -> {
                 // TODO when we generate Kotlin code, we need to handle not having enclosing
                 //  elements.
                 builder.addStatement(
-                    "$L = $T.$L($L)", outVar,
-                    element.enclosingElement.className,
-                    element.jvmName, args
+                    "%L = %T.%L(%L)",
+                    outVar,
+                    element.enclosingElement.asClassName(),
+                    element.jvmName,
+                    args
                 )
             }
             else -> throw IllegalStateException("Invalid constructor kind ${element.kindName()}")
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/vo/EntityOrView.kt b/room/room-compiler/src/main/kotlin/androidx/room/vo/EntityOrView.kt
index acfe6a1..b52fa68 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/vo/EntityOrView.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/vo/EntityOrView.kt
@@ -16,7 +16,7 @@
 
 package androidx.room.vo
 
-import com.squareup.javapoet.TypeName
+import androidx.room.compiler.codegen.XTypeName
 
 /**
  * Common interface between [Entity] and [DatabaseView].
@@ -28,5 +28,5 @@
      */
     val tableName: String
 
-    val typeName: TypeName
+    val typeName: XTypeName
 }
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/vo/Field.kt b/room/room-compiler/src/main/kotlin/androidx/room/vo/Field.kt
index fc84ff6..f2e9bf4 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/vo/Field.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/vo/Field.kt
@@ -16,6 +16,8 @@
 
 package androidx.room.vo
 
+import androidx.room.compiler.codegen.XTypeName
+import androidx.room.compiler.codegen.toJavaPoet
 import androidx.room.compiler.processing.XFieldElement
 import androidx.room.compiler.processing.XNullability
 import androidx.room.compiler.processing.XType
@@ -50,9 +52,10 @@
     lateinit var setter: FieldSetter
     // binds the field into a statement
     var statementBinder: StatementValueBinder? = null
+
     // reads this field from a cursor column
     var cursorValueReader: CursorValueReader? = null
-    val typeName: TypeName by lazy { type.typeName }
+    val typeName: XTypeName by lazy { type.asTypeName() }
 
     override fun getIdKey(): String {
         return buildString {
@@ -99,7 +102,10 @@
                 result.add(name.substring(1).decapitalize(Locale.US))
             }
 
-            if (typeName == TypeName.BOOLEAN || typeName == TypeName.BOOLEAN.box()) {
+            if (
+                typeName.toJavaPoet() == TypeName.BOOLEAN ||
+                typeName.toJavaPoet() == TypeName.BOOLEAN.box()
+            ) {
                 if (name.length > 2 && name.startsWith("is") && name[2].isUpperCase()) {
                     result.add(name.substring(2).decapitalize(Locale.US))
                 }
@@ -113,7 +119,10 @@
 
     val getterNameWithVariations by lazy {
         nameWithVariations.map { "get${it.capitalize(Locale.US)}" } +
-            if (typeName == TypeName.BOOLEAN || typeName == TypeName.BOOLEAN.box()) {
+            if (
+                typeName.toJavaPoet() == TypeName.BOOLEAN ||
+                typeName.toJavaPoet() == TypeName.BOOLEAN.box()
+            ) {
                 nameWithVariations.flatMap {
                     listOf("is${it.capitalize(Locale.US)}", "has${it.capitalize(Locale.US)}")
                 }
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/vo/FieldGetter.kt b/room/room-compiler/src/main/kotlin/androidx/room/vo/FieldGetter.kt
index ae3ce9c..d3a040e 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/vo/FieldGetter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/vo/FieldGetter.kt
@@ -16,20 +16,22 @@
 
 package androidx.room.vo
 
-import androidx.room.ext.L
-import androidx.room.ext.T
+import androidx.room.compiler.codegen.XCodeBlock
 import androidx.room.compiler.processing.XType
-import com.squareup.javapoet.CodeBlock
 
 data class FieldGetter(val jvmName: String, val type: XType, val callType: CallType) {
-    fun writeGet(ownerVar: String, outVar: String, builder: CodeBlock.Builder) {
+    fun writeGet(ownerVar: String, outVar: String, builder: XCodeBlock.Builder) {
         val stmt = when (callType) {
-            CallType.FIELD -> "final $T $L = $L.$L"
-            CallType.METHOD -> "final $T $L = $L.$L()"
+            CallType.FIELD -> "%L.%L"
+            CallType.METHOD -> "%L.%L()"
             CallType.CONSTRUCTOR -> null
         }
-        stmt?.let {
-            builder.addStatement(stmt, type.typeName, outVar, ownerVar, jvmName)
+        if (stmt != null) {
+            builder.addLocalVariable(
+                name = outVar,
+                typeName = type.asTypeName(),
+                assignExpr = XCodeBlock.of(builder.language, stmt, ownerVar, jvmName)
+            )
         }
     }
 }
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/vo/FieldSetter.kt b/room/room-compiler/src/main/kotlin/androidx/room/vo/FieldSetter.kt
index 01a3ba3..a8086bc 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/vo/FieldSetter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/vo/FieldSetter.kt
@@ -16,18 +16,17 @@
 
 package androidx.room.vo
 
-import androidx.room.ext.L
+import androidx.room.compiler.codegen.XCodeBlock
 import androidx.room.compiler.processing.XType
-import com.squareup.javapoet.CodeBlock
 
 data class FieldSetter(val jvmName: String, val type: XType, val callType: CallType) {
-    fun writeSet(ownerVar: String, inVar: String, builder: CodeBlock.Builder) {
+    fun writeSet(ownerVar: String, inVar: String, builder: XCodeBlock.Builder) {
         val stmt = when (callType) {
-            CallType.FIELD -> "$L.$L = $L"
-            CallType.METHOD -> "$L.$L($L)"
+            CallType.FIELD -> "%L.%L = %L"
+            CallType.METHOD -> "%L.%L(%L)"
             CallType.CONSTRUCTOR -> null
         }
-        stmt?.let {
+        if (stmt != null) {
             builder.addStatement(stmt, ownerVar, jvmName, inVar)
         }
     }
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/vo/Pojo.kt b/room/room-compiler/src/main/kotlin/androidx/room/vo/Pojo.kt
index b463a7b..25241e9 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/vo/Pojo.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/vo/Pojo.kt
@@ -16,11 +16,11 @@
 
 package androidx.room.vo
 
+import androidx.room.compiler.codegen.XTypeName
 import androidx.room.compiler.processing.XType
 import androidx.room.compiler.processing.XTypeElement
 import androidx.room.processor.DatabaseViewProcessor
 import androidx.room.processor.EntityProcessor
-import com.squareup.javapoet.TypeName
 
 /**
  * A class is turned into a Pojo if it is used in a query response.
@@ -33,7 +33,7 @@
     val relations: List<Relation>,
     val constructor: Constructor? = null
 ) : HasFields {
-    val typeName: TypeName by lazy { type.typeName }
+    val typeName: XTypeName by lazy { type.asTypeName() }
 
     override val fields = Fields(fields)
 
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/vo/RelationCollector.kt b/room/room-compiler/src/main/kotlin/androidx/room/vo/RelationCollector.kt
index c14b90a..00a4ccb 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/vo/RelationCollector.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/vo/RelationCollector.kt
@@ -16,6 +16,7 @@
 
 package androidx.room.vo
 
+import androidx.room.compiler.codegen.toJavaPoet
 import androidx.room.compiler.processing.XType
 import androidx.room.ext.CollectionTypeNames
 import androidx.room.ext.CommonTypeNames
@@ -44,8 +45,6 @@
 import com.squareup.javapoet.ParameterizedTypeName
 import com.squareup.javapoet.TypeName
 import java.nio.ByteBuffer
-import java.util.ArrayList
-import java.util.HashSet
 import java.util.Locale
 
 /**
@@ -402,8 +401,8 @@
 
         // Gets the resulting relation type name. (i.e. the Pojo's @Relation field type name.)
         private fun relationTypeFor(relation: Relation) =
-            if (relation.field.typeName is ParameterizedTypeName) {
-                val paramType = relation.field.typeName as ParameterizedTypeName
+            if (relation.field.typeName.toJavaPoet() is ParameterizedTypeName) {
+                val paramType = relation.field.typeName.toJavaPoet() as ParameterizedTypeName
                 val paramTypeName = if (paramType.rawType == CommonTypeNames.LIST) {
                     ParameterizedTypeName.get(
                         ClassName.get(ArrayList::class.java),
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/vo/ShortcutEntity.kt b/room/room-compiler/src/main/kotlin/androidx/room/vo/ShortcutEntity.kt
index 01c6e60..a87b33f 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/vo/ShortcutEntity.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/vo/ShortcutEntity.kt
@@ -16,6 +16,8 @@
 
 package androidx.room.vo
 
+import androidx.room.compiler.codegen.toJavaPoet
+
 /**
  * Represents a shortcut method parameter entity.
  */
@@ -24,7 +26,7 @@
     private val partialEntity: Pojo? // the partial entity
 ) {
     val tableName = entity.tableName
-    val entityTypeName = entity.typeName
+    val entityTypeName = entity.typeName.toJavaPoet()
     val primaryKey by lazy {
         if (partialEntity == null) {
             entity.primaryKey
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/writer/AutoMigrationWriter.kt b/room/room-compiler/src/main/kotlin/androidx/room/writer/AutoMigrationWriter.kt
index db75984..9898cf0 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/writer/AutoMigrationWriter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/writer/AutoMigrationWriter.kt
@@ -19,7 +19,6 @@
 import androidx.room.compiler.codegen.CodeLanguage
 import androidx.room.compiler.codegen.VisibilityModifier
 import androidx.room.compiler.codegen.XCodeBlock
-import androidx.room.compiler.codegen.XCodeBlock.Builder.Companion.apply
 import androidx.room.compiler.codegen.XFunSpec
 import androidx.room.compiler.codegen.XFunSpec.Builder.Companion.addStatement
 import androidx.room.compiler.codegen.XTypeSpec
@@ -28,11 +27,9 @@
 import androidx.room.compiler.processing.XTypeElement
 import androidx.room.ext.RoomTypeNames
 import androidx.room.ext.SupportDbTypeNames
-import androidx.room.ext.T
 import androidx.room.migration.bundle.EntityBundle
 import androidx.room.migration.bundle.FtsEntityBundle
 import androidx.room.vo.AutoMigration
-import com.squareup.kotlinpoet.javapoet.toKClassName
 
 /**
  * Writes the implementation of migrations that were annotated with @AutoMigration.
@@ -63,14 +60,10 @@
                     name = "callback",
                     visibility = VisibilityModifier.PRIVATE,
                     initExpr = if (!autoMigration.isSpecProvided) {
-                        XCodeBlock.builder(codeLanguage).apply(
-                            javaCodeBuilder = {
-                                add("new $T()", autoMigration.specClassName)
-                            },
-                            kotlinCodeBuilder = {
-                                add("%T()", autoMigration.specClassName.toKClassName())
-                            }
-                        ).build()
+                        XCodeBlock.ofNewInstance(
+                            codeLanguage,
+                            autoMigration.specClassName.toXClassName()
+                        )
                     } else {
                         null
                     }
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/writer/DaoWriter.kt b/room/room-compiler/src/main/kotlin/androidx/room/writer/DaoWriter.kt
index 9b465ec..d5ac75a 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/writer/DaoWriter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/writer/DaoWriter.kt
@@ -89,7 +89,7 @@
 
         private fun shortcutEntityFieldNamePart(shortcutEntity: ShortcutEntity): String {
             return if (shortcutEntity.isPartialEntity) {
-                typeNameToFieldName(shortcutEntity.pojo.typeName) + "As" +
+                typeNameToFieldName(shortcutEntity.pojo.typeName.toJavaPoet()) + "As" +
                     typeNameToFieldName(shortcutEntity.entityTypeName)
             } else {
                 typeNameToFieldName(shortcutEntity.entityTypeName)
@@ -635,11 +635,12 @@
     ) : SharedFieldSpec(
         baseName = "insertionAdapterOf${shortcutEntityFieldNamePart(shortcutEntity)}",
         type = ParameterizedTypeName.get(
-            RoomTypeNames.INSERTION_ADAPTER, shortcutEntity.pojo.typeName
+            RoomTypeNames.INSERTION_ADAPTER, shortcutEntity.pojo.typeName.toJavaPoet()
         )
     ) {
         override fun getUniqueKey(): String {
-            return "${shortcutEntity.pojo.typeName}-${shortcutEntity.entityTypeName}$onConflictText"
+            return "${shortcutEntity.pojo.typeName.toJavaPoet()}-" +
+                "${shortcutEntity.entityTypeName}$onConflictText"
         }
 
         override fun prepare(writer: TypeWriter, builder: FieldSpec.Builder) {
@@ -654,7 +655,7 @@
     ) : SharedFieldSpec(
         baseName = "${methodPrefix}AdapterOf${shortcutEntityFieldNamePart(shortcutEntity)}",
         type = ParameterizedTypeName.get(
-            RoomTypeNames.DELETE_OR_UPDATE_ADAPTER, shortcutEntity.pojo.typeName
+            RoomTypeNames.DELETE_OR_UPDATE_ADAPTER, shortcutEntity.pojo.typeName.toJavaPoet()
         )
     ) {
         override fun prepare(writer: TypeWriter, builder: FieldSpec.Builder) {
@@ -662,7 +663,7 @@
         }
 
         override fun getUniqueKey(): String {
-            return "${shortcutEntity.pojo.typeName}-${shortcutEntity.entityTypeName}" +
+            return "${shortcutEntity.pojo.typeName.toJavaPoet()}-${shortcutEntity.entityTypeName}" +
                 "$methodPrefix$onConflictText"
         }
     }
@@ -672,11 +673,11 @@
     ) : SharedFieldSpec(
         baseName = "upsertionAdapterOf${shortcutEntityFieldNamePart(shortcutEntity)}",
         type = ParameterizedTypeName.get(
-            RoomTypeNames.UPSERTION_ADAPTER, shortcutEntity.pojo.typeName
+            RoomTypeNames.UPSERTION_ADAPTER, shortcutEntity.pojo.typeName.toJavaPoet()
         )
     ) {
         override fun getUniqueKey(): String {
-            return "${shortcutEntity.pojo.typeName}-${shortcutEntity.entityTypeName}"
+            return "${shortcutEntity.pojo.typeName.toJavaPoet()}-${shortcutEntity.entityTypeName}"
         }
 
         override fun prepare(writer: TypeWriter, builder: FieldSpec.Builder) {
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/writer/EntityCursorConverterWriter.kt b/room/room-compiler/src/main/kotlin/androidx/room/writer/EntityCursorConverterWriter.kt
index af86099..891711a 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/writer/EntityCursorConverterWriter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/writer/EntityCursorConverterWriter.kt
@@ -16,7 +16,8 @@
 
 package androidx.room.writer
 
-import androidx.room.ext.AndroidTypeNames
+import androidx.room.compiler.codegen.toJavaPoet
+import androidx.room.ext.AndroidTypeNames.CURSOR
 import androidx.room.ext.L
 import androidx.room.ext.N
 import androidx.room.ext.RoomTypeNames
@@ -35,7 +36,7 @@
 import javax.lang.model.element.Modifier.PRIVATE
 
 class EntityCursorConverterWriter(val entity: Entity) : TypeWriter.SharedMethodSpec(
-    "entityCursorConverter_${entity.typeName.toString().stripNonJava()}"
+    "entityCursorConverter_${entity.typeName.toJavaPoet().toString().stripNonJava()}"
 ) {
     override fun getUniqueKey(): String {
         return "generic_entity_converter_of_${entity.element.qualifiedName}"
@@ -44,10 +45,10 @@
     override fun prepare(methodName: String, writer: TypeWriter, builder: MethodSpec.Builder) {
         builder.apply {
             val cursorParam = ParameterSpec
-                .builder(AndroidTypeNames.CURSOR, "cursor").build()
+                .builder(CURSOR.toJavaPoet(), "cursor").build()
             addParameter(cursorParam)
             addModifiers(PRIVATE)
-            returns(entity.typeName)
+            returns(entity.typeName.toJavaPoet())
             addCode(buildConvertMethodBody(writer, cursorParam))
         }
     }
@@ -56,7 +57,11 @@
         val scope = CodeGenScope(writer)
         val entityVar = scope.getTmpVar("_entity")
         scope.builder().apply {
-            scope.builder().addStatement("final $T $L", entity.typeName, entityVar)
+            scope.builder().addStatement(
+                "final $T $L",
+                entity.typeName.toJavaPoet(),
+                entityVar
+            )
             val fieldsWithIndices = entity.fields.map {
                 val indexVar = scope.getTmpVar(
                     "_cursorIndexOf${it.name.stripNonJava().capitalize(Locale.US)}"
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/writer/EntityDeletionAdapterWriter.kt b/room/room-compiler/src/main/kotlin/androidx/room/writer/EntityDeletionAdapterWriter.kt
index 4963ac1..5766ce6 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/writer/EntityDeletionAdapterWriter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/writer/EntityDeletionAdapterWriter.kt
@@ -16,6 +16,7 @@
 
 package androidx.room.writer
 
+import androidx.room.compiler.codegen.toJavaPoet
 import androidx.room.ext.L
 import androidx.room.ext.RoomTypeNames
 import androidx.room.ext.S
@@ -48,7 +49,7 @@
             }
             return EntityDeletionAdapterWriter(
                 tableName = entity.tableName,
-                pojoTypeName = entity.pojo.typeName,
+                pojoTypeName = entity.pojo.typeName.toJavaPoet(),
                 fields = fieldsToUse
             )
         }
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/writer/EntityInsertionAdapterWriter.kt b/room/room-compiler/src/main/kotlin/androidx/room/writer/EntityInsertionAdapterWriter.kt
index cd328f7..fc6e477 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/writer/EntityInsertionAdapterWriter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/writer/EntityInsertionAdapterWriter.kt
@@ -16,6 +16,7 @@
 
 package androidx.room.writer
 
+import androidx.room.compiler.codegen.toJavaPoet
 import androidx.room.compiler.processing.XNullability
 import androidx.room.ext.L
 import androidx.room.ext.RoomTypeNames
@@ -69,7 +70,12 @@
     fun createAnonymous(typeWriter: TypeWriter, dbParam: String): TypeSpec {
         @Suppress("RemoveSingleExpressionStringTemplate")
         return TypeSpec.anonymousClassBuilder("$L", dbParam).apply {
-            superclass(ParameterizedTypeName.get(RoomTypeNames.INSERTION_ADAPTER, pojo.typeName))
+            superclass(
+                ParameterizedTypeName.get(
+                    RoomTypeNames.INSERTION_ADAPTER,
+                    pojo.typeName.toJavaPoet()
+                )
+            )
             addMethod(
                 MethodSpec.methodBuilder("createQuery").apply {
                     addAnnotation(Override::class.java)
@@ -111,7 +117,9 @@
                         ).build()
                     )
                     val valueParam = "value"
-                    addParameter(ParameterSpec.builder(pojo.typeName, valueParam).build())
+                    addParameter(
+                        ParameterSpec.builder(pojo.typeName.toJavaPoet(), valueParam).build()
+                    )
                     val mapped = FieldWithIndex.byOrder(pojo.fields)
                     FieldReadWriteWriter.bindToStatement(
                         ownerVar = valueParam,
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/writer/EntityUpdateAdapterWriter.kt b/room/room-compiler/src/main/kotlin/androidx/room/writer/EntityUpdateAdapterWriter.kt
index e6023e5..e085422 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/writer/EntityUpdateAdapterWriter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/writer/EntityUpdateAdapterWriter.kt
@@ -16,6 +16,7 @@
 
 package androidx.room.writer
 
+import androidx.room.compiler.codegen.toJavaPoet
 import androidx.room.ext.L
 import androidx.room.ext.RoomTypeNames
 import androidx.room.ext.S
@@ -56,7 +57,7 @@
             superclass(
                 ParameterizedTypeName.get(
                     RoomTypeNames.DELETE_OR_UPDATE_ADAPTER,
-                    pojo.typeName
+                    pojo.typeName.toJavaPoet()
                 )
             )
             addMethod(
@@ -97,7 +98,9 @@
                         ).build()
                     )
                     val valueParam = "value"
-                    addParameter(ParameterSpec.builder(pojo.typeName, valueParam).build())
+                    addParameter(
+                        ParameterSpec.builder(pojo.typeName.toJavaPoet(), valueParam).build()
+                    )
                     val mappedField = FieldWithIndex.byOrder(pojo.fields)
                     FieldReadWriteWriter.bindToStatement(
                         ownerVar = valueParam,
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/writer/EntityUpsertionAdapterWriter.kt b/room/room-compiler/src/main/kotlin/androidx/room/writer/EntityUpsertionAdapterWriter.kt
index eab3b3e..ab9a46a 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/writer/EntityUpsertionAdapterWriter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/writer/EntityUpsertionAdapterWriter.kt
@@ -16,6 +16,7 @@
 
 package androidx.room.writer
 
+import androidx.room.compiler.codegen.toJavaPoet
 import androidx.room.ext.L
 import androidx.room.ext.RoomTypeNames
 import androidx.room.ext.T
@@ -44,7 +45,7 @@
         dbParam: String
     ): CodeBlock {
         val upsertionAdapter = ParameterizedTypeName.get(
-            RoomTypeNames.UPSERTION_ADAPTER, pojo.typeName
+            RoomTypeNames.UPSERTION_ADAPTER, pojo.typeName.toJavaPoet()
         )
         val insertionHelper = EntityInsertionAdapterWriter.create(entity, "")
             .createAnonymous(typeWriter, dbParam)
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/writer/FieldReadWriteWriter.kt b/room/room-compiler/src/main/kotlin/androidx/room/writer/FieldReadWriteWriter.kt
index 51719ba..bf053bb 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/writer/FieldReadWriteWriter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/writer/FieldReadWriteWriter.kt
@@ -16,8 +16,9 @@
 
 package androidx.room.writer
 
-import androidx.room.ext.L
-import androidx.room.ext.T
+import androidx.room.compiler.codegen.XCodeBlock
+import androidx.room.compiler.codegen.XTypeName
+import androidx.room.compiler.codegen.toJavaPoet
 import androidx.room.ext.capitalize
 import androidx.room.ext.defaultValue
 import androidx.room.solver.CodeGenScope
@@ -28,7 +29,6 @@
 import androidx.room.vo.FieldWithIndex
 import androidx.room.vo.Pojo
 import androidx.room.vo.RelationCollector
-import com.squareup.javapoet.TypeName
 import java.util.Locale
 
 /**
@@ -73,13 +73,10 @@
             val allParents = getAllParents(fieldsWithIndices.map { it.field })
             val rootNode = Node(rootVar, null)
             rootNode.directFields = fieldsWithIndices.filter { it.field.parent == null }
-            val parentNodes = allParents.associate {
-                Pair(
-                    it,
-                    Node(
-                        varName = scope.getTmpVar("_tmp${it.field.name.capitalize(Locale.US)}"),
-                        fieldParent = it
-                    )
+            val parentNodes = allParents.associateWith {
+                Node(
+                    varName = scope.getTmpVar("_tmp${it.field.name.capitalize(Locale.US)}"),
+                    fieldParent = it
                 )
             }
             parentNodes.values.forEach { node ->
@@ -118,15 +115,15 @@
                     fieldParent.getter.writeGet(
                         ownerVar = node.parentNode!!.varName,
                         outVar = node.varName,
-                        builder = scope.builder()
+                        builder = scope.builder
                     )
-                    scope.builder().apply {
-                        beginControlFlow("if($L != null)", node.varName).apply {
+                    scope.builder.apply {
+                        beginControlFlow("if (%L != null)", node.varName).apply {
                             bindWithDescendants()
                         }
                         nextControlFlow("else").apply {
                             node.allFields().forEach {
-                                addStatement("$L.bindNull($L)", stmtParamVar, it.indexVar)
+                                addStatement("%L.bindNull(%L)", stmtParamVar, it.indexVar)
                             }
                         }
                         endControlFlow()
@@ -146,16 +143,20 @@
         private fun construct(
             outVar: String,
             constructor: Constructor?,
-            typeName: TypeName,
+            typeName: XTypeName,
             localVariableNames: Map<String, FieldWithIndex>,
             localEmbeddeds: List<Node>,
             localRelations: Map<String, Field>,
             scope: CodeGenScope
         ) {
             if (constructor == null) {
-                // best hope code generation
-                scope.builder().apply {
-                    addStatement("$L = new $T()", outVar, typeName)
+                // Instantiate with default constructor, best hope for code generation
+                scope.builder.apply {
+                    addStatement(
+                        "%L = %L",
+                        outVar,
+                        XCodeBlock.ofNewInstance(scope.language, typeName)
+                    )
                 }
                 return
             }
@@ -173,7 +174,7 @@
                 }
             }
             val args = variableNames.joinToString(",") { it ?: "null" }
-            constructor.writeConstructor(outVar, args, scope.builder())
+            constructor.writeConstructor(outVar, args, scope.builder)
         }
 
         /**
@@ -196,7 +197,7 @@
                     }.associateBy { fwi ->
                         FieldReadWriteWriter(fwi).readIntoTmpVar(
                             cursorVar,
-                            fwi.field.setter.type.typeName,
+                            fwi.field.setter.type.asTypeName(),
                             scope
                         )
                     }
@@ -257,7 +258,7 @@
                         setter.writeSet(
                             ownerVar = node.varName,
                             inVar = varName,
-                            builder = scope.builder()
+                            builder = scope.builder
                         )
                     }
                     // assign relation fields that were not part of the constructor
@@ -267,7 +268,7 @@
                         field.setter.writeSet(
                             ownerVar = node.varName,
                             inVar = varName,
-                            builder = scope.builder()
+                            builder = scope.builder
                         )
                     }
                 }
@@ -277,9 +278,9 @@
                     readNode()
                 } else {
                     // always declare, we'll set below
-                    scope.builder().addStatement(
-                        "final $T $L", fieldParent.pojo.typeName,
-                        node.varName
+                    scope.builder.addLocalVariable(
+                        node.varName,
+                        fieldParent.pojo.typeName
                     )
                     if (fieldParent.nonNull) {
                         readNode()
@@ -289,15 +290,15 @@
                             if (it.alwaysExists) {
                                 "$cursorVar.isNull(${it.indexVar})"
                             } else {
-                                "( ${it.indexVar} == -1 || $cursorVar.isNull(${it.indexVar}))"
+                                "(${it.indexVar} == -1 || $cursorVar.isNull(${it.indexVar}))"
                             }
                         }
-                        scope.builder().apply {
-                            beginControlFlow("if (! ($L))", allNullCheck).apply {
+                        scope.builder.apply {
+                            beginControlFlow("if (!(%L))", allNullCheck).apply {
                                 readNode()
                             }
-                            nextControlFlow(" else ").apply {
-                                addStatement("$L = null", node.varName)
+                            nextControlFlow("else").apply {
+                                addStatement("%L = null", node.varName)
                             }
                             endControlFlow()
                         }
@@ -332,23 +333,21 @@
      * @param scope The code generation scope
      */
     private fun readFromCursor(ownerVar: String, cursorVar: String, scope: CodeGenScope) {
-        fun toRead() {
+        fun doRead() {
             field.cursorValueReader?.let { reader ->
-                scope.builder().apply {
+                scope.builder.apply {
                     when (field.setter.callType) {
                         CallType.FIELD -> {
-                            reader.readFromCursor(
-                                "$ownerVar.${field.setter.jvmName}", cursorVar,
-                                indexVar, scope
-                            )
+                            val outFieldName = "$ownerVar.${field.setter.jvmName}"
+                            reader.readFromCursor(outFieldName, cursorVar, indexVar, scope)
                         }
                         CallType.METHOD -> {
                             val tmpField = scope.getTmpVar(
                                 "_tmp${field.name.capitalize(Locale.US)}"
                             )
-                            addStatement("final $T $L", field.setter.type.typeName, tmpField)
+                            addLocalVariable(tmpField, field.setter.type.asTypeName())
                             reader.readFromCursor(tmpField, cursorVar, indexVar, scope)
-                            addStatement("$L.$L($L)", ownerVar, field.setter.jvmName, tmpField)
+                            addStatement("%L.%L(%L)", ownerVar, field.setter.jvmName, tmpField)
                         }
                         CallType.CONSTRUCTOR -> {
                             // no-op
@@ -358,11 +357,11 @@
             }
         }
         if (alwaysExists) {
-            toRead()
+            doRead()
         } else {
-            scope.builder().apply {
-                beginControlFlow("if ($L != -1)", indexVar).apply {
-                    toRead()
+            scope.builder.apply {
+                beginControlFlow("if (%L != -1)", indexVar).apply {
+                    doRead()
                 }
                 endControlFlow()
             }
@@ -374,17 +373,17 @@
      */
     fun readIntoTmpVar(
         cursorVar: String,
-        typeName: TypeName,
+        typeName: XTypeName,
         scope: CodeGenScope
     ): String {
         val tmpField = scope.getTmpVar("_tmp${field.name.capitalize(Locale.US)}")
-        scope.builder().apply {
-            addStatement("final $T $L", typeName, tmpField)
+        scope.builder.apply {
+            addLocalVariable(tmpField, typeName)
             if (alwaysExists) {
                 field.cursorValueReader?.readFromCursor(tmpField, cursorVar, indexVar, scope)
             } else {
-                beginControlFlow("if ($L == -1)", indexVar).apply {
-                    addStatement("$L = $L", tmpField, typeName.defaultValue())
+                beginControlFlow("if (%L == -1)", indexVar).apply {
+                    addStatement("%L = %L", tmpField, typeName.toJavaPoet().defaultValue())
                 }
                 nextControlFlow("else").apply {
                     field.cursorValueReader?.readFromCursor(tmpField, cursorVar, indexVar, scope)
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/writer/RelationCollectorMethodWriter.kt b/room/room-compiler/src/main/kotlin/androidx/room/writer/RelationCollectorMethodWriter.kt
index e7bd3a9..3d96052 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/writer/RelationCollectorMethodWriter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/writer/RelationCollectorMethodWriter.kt
@@ -16,7 +16,8 @@
 
 package androidx.room.writer
 
-import androidx.room.ext.AndroidTypeNames
+import androidx.room.compiler.codegen.toJavaPoet
+import androidx.room.ext.AndroidTypeNames.CURSOR
 import androidx.room.ext.CollectionTypeNames
 import androidx.room.ext.CommonTypeNames
 import androidx.room.ext.L
@@ -52,7 +53,7 @@
         val relation = collector.relation
         return "RelationCollectorMethodWriter" +
             "-${collector.mapTypeName}" +
-            "-${relation.entity.typeName}" +
+            "-${relation.entity.typeName.toJavaPoet()}" +
             "-${relation.entityField.columnName}" +
             "-${relation.pojoTypeName}" +
             "-${relation.createLoadAllSql()}"
@@ -191,7 +192,7 @@
             }
             addStatement(
                 "final $T $L = $T.query($N, $L, $L, $L)",
-                AndroidTypeNames.CURSOR,
+                CURSOR.toJavaPoet(),
                 cursorVar,
                 RoomTypeNames.DB_UTIL,
                 DaoWriter.dbField,
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/processor/DeleteOrUpdateShortcutMethodProcessorTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/processor/DeleteOrUpdateShortcutMethodProcessorTest.kt
index 9991b4d..f5ea0af 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/processor/DeleteOrUpdateShortcutMethodProcessorTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/processor/DeleteOrUpdateShortcutMethodProcessorTest.kt
@@ -18,6 +18,7 @@
 
 import COMMON
 import androidx.room.Dao
+import androidx.room.compiler.codegen.toJavaPoet
 import androidx.room.compiler.processing.XMethodElement
 import androidx.room.compiler.processing.XType
 import androidx.room.compiler.processing.XTypeElement
@@ -37,10 +38,10 @@
 import com.squareup.javapoet.ClassName
 import com.squareup.javapoet.ParameterizedTypeName
 import com.squareup.javapoet.TypeName
+import kotlin.reflect.KClass
 import org.hamcrest.CoreMatchers.`is`
 import org.hamcrest.MatcherAssert.assertThat
 import org.junit.Test
-import kotlin.reflect.KClass
 
 /**
  * Base test class for shortcut methods.
@@ -110,7 +111,10 @@
             assertThat(param.pojoType?.typeName, `is`(USER_TYPE_NAME))
             assertThat(shortcut.entities.size, `is`(1))
             assertThat(shortcut.entities["user"]?.isPartialEntity, `is`(false))
-            assertThat(shortcut.entities["user"]?.pojo?.typeName, `is`(USER_TYPE_NAME))
+            assertThat(
+                shortcut.entities["user"]?.pojo?.let { it.typeName.toJavaPoet() },
+                `is`(USER_TYPE_NAME)
+            )
         }
     }
 
@@ -149,8 +153,14 @@
                 assertThat(it.pojoType?.typeName, `is`(USER_TYPE_NAME))
             }
             assertThat(shortcut.entities.size, `is`(2))
-            assertThat(shortcut.entities["u1"]?.pojo?.typeName, `is`(USER_TYPE_NAME))
-            assertThat(shortcut.entities["u1"]?.pojo?.typeName, `is`(USER_TYPE_NAME))
+            assertThat(
+                shortcut.entities["u1"]?.pojo?.let { it.typeName.toJavaPoet() },
+                `is`(USER_TYPE_NAME)
+            )
+            assertThat(
+                shortcut.entities["u1"]?.pojo?.let { it.typeName.toJavaPoet() },
+                `is`(USER_TYPE_NAME)
+            )
             assertThat(
                 shortcut.parameters.map { it.name },
                 `is`(listOf("u1", "u2"))
@@ -190,7 +200,10 @@
                 )
                 assertThat(param.pojoType?.typeName, `is`(USER_TYPE_NAME))
                 assertThat(shortcut.entities.size, `is`(1))
-                assertThat(shortcut.entities["users"]?.pojo?.typeName, `is`(USER_TYPE_NAME))
+                assertThat(
+                    shortcut.entities["users"]?.pojo?.let { it.typeName.toJavaPoet() },
+                    `is`(USER_TYPE_NAME)
+                )
             }
         }
     }
@@ -213,7 +226,10 @@
                 )
             )
             assertThat(shortcut.entities.size, `is`(1))
-            assertThat(shortcut.entities["users"]?.pojo?.typeName, `is`(USER_TYPE_NAME))
+            assertThat(
+                shortcut.entities["users"]?.pojo?.let { it.typeName.toJavaPoet() },
+                `is`(USER_TYPE_NAME)
+            )
         }
     }
 
@@ -238,7 +254,10 @@
                 )
             )
             assertThat(shortcut.entities.size, `is`(1))
-            assertThat(shortcut.entities["users"]?.pojo?.typeName, `is`(USER_TYPE_NAME))
+            assertThat(
+                shortcut.entities["users"]?.pojo?.let { it.typeName.toJavaPoet() },
+                `is`(USER_TYPE_NAME)
+            )
         }
     }
 
@@ -263,7 +282,10 @@
                 )
             )
             assertThat(shortcut.entities.size, `is`(1))
-            assertThat(shortcut.entities["users"]?.pojo?.typeName, `is`(USER_TYPE_NAME))
+            assertThat(
+                shortcut.entities["users"]?.pojo?.let { it.typeName.toJavaPoet() },
+                `is`(USER_TYPE_NAME)
+            )
         }
     }
 
@@ -289,7 +311,10 @@
                 )
             )
             assertThat(shortcut.entities.size, `is`(1))
-            assertThat(shortcut.entities["users"]?.pojo?.typeName, `is`(USER_TYPE_NAME))
+            assertThat(
+                shortcut.entities["users"]?.pojo?.let { it.typeName.toJavaPoet() },
+                `is`(USER_TYPE_NAME)
+            )
         }
     }
 
@@ -324,8 +349,14 @@
                 )
                 assertThat(shortcut.parameters.map { it.name }, `is`(listOf("u1", "b1")))
                 assertThat(shortcut.entities.size, `is`(2))
-                assertThat(shortcut.entities["u1"]?.pojo?.typeName, `is`(USER_TYPE_NAME))
-                assertThat(shortcut.entities["b1"]?.pojo?.typeName, `is`(BOOK_TYPE_NAME))
+                assertThat(
+                    shortcut.entities["u1"]?.pojo?.let { it.typeName.toJavaPoet() },
+                    `is`(USER_TYPE_NAME)
+                )
+                assertThat(
+                    shortcut.entities["b1"]?.pojo?.let { it.typeName.toJavaPoet() },
+                    `is`(BOOK_TYPE_NAME)
+                )
             }
         }
     }
@@ -421,7 +452,10 @@
             assertThat(shortcut.entities.size, `is`(1))
             assertThat(shortcut.entities["username"]?.isPartialEntity, `is`(true))
             assertThat(shortcut.entities["username"]?.entityTypeName, `is`(USER_TYPE_NAME))
-            assertThat(shortcut.entities["username"]?.pojo?.typeName, `is`(USERNAME_TYPE_NAME))
+            assertThat(
+                shortcut.entities["username"]?.pojo?.let { it.typeName.toJavaPoet() },
+                `is`(USERNAME_TYPE_NAME)
+            )
         }
     }
 
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/processor/InsertOrUpsertShortcutMethodProcessorTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/processor/InsertOrUpsertShortcutMethodProcessorTest.kt
index 46b6fca..467a22f 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/processor/InsertOrUpsertShortcutMethodProcessorTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/processor/InsertOrUpsertShortcutMethodProcessorTest.kt
@@ -18,6 +18,7 @@
 
 import COMMON
 import androidx.room.Dao
+import androidx.room.compiler.codegen.toJavaPoet
 import androidx.room.compiler.processing.XMethodElement
 import androidx.room.compiler.processing.XType
 import androidx.room.compiler.processing.XTypeElement
@@ -34,12 +35,12 @@
 import androidx.room.solver.shortcut.result.InsertOrUpsertMethodAdapter
 import androidx.room.testing.context
 import androidx.room.vo.InsertOrUpsertShortcutMethod
-import com.squareup.javapoet.ClassName
-import com.squareup.javapoet.TypeName
-import kotlin.reflect.KClass
 import com.google.common.truth.Truth.assertThat
 import com.squareup.javapoet.ArrayTypeName
+import com.squareup.javapoet.ClassName
 import com.squareup.javapoet.ParameterizedTypeName
+import com.squareup.javapoet.TypeName
+import kotlin.reflect.KClass
 import org.junit.Test
 
 /**
@@ -137,7 +138,7 @@
             assertThat(insertionUpsertion.entities["user"]?.isPartialEntity)
                 .isEqualTo(false)
 
-            assertThat(insertionUpsertion.entities["user"]?.pojo?.typeName)
+            assertThat(insertionUpsertion.entities["user"]?.pojo?.typeName?.toJavaPoet())
                 .isEqualTo(ClassName.get("foo.bar", "User") as TypeName)
 
             assertThat(insertionUpsertion.returnType.typeName)
@@ -162,10 +163,10 @@
             assertThat(insertionUpsertion.entities.size)
                 .isEqualTo(2)
 
-            assertThat(insertionUpsertion.entities["u1"]?.pojo?.typeName)
+            assertThat(insertionUpsertion.entities["u1"]?.pojo?.typeName?.toJavaPoet())
                 .isEqualTo(USER_TYPE_NAME)
 
-            assertThat(insertionUpsertion.entities["u2"]?.pojo?.typeName)
+            assertThat(insertionUpsertion.entities["u2"]?.pojo?.typeName?.toJavaPoet())
                 .isEqualTo(USER_TYPE_NAME)
 
             assertThat(insertionUpsertion.parameters.map { it.name })
@@ -200,7 +201,7 @@
 
             assertThat(insertionUpsertion.entities.size).isEqualTo(1)
 
-            assertThat(insertionUpsertion.entities["users"]?.pojo?.typeName)
+            assertThat(insertionUpsertion.entities["users"]?.pojo?.typeName?.toJavaPoet())
                 .isEqualTo(USER_TYPE_NAME)
 
             assertThat(insertionUpsertion.returnType.typeName)
@@ -229,7 +230,7 @@
 
             assertThat(insertionUpsertion.entities.size).isEqualTo(1)
 
-            assertThat(insertionUpsertion.entities["users"]?.pojo?.typeName)
+            assertThat(insertionUpsertion.entities["users"]?.pojo?.typeName?.toJavaPoet())
                 .isEqualTo(USER_TYPE_NAME)
 
             assertThat(insertionUpsertion.returnType.typeName)
@@ -258,7 +259,7 @@
 
             assertThat(insertionUpsertion.entities.size).isEqualTo(1)
 
-            assertThat(insertionUpsertion.entities["users"]?.pojo?.typeName)
+            assertThat(insertionUpsertion.entities["users"]?.pojo?.typeName?.toJavaPoet())
                 .isEqualTo(USER_TYPE_NAME)
 
             assertThat(insertionUpsertion.returnType.typeName).isEqualTo(TypeName.VOID)
@@ -286,7 +287,7 @@
 
             assertThat(insertionUpsertion.entities.size).isEqualTo(1)
 
-            assertThat(insertionUpsertion.entities["users"]?.pojo?.typeName)
+            assertThat(insertionUpsertion.entities["users"]?.pojo?.typeName?.toJavaPoet())
                 .isEqualTo(USER_TYPE_NAME)
 
             assertThat(insertionUpsertion.returnType.typeName).isEqualTo(TypeName.VOID)
@@ -314,7 +315,7 @@
 
             assertThat(insertionUpsertion.entities.size).isEqualTo(1)
 
-            assertThat(insertionUpsertion.entities["users"]?.pojo?.typeName)
+            assertThat(insertionUpsertion.entities["users"]?.pojo?.typeName?.toJavaPoet())
                 .isEqualTo(USER_TYPE_NAME)
 
             assertThat(insertionUpsertion.returnType.typeName).isEqualTo(TypeName.VOID)
@@ -343,7 +344,7 @@
 
             assertThat(insertionUpsertion.entities.size).isEqualTo(1)
 
-            assertThat(insertionUpsertion.entities["users"]?.pojo?.typeName)
+            assertThat(insertionUpsertion.entities["users"]?.pojo?.typeName?.toJavaPoet())
                 .isEqualTo(USER_TYPE_NAME)
 
             assertThat(insertionUpsertion.returnType.typeName).isEqualTo(TypeName.VOID)
@@ -371,10 +372,10 @@
 
             assertThat(insertionUpsertion.entities.size).isEqualTo(2)
 
-            assertThat(insertionUpsertion.entities["u1"]?.pojo?.typeName)
+            assertThat(insertionUpsertion.entities["u1"]?.pojo?.typeName?.toJavaPoet())
                 .isEqualTo(USER_TYPE_NAME)
 
-            assertThat(insertionUpsertion.entities["b1"]?.pojo?.typeName)
+            assertThat(insertionUpsertion.entities["b1"]?.pojo?.typeName?.toJavaPoet())
                 .isEqualTo(BOOK_TYPE_NAME)
         }
     }
@@ -593,7 +594,7 @@
             assertThat(insertionUpsertion.entities["username"]?.entityTypeName)
                 .isEqualTo(USER_TYPE_NAME)
 
-            assertThat(insertionUpsertion.entities["username"]?.pojo?.typeName)
+            assertThat(insertionUpsertion.entities["username"]?.pojo?.typeName?.toJavaPoet())
                 .isEqualTo(USERNAME_TYPE_NAME)
         }
     }
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/processor/PojoProcessorTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/processor/PojoProcessorTest.kt
index 1df902e..f408fbe 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/processor/PojoProcessorTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/processor/PojoProcessorTest.kt
@@ -18,6 +18,7 @@
 
 import COMMON
 import androidx.room.Embedded
+import androidx.room.compiler.codegen.toJavaPoet
 import androidx.room.compiler.processing.XFieldElement
 import androidx.room.compiler.processing.util.Source
 import androidx.room.compiler.processing.util.XTestInvocation
@@ -44,8 +45,8 @@
 import com.squareup.javapoet.ClassName
 import com.squareup.javapoet.TypeName
 import java.io.File
-import org.hamcrest.CoreMatchers.`is`
 import org.hamcrest.CoreMatchers.instanceOf
+import org.hamcrest.CoreMatchers.`is`
 import org.hamcrest.CoreMatchers.not
 import org.hamcrest.CoreMatchers.notNullValue
 import org.hamcrest.CoreMatchers.nullValue
@@ -212,7 +213,7 @@
             assertThat(parent.prefix, `is`(""))
             assertThat(parent.field.name, `is`("myPoint"))
             assertThat(
-                parent.pojo.typeName,
+                parent.pojo.typeName.toJavaPoet(),
                 `is`(ClassName.get("foo.bar.MyPojo", "Point") as TypeName)
             )
         }
@@ -315,7 +316,7 @@
             )
             val pointField = pojo.embeddedFields.first { it.field.name == "genericField" }
             assertThat(
-                pointField.pojo.typeName,
+                pointField.pojo.typeName.toJavaPoet(),
                 `is`(ClassName.get("foo.bar", "Point") as TypeName)
             )
         }
@@ -686,7 +687,7 @@
             assertThat(pojo.relations.size, `is`(1))
             val rel = pojo.relations.first()
             assertThat(rel.projection, `is`(listOf("uid")))
-            assertThat(rel.entity.typeName, `is`(COMMON.USER_TYPE_NAME as TypeName))
+            assertThat(rel.entity.typeName.toJavaPoet(), `is`(COMMON.USER_TYPE_NAME as TypeName))
         }
     }
 
@@ -704,7 +705,7 @@
             assertThat(pojo.relations.size, `is`(1))
             val rel = pojo.relations.first()
             assertThat(rel.projection, `is`(listOf("name")))
-            assertThat(rel.entity.typeName, `is`(COMMON.USER_TYPE_NAME as TypeName))
+            assertThat(rel.entity.typeName.toJavaPoet(), `is`(COMMON.USER_TYPE_NAME as TypeName))
         }
     }
 
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/processor/TableEntityProcessorTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/processor/TableEntityProcessorTest.kt
index 235d729..42c6c57 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/processor/TableEntityProcessorTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/processor/TableEntityProcessorTest.kt
@@ -17,6 +17,7 @@
 package androidx.room.processor
 
 import COMMON
+import androidx.room.compiler.codegen.toJavaPoet
 import androidx.room.compiler.processing.util.Source
 import androidx.room.compiler.processing.util.compileFiles
 import androidx.room.compiler.processing.util.runProcessorTest
@@ -34,8 +35,8 @@
 import com.google.common.truth.Truth.assertThat
 import com.squareup.javapoet.ClassName
 import com.squareup.javapoet.TypeName
-import org.hamcrest.CoreMatchers.`is`
 import org.hamcrest.CoreMatchers.hasItems
+import org.hamcrest.CoreMatchers.`is`
 import org.hamcrest.MatcherAssert.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -2600,7 +2601,7 @@
             )
             val parsed = parser.process()
             val field = parsed.primaryKey.fields.first()
-            assertThat(field.typeName).isEqualTo(TypeName.LONG)
+            assertThat(field.typeName.toJavaPoet()).isEqualTo(TypeName.LONG)
         }
     }
 }
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/solver/BasicColumnTypeAdaptersTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/solver/BasicColumnTypeAdaptersTest.kt
index 939a336..4b649bf 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/solver/BasicColumnTypeAdaptersTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/solver/BasicColumnTypeAdaptersTest.kt
@@ -57,7 +57,7 @@
                 arrayOf(
                     TypeName.BYTE,
                     "st.bindLong(6, inp);",
-                    "out = (byte) crs.getShort(9);"
+                    "out = (byte) (crs.getShort(9));"
                 ),
                 arrayOf(
                     TypeName.SHORT,
@@ -72,7 +72,7 @@
                 arrayOf(
                     TypeName.CHAR,
                     "st.bindLong(6, inp);",
-                    "out = (char) crs.getInt(9);"
+                    "out = (char) (crs.getInt(9));"
                 ),
                 arrayOf(
                     TypeName.FLOAT,
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/writer/KotlinCodeGenTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/writer/KotlinCodeGenTest.kt
new file mode 100644
index 0000000..0e920dd
--- /dev/null
+++ b/room/room-compiler/src/test/kotlin/androidx/room/writer/KotlinCodeGenTest.kt
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.writer
+
+import androidx.room.DatabaseProcessingStep
+import androidx.room.compiler.processing.util.Source
+import androidx.room.compiler.processing.util.XTestInvocation
+import androidx.room.compiler.processing.util.runKspTest
+import androidx.room.processor.Context
+import loadTestSource
+import org.junit.Test
+
+// Dany's Kotlin codegen test playground (and tests too)
+class KotlinCodeGenTest {
+
+    val databaseSrc = Source.kotlin(
+        "MyDatabase.kt",
+        """
+        import androidx.room.*
+
+        @Database(entities = [MyEntity::class], version = 1, exportSchema = false)
+        abstract class MyDatabase : RoomDatabase() {
+          abstract fun getDao(): MyDao
+        }
+        """.trimIndent()
+    )
+
+    @Test
+    fun pojoRowAdapter_primitives() {
+        val testName = object {}.javaClass.enclosingMethod!!.name
+        val src = Source.kotlin(
+            "MyDao.kt",
+            """
+            import androidx.room.*
+
+            @Dao
+            interface MyDao {
+              @Query("SELECT * FROM MyEntity")
+              fun getEntity(): MyEntity
+            }
+
+            @Entity
+            data class MyEntity(
+                @PrimaryKey
+                val int: Int,
+                val short: Short,
+                val byte: Byte,
+                val long: Long,
+                val char: Char,
+                val float: Float,
+                val double: Double,
+            )
+            """.trimIndent()
+        )
+        runTest(
+            sources = listOf(src, databaseSrc),
+            expectedFilePath = getTestGoldenPath(testName)
+        )
+    }
+
+    private fun getTestGoldenPath(testName: String): String {
+        return "kotlinCodeGen/$testName.kt"
+    }
+
+    private fun runTest(
+        sources: List<Source>,
+        expectedFilePath: String,
+        handler: (XTestInvocation) -> Unit = { }
+    ) {
+        runKspTest(
+            sources = sources,
+            options = mapOf(Context.BooleanProcessorOptions.GENERATE_KOTLIN.argName to "true"),
+        ) {
+            val databaseFqn = "androidx.room.Database"
+            DatabaseProcessingStep().process(
+                it.processingEnv,
+                mapOf(databaseFqn to it.roundEnv.getElementsAnnotatedWith(databaseFqn)),
+                it.roundEnv.isProcessingOver
+            )
+            it.assertCompilationResult {
+                this.generatedSource(
+                    loadTestSource(
+                        expectedFilePath,
+                        "androidx.room.temp.PojoRowAdapter_1427165205"
+                    )
+                )
+                this.hasNoWarnings()
+            }
+            handler.invoke(it)
+        }
+    }
+}
\ No newline at end of file
diff --git a/room/room-compiler/src/test/test-data/daoWriter/output/ComplexDao.java b/room/room-compiler/src/test/test-data/daoWriter/output/ComplexDao.java
index 4436ebf..c9f0981c7 100644
--- a/room/room-compiler/src/test/test-data/daoWriter/output/ComplexDao.java
+++ b/room/room-compiler/src/test/test-data/daoWriter/output/ComplexDao.java
@@ -493,7 +493,7 @@
                     _tmpName = _cursor.getString(_cursorIndexOfName);
                 }
                 final Info _tmpInfo;
-                if (! (_cursor.isNull(_cursorIndexOfSerial) && _cursor.isNull(_cursorIndexOfCode))) {
+                if (!(_cursor.isNull(_cursorIndexOfSerial) && _cursor.isNull(_cursorIndexOfCode))) {
                     _tmpInfo = new Info();
                     _tmpInfo.serial = _cursor.getInt(_cursorIndexOfSerial);
                     if (_cursor.isNull(_cursorIndexOfCode)) {
@@ -501,7 +501,7 @@
                     } else {
                         _tmpInfo.code = _cursor.getString(_cursorIndexOfCode);
                     }
-                }  else  {
+                } else {
                     _tmpInfo = null;
                 }
                 _item = new Child1(_tmpId,_tmpName,_tmpInfo);
@@ -537,7 +537,7 @@
                     _tmpName = _cursor.getString(_cursorIndexOfName);
                 }
                 final Info _tmpInfo;
-                if (! (_cursor.isNull(_cursorIndexOfSerial) && _cursor.isNull(_cursorIndexOfCode))) {
+                if (!(_cursor.isNull(_cursorIndexOfSerial) && _cursor.isNull(_cursorIndexOfCode))) {
                     _tmpInfo = new Info();
                     _tmpInfo.serial = _cursor.getInt(_cursorIndexOfSerial);
                     if (_cursor.isNull(_cursorIndexOfCode)) {
@@ -545,7 +545,7 @@
                     } else {
                         _tmpInfo.code = _cursor.getString(_cursorIndexOfCode);
                     }
-                }  else  {
+                } else {
                     _tmpInfo = null;
                 }
                 _item = new Child2(_tmpId,_tmpName,_tmpInfo);
@@ -584,7 +584,7 @@
                             _tmpName = _cursor.getString(_cursorIndexOfName);
                         }
                         final Info _tmpInfo;
-                        if (! (_cursor.isNull(_cursorIndexOfSerial) && _cursor.isNull(_cursorIndexOfCode))) {
+                        if (!(_cursor.isNull(_cursorIndexOfSerial) && _cursor.isNull(_cursorIndexOfCode))) {
                             _tmpInfo = new Info();
                             _tmpInfo.serial = _cursor.getInt(_cursorIndexOfSerial);
                             if (_cursor.isNull(_cursorIndexOfCode)) {
@@ -592,7 +592,7 @@
                             } else {
                                 _tmpInfo.code = _cursor.getString(_cursorIndexOfCode);
                             }
-                        }  else  {
+                        } else {
                             _tmpInfo = null;
                         }
                         _item = new Child1(_tmpId,_tmpName,_tmpInfo);
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_primitives.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_primitives.kt
new file mode 100644
index 0000000..367cf89
--- /dev/null
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_primitives.kt
@@ -0,0 +1,46 @@
+package androidx.room.temp
+
+import MyEntity
+import android.database.Cursor
+import javax.`annotation`.processing.Generated
+import kotlin.Byte
+import kotlin.Char
+import kotlin.Double
+import kotlin.Float
+import kotlin.Int
+import kotlin.Long
+import kotlin.Short
+import kotlin.Suppress
+
+@Generated(value = ["androidx.room.RoomProcessor"])
+@Suppress(names = ["unchecked", "deprecation"])
+public class PojoRowAdapter_1427165205 {
+    public fun readFromCursor(
+        _cursor: Cursor,
+        _cursorIndexOfInt: Int,
+        _cursorIndexOfShort: Int,
+        _cursorIndexOfByte: Int,
+        _cursorIndexOfLong: Int,
+        _cursorIndexOfChar: Int,
+        _cursorIndexOfFloat: Int,
+        _cursorIndexOfDouble: Int,
+    ): MyEntity {
+        val _result: MyEntity
+        val _tmpInt: Int
+        _tmpInt = _cursor.getInt(_cursorIndexOfInt)
+        val _tmpShort: Short
+        _tmpShort = _cursor.getShort(_cursorIndexOfShort)
+        val _tmpByte: Byte
+        _tmpByte = _cursor.getShort(_cursorIndexOfByte).toByte()
+        val _tmpLong: Long
+        _tmpLong = _cursor.getLong(_cursorIndexOfLong)
+        val _tmpChar: Char
+        _tmpChar = _cursor.getInt(_cursorIndexOfChar).toChar()
+        val _tmpFloat: Float
+        _tmpFloat = _cursor.getFloat(_cursorIndexOfFloat)
+        val _tmpDouble: Double
+        _tmpDouble = _cursor.getDouble(_cursorIndexOfDouble)
+        _result = MyEntity(_tmpInt,_tmpShort,_tmpByte,_tmpLong,_tmpChar,_tmpFloat,_tmpDouble)
+        return _result
+    }
+}
\ No newline at end of file