Migrate Kotlin delegating method binders to XPoet
When generating Kotlin there is no need to override and implement bridge functions for DefaultImpls and type args of boxed primitives. These two set of binders are only needed when generating Java and are entirely skipped in Kotlin. However, this change does migrate the binders to XPoet for migration completeness as we want to minimize JavaPoet usages in room-compiler.
Additionally add com.google.testparameterinjector:test-parameter-injector to the repo, useful for parametrized test methods (as opposed to the entire class).
Bug: 127483380
Test: KotlinCodeGenTest + DaoPrimitiveTest.kt + existing.
Change-Id: I85f675fb72b9956bc49f7153f234ff6a49efedd7
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 9eb5e55..c840cad 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -227,6 +227,7 @@
testExtJunitKtx = { module = "androidx.test.ext:junit-ktx", version.ref = "androidxTestExtJunit" }
testExtTruth = { module = "androidx.test.ext:truth", version.ref = "androidxTestExtTruth" }
testMonitor = { module = "androidx.test:monitor", version.ref = "androidxTestMonitor" }
+testParameterInjector = { module = "com.google.testparameterinjector:test-parameter-injector", version = "1.9" }
testRules = { module = "androidx.test:rules", version.ref = "androidxTestRules" }
testRunner = { module = "androidx.test:runner", version.ref = "androidxTestRunner" }
testUiautomator = { module = "androidx.test.uiautomator:uiautomator", version = "2.2.0" }
diff --git a/gradle/verification-keyring.keys b/gradle/verification-keyring.keys
index 787b19f..175670e 100644
--- a/gradle/verification-keyring.keys
+++ b/gradle/verification-keyring.keys
@@ -2613,6 +2613,54 @@
-----END PGP PUBLIC KEY BLOCK-----
+pub 358A4ABAE72947C2
+uid Jens Nyman <[email protected]>
+
+sub B8EB751F2C19011D
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: BCPG v1.68
+
+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
+FFbW42hpI8TDRfB4oHWyTsM6zYXNXw==
+=zYIg
+-----END PGP PUBLIC KEY BLOCK-----
+
+
pub 36D4E9618F3ADAB5
sub C4935FA8AC763C70
-----BEGIN PGP PUBLIC KEY BLOCK-----
@@ -6983,6 +7031,54 @@
-----END PGP PUBLIC KEY BLOCK-----
+pub 9987B20C8F6A3064
+uid mkruskal <[email protected]>
+
+sub 80CFA7C482552DC3
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: BCPG v1.68
+
+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
+vyMjyB4YL48bYbjwFEar/jLCQJj/Dg==
+=Mtq5
+-----END PGP PUBLIC KEY BLOCK-----
+
+
pub 99CE9D9F22DC5C99
uid Benjamin Gehrels (used for *@gehrels.info) <[email protected]>
uid Benjamin Gehrels <[email protected]>
diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml
index c11dc85e..9e3a053 100644
--- a/gradle/verification-metadata.xml
+++ b/gradle/verification-metadata.xml
@@ -65,7 +65,10 @@
<trusted-key id="10ae8966a146e8be" group="com.google.crypto.tink"/>
<trusted-key id="1188b69f6d6259ca" group="com.google.accompanist"/>
<trusted-key id="11b581967f079a30a3e93140d57506cd188fd842" group="com.google.api.grpc"/>
- <trusted-key id="120d6f34e627ed3a772ebbfe55c7e5e701832382" group="org.snakeyaml"/>
+ <trusted-key id="120d6f34e627ed3a772ebbfe55c7e5e701832382">
+ <trusting group="org.snakeyaml"/>
+ <trusting group="org.yaml" name="snakeyaml"/>
+ </trusted-key>
<trusted-key id="12d16069219c90212a974d119ae296fd02e9f65b" group="org.apache.commons" name="commons-math3"/>
<trusted-key id="13ac2213964abe1d1c147c0e1939a2520bab1d90" group="org.freemarker" name="freemarker"/>
<trusted-key id="147b691a19097624902f4ea9689cbe64f4bc997f" group="^org[.]mockito($|([.].*))" regex="true"/>
@@ -73,6 +76,7 @@
<trusted-key id="1597ab231b7add7e14b1d9c43f00db67ae236e2e" group="org.conscrypt"/>
<trusted-key id="160a7a9cf46221a56b06ad64461a804f2609fd89" group="com.github.shyiko.klob" name="klob"/>
<trusted-key id="1861c322c56014b2" group="commons-lang"/>
+ <trusted-key id="187366a3ffe6bf8f94b9136a9987b20c8f6a3064" group="com.google.protobuf"/>
<trusted-key id="190d5a957ff22273e601f7a7c92c5fec70161c62" group="org.codehaus.mojo"/>
<trusted-key id="19beab2d799c020f17c69126b16698a4adf4d638" group="org.checkerframework"/>
<trusted-key id="1b2718089ce964b8" group="com.thoughtworks.qdox"/>
@@ -339,7 +343,7 @@
<trusted-key id="a33a0b49a4c1ab590b0a4ddc1364c5e2df3e99c5" group="org.reactivestreams"/>
<trusted-key id="a40e24b5b408dbd5" group="org.robolectric"/>
<trusted-key id="a413f67d71beec23add0ce0acb43338e060cf9fa">
- <trusting group="com.google.code.findbugs" name="jsr305" version="2.0.2"/>
+ <trusting group="com.google.code.findbugs" name="jsr305"/>
<trusting group="org.jacoco"/>
</trusted-key>
<trusted-key id="a4fd709cc4b0515f2e6af04e218fa0f6a941a037" group="com.github.kevinstern" name="software-and-algorithms"/>
@@ -452,6 +456,7 @@
</trusted-key>
<trusted-key id="e7dc75fc24fb3c8dfe8086ad3d5839a2262cbbfb" group="org.jetbrains.kotlinx"/>
<trusted-key id="e85aed155021af8a6c6b7a4a7c7d8456294423ba" group="org.objenesis"/>
+ <trusted-key id="e8bf633b386b7ddcf1e1a9b3358a4abae72947c2" group="com.google.testparameterinjector"/>
<trusted-key id="ea0b70b5050192c98cfa7e4f3f36885c24df4b75" group="org.mozilla" name="rhino"/>
<trusted-key id="ea23db1360d9029481e7f2efecdfea3cb4493b94" group="jline" name="jline"/>
<trusted-key id="eaa526b91dd83ba3e1b9636fa730529ca355a63e" group="org.ccil.cowan.tagsoup" name="tagsoup"/>
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/DaoPrimitiveTest.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/DaoBoxedPrimitiveDelegateTest.kt
similarity index 84%
rename from room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/DaoPrimitiveTest.kt
rename to room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/DaoBoxedPrimitiveDelegateTest.kt
index 80986a4..31b855e 100644
--- a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/DaoPrimitiveTest.kt
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/DaoBoxedPrimitiveDelegateTest.kt
@@ -33,7 +33,7 @@
@RunWith(AndroidJUnit4::class)
@SmallTest
-class DaoPrimitiveTest {
+class DaoBoxedPrimitiveDelegateTest {
@Entity(tableName = "longFoo")
data class LongFoo(@PrimaryKey val id: Long, val description: String)
@@ -49,36 +49,15 @@
fun getItem(id: Key): Value?
+ fun getItemWithDescription(id: Key, desc: String): Value?
+
fun delete(id: Key)
fun getFirstItemId(): Key
}
- /* Interface with non-generics and generics */
- interface LongBaseDao<Long, Value> {
-
- fun getItem(id: Long): Value?
-
- fun delete(id: Long)
-
- @Query("select id from longFoo limit 1")
- fun getFirstItemId(): Long
- }
-
- /* Interface with non-generics and generics */
- interface StringBaseDao<String, Value> {
-
- fun getItem(id: String): Value?
-
- fun delete(id: String)
-
- @Query("select id from stringFoo limit 1")
- fun getFirstItemId(): String
- }
-
- interface ByteArrayBaseDao<ByteArray, Value> {
- @Query("select id from byteArrayFoo limit 1")
- fun getByteArray(): Array<ByteArray>
+ interface ByteArrayBaseDao<T> {
+ fun getByteArray(): T
}
// Foo interfaces that are using the base
@@ -89,6 +68,9 @@
@Query("select * from longFoo where id=:id")
override fun getItem(id: Long): LongFoo?
+ @Query("select * from longFoo where id=:id AND description=:desc")
+ override fun getItemWithDescription(id: Long, desc: String): LongFoo?
+
@Query("delete from longFoo where id=:id")
override fun delete(id: Long)
@@ -105,6 +87,9 @@
@Query("select * from stringFoo where id=:id")
override fun getItem(id: String): StringFoo?
+ @Query("select * from stringFoo where id=:id AND description=:desc")
+ override fun getItemWithDescription(id: String, desc: String): StringFoo?
+
@Query("delete from stringFoo where id=:id")
override fun delete(id: String)
@@ -116,13 +101,13 @@
}
@Dao
- interface ByteArrayFooDao : ByteArrayBaseDao<ByteArray, String> {
+ interface ByteArrayFooDao : ByteArrayBaseDao<ByteArray> {
@Insert
fun insert(item: ByteArrayFoo)
@Query("select id from byteArrayFoo limit 1")
- override fun getByteArray(): Array<ByteArray>
+ override fun getByteArray(): ByteArray
}
@Database(
@@ -181,6 +166,6 @@
).build()
val foo = ByteArrayFoo(ByteArray(16), "Elif")
db.byteArrayFooDao().insert(foo)
- assertThat(db.byteArrayFooDao().getByteArray()).isEqualTo(arrayOf(ByteArray(16)))
+ assertThat(db.byteArrayFooDao().getByteArray()).isEqualTo(ByteArray(16))
}
}
\ 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 9df2a7c..18048d5 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
@@ -278,5 +278,8 @@
else -> error("Can't get JTypeName from java.lang.Class: $klass")
}
+fun XTypeName.box() = XTypeName(java.box(), kotlin)
+fun XTypeName.unbox() = XTypeName(java.unbox(), kotlin.copy(nullable = false), XNullability.NONNULL)
+
fun XTypeName.toJavaPoet(): JTypeName = this.java
fun XClassName.toJavaPoet(): JClassName = this.java
\ No newline at end of file
diff --git a/room/room-compiler/build.gradle b/room/room-compiler/build.gradle
index 15f305c..571dda1 100644
--- a/room/room-compiler/build.gradle
+++ b/room/room-compiler/build.gradle
@@ -14,13 +14,15 @@
* limitations under the License.
*/
+
import androidx.build.BuildOnServerKt
import androidx.build.LibraryType
-import androidx.build.SupportConfig
import androidx.build.SdkHelperKt
+import androidx.build.SupportConfig
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+
import java.util.zip.ZipEntry
import java.util.zip.ZipFile
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
id("AndroidXPlugin")
@@ -103,6 +105,7 @@
implementation(libs.apacheCommonsCodec)
implementation(libs.intellijAnnotations)
testImplementation(libs.truth)
+ testImplementation(libs.testParameterInjector)
testImplementation(libs.autoValue) // to access the processor in tests
testImplementation(libs.autoServiceAnnotations)
testImplementation(libs.autoService) // to access the processor in tests
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/processor/DaoProcessor.kt b/room/room-compiler/src/main/kotlin/androidx/room/processor/DaoProcessor.kt
index 898a915..a0f36c1 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/processor/DaoProcessor.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/processor/DaoProcessor.kt
@@ -62,7 +62,7 @@
deletionMethods = emptyList(),
updateMethods = emptyList(),
transactionMethods = emptyList(),
- delegatingMethods = emptyList(),
+ kotlinBoxedPrimitiveMethodDelegates = emptyList(),
kotlinDefaultMethodDelegates = emptyList(),
constructorParamType = null
)
@@ -179,11 +179,11 @@
).process()
}
- // Only try to find kotlin boxed delegating methods when the dao extends a class or
- // implements an interface since otherwise there are no duplicated method generated by
+ // Only try to find Kotlin boxed bridge methods when the dao extends a class or
+ // implements an interface since otherwise there are no bridge method generated by
// Kotlin.
- val unannotatedMethods = methods[Any::class] ?: emptyList<XMethodElement>()
- val delegatingMethods =
+ val unannotatedMethods = methods[Any::class] ?: emptyList()
+ val kotlinBoxedPrimitiveBridgeMethods =
if (element.superClass != null || element.getSuperInterfaceElements().isNotEmpty()) {
matchKotlinBoxedPrimitiveMethods(
unannotatedMethods,
@@ -229,8 +229,10 @@
ProcessorErrors.CANNOT_USE_UNBOUND_GENERICS_IN_DAO_CLASSES
)
- (unannotatedMethods - delegatingMethods.map { it.element }).forEach { method ->
- context.logger.e(method, ProcessorErrors.INVALID_ANNOTATION_COUNT_IN_DAO_METHOD)
+ val invalidAnnotatedMethods =
+ unannotatedMethods - kotlinBoxedPrimitiveBridgeMethods.map { it.element }
+ invalidAnnotatedMethods.forEach {
+ context.logger.e(it, ProcessorErrors.INVALID_ANNOTATION_COUNT_IN_DAO_METHOD)
}
return Dao(
@@ -243,7 +245,7 @@
updateMethods = updateMethods,
upsertionMethods = upsertionMethods,
transactionMethods = transactionMethods.toList(),
- delegatingMethods = delegatingMethods,
+ kotlinBoxedPrimitiveMethodDelegates = kotlinBoxedPrimitiveBridgeMethods,
kotlinDefaultMethodDelegates = kotlinDefaultMethodDelegates.toList(),
constructorParamType = constructorParamType
)
@@ -260,6 +262,12 @@
}
}
+ /**
+ * Find Kotlin bridge methods generated for overrides of primitives, see KT-46650.
+ * When generating the Java implementation of the DAO, Room needs to also override the bridge
+ * method generated by Kotlin for the boxed version, it will contain the same name, return type
+ * and parameter, but the generic primitive params will be boxed.
+ */
private fun matchKotlinBoxedPrimitiveMethods(
unannotatedMethods: List<XMethodElement>,
annotatedMethods: List<XMethodElement>
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/KotlinBoxedPrimitiveMethodDelegateBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/KotlinBoxedPrimitiveMethodDelegateBinder.kt
new file mode 100644
index 0000000..3318d56
--- /dev/null
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/KotlinBoxedPrimitiveMethodDelegateBinder.kt
@@ -0,0 +1,68 @@
+/*
+ * 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.solver
+
+import androidx.room.compiler.codegen.CodeLanguage
+import androidx.room.compiler.codegen.XTypeName
+import androidx.room.compiler.codegen.unbox
+import androidx.room.compiler.processing.XType
+import androidx.room.compiler.processing.isVoid
+import androidx.room.vo.KotlinBoxedPrimitiveMethodDelegate
+
+/**
+ * Method binder that delegates to a sibling DAO function in a Kotlin interface or abstract class
+ * and specifically to a sibling function with unboxed primitive parameters.
+ *
+ * @see [KotlinBoxedPrimitiveMethodDelegate]
+ */
+object KotlinBoxedPrimitiveMethodDelegateBinder {
+
+ fun execute(
+ methodName: String,
+ returnType: XType,
+ parameters: List<Pair<XTypeName, String>>,
+ scope: CodeGenScope
+ ) {
+ check(scope.language == CodeLanguage.JAVA)
+ scope.builder.apply {
+ val params = mutableListOf<Any>()
+ val format = buildString {
+ if (!returnType.isVoid()) {
+ append("return ")
+ }
+ append("%L(")
+ params.add(methodName)
+ parameters.forEachIndexed { i, (typeName, name) ->
+ if (typeName.isPrimitive) {
+ append("(%T) %L")
+ params.add(typeName.unbox())
+ params.add(name)
+ } else {
+ append("%L")
+ params.add(name)
+ }
+ if (i < parameters.size - 1) {
+ append(", ")
+ }
+ }
+ append(")")
+ emptyList<String>().joinToString()
+ }
+ addStatement(format, *params.toTypedArray())
+ }
+ }
+}
\ No newline at end of file
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/KotlinDefaultMethodDelegateBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/KotlinDefaultMethodDelegateBinder.kt
index 5c36524..f4787ca 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/KotlinDefaultMethodDelegateBinder.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/KotlinDefaultMethodDelegateBinder.kt
@@ -16,40 +16,42 @@
package androidx.room.solver
-import androidx.room.ext.DEFAULT_IMPLS_CLASS_NAME
-import androidx.room.ext.L
-import androidx.room.ext.N
-import androidx.room.ext.T
+import androidx.room.compiler.codegen.CodeLanguage
+import androidx.room.compiler.codegen.XClassName
import androidx.room.compiler.processing.XType
import androidx.room.compiler.processing.isVoid
-import com.squareup.javapoet.ClassName
+import androidx.room.ext.DEFAULT_IMPLS_CLASS_NAME
+import androidx.room.vo.KotlinDefaultMethodDelegate
/**
- * Method binder that delegates to concrete DAO function in a Kotlin interface.
+ * Method binder that delegates to concrete DAO function in a Kotlin interface, specifically to
+ * a function where the implementation is in the DefaultImpl Kotlin generated class.
+ *
+ * @see [KotlinDefaultMethodDelegate]
*/
object KotlinDefaultMethodDelegateBinder {
fun executeAndReturn(
- daoName: ClassName,
- daoImplName: ClassName,
+ daoName: XClassName,
+ daoImplName: XClassName,
methodName: String,
returnType: XType,
parameterNames: List<String>,
scope: CodeGenScope
) {
- scope.builder().apply {
- val params: MutableList<Any> = mutableListOf()
+ check(scope.language == CodeLanguage.JAVA)
+ scope.builder.apply {
+ val params = mutableListOf<Any>()
val format = buildString {
if (!returnType.isVoid()) {
append("return ")
}
- append("$T.$N.$N($T.this")
+ append("%T.%L.%L(%T.this")
params.add(daoName)
params.add(DEFAULT_IMPLS_CLASS_NAME)
params.add(methodName)
params.add(daoImplName)
parameterNames.forEach {
- append(", ")
- append(L)
+ append(", %L")
params.add(it)
}
append(")")
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/vo/Dao.kt b/room/room-compiler/src/main/kotlin/androidx/room/vo/Dao.kt
index eebca3b..d35a3a1 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/vo/Dao.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/vo/Dao.kt
@@ -31,7 +31,7 @@
val updateMethods: List<UpdateMethod>,
val upsertionMethods: List<UpsertionMethod>,
val transactionMethods: List<TransactionMethod>,
- val delegatingMethods: List<KotlinBoxedPrimitiveMethodDelegate>,
+ val kotlinBoxedPrimitiveMethodDelegates: List<KotlinBoxedPrimitiveMethodDelegate>,
val kotlinDefaultMethodDelegates: List<KotlinDefaultMethodDelegate>,
val constructorParamType: XTypeName?
) {
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/vo/KotlinBoxedPrimitiveMethodDelegate.kt b/room/room-compiler/src/main/kotlin/androidx/room/vo/KotlinBoxedPrimitiveMethodDelegate.kt
index 4d70e91..46ba1a3 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/vo/KotlinBoxedPrimitiveMethodDelegate.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/vo/KotlinBoxedPrimitiveMethodDelegate.kt
@@ -19,8 +19,9 @@
import androidx.room.compiler.processing.XMethodElement
/**
- * A class that holds information about a boxed Method and the corresponding annotated method to
- * be delegated to.
+ * Represents a DAO bridge method that to supported the boxed version of a generic abstract class or
+ * interface in Kotlin. When generating the Java implementation of the DAO, Room needs to also
+ * override the bridge method generated by Kotlin for the boxed version support, see KT-46650.
*/
data class KotlinBoxedPrimitiveMethodDelegate(
val element: XMethodElement,
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/vo/KotlinDefaultMethodDelegate.kt b/room/room-compiler/src/main/kotlin/androidx/room/vo/KotlinDefaultMethodDelegate.kt
index 4090859..7432c9e 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/vo/KotlinDefaultMethodDelegate.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/vo/KotlinDefaultMethodDelegate.kt
@@ -19,8 +19,8 @@
import androidx.room.compiler.processing.XMethodElement
/**
- * Represents a DAO method that delegates to a concrete implementation, such as a concrete function
- * in a Kotlin interface.
+ * Represents a DAO method that delegates to a concrete implementation, specifically to override
+ * a Kotlin interface whose implementation is in the DefaultImpl generated class.
*/
data class KotlinDefaultMethodDelegate(
// the original element, not the stub that is generated for DefaultImpls
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 fd6511f..a7ff843 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
@@ -21,7 +21,6 @@
import androidx.room.compiler.codegen.XClassName
import androidx.room.compiler.codegen.XCodeBlock
import androidx.room.compiler.codegen.XCodeBlock.Builder.Companion.addLocalVal
-import androidx.room.compiler.codegen.XCodeBlock.Builder.Companion.apply
import androidx.room.compiler.codegen.XFunSpec
import androidx.room.compiler.codegen.XFunSpec.Builder.Companion.apply
import androidx.room.compiler.codegen.XPropertySpec
@@ -33,8 +32,6 @@
import androidx.room.compiler.processing.XElement
import androidx.room.compiler.processing.XMethodElement
import androidx.room.compiler.processing.XType
-import androidx.room.compiler.processing.isVoid
-import androidx.room.ext.L
import androidx.room.ext.RoomMemberNames
import androidx.room.ext.RoomTypeNames
import androidx.room.ext.RoomTypeNames.DELETE_OR_UPDATE_ADAPTER
@@ -43,11 +40,10 @@
import androidx.room.ext.RoomTypeNames.SHARED_SQLITE_STMT
import androidx.room.ext.RoomTypeNames.UPSERTION_ADAPTER
import androidx.room.ext.SupportDbTypeNames
-import androidx.room.ext.T
-import androidx.room.ext.W
import androidx.room.ext.capitalize
import androidx.room.processor.OnConflictProcessor
import androidx.room.solver.CodeGenScope
+import androidx.room.solver.KotlinBoxedPrimitiveMethodDelegateBinder
import androidx.room.solver.KotlinDefaultMethodDelegateBinder
import androidx.room.solver.types.getRequiredTypeConverters
import androidx.room.vo.Dao
@@ -162,12 +158,13 @@
dao.rawQueryMethods.forEach {
addFunction(createRawQueryMethod(it))
}
- dao.kotlinDefaultMethodDelegates.forEach {
- addFunction(createDefaultMethodDelegate(it))
- }
-
- dao.delegatingMethods.forEach {
- addFunction(createDelegatingMethod(it))
+ if (codeLanguage == CodeLanguage.JAVA) {
+ dao.kotlinDefaultMethodDelegates.forEach {
+ addFunction(createDefaultImplMethodDelegate(it))
+ }
+ dao.kotlinBoxedPrimitiveMethodDelegates.forEach {
+ addFunction(createBoxedPrimitiveBridgeMethodDelegate(it))
+ }
}
// Keep this the last one to be generated because used custom converters will
// register fields with a payload which we collect in dao to report used
@@ -606,13 +603,12 @@
}
// TODO(b/251459654): Handle @JvmOverloads in delegating functions with Kotlin codegen.
- private fun createDefaultMethodDelegate(method: KotlinDefaultMethodDelegate): XFunSpec {
+ private fun createDefaultImplMethodDelegate(method: KotlinDefaultMethodDelegate): XFunSpec {
val scope = CodeGenScope(this)
return overrideWithoutAnnotations(method.element, declaredDao).apply {
- // TODO(danysantiago): Revisit this in Kotlin codegen
KotlinDefaultMethodDelegateBinder.executeAndReturn(
- daoName = dao.typeName.toJavaPoet(),
- daoImplName = dao.implTypeName.toJavaPoet(),
+ daoName = dao.typeName,
+ daoImplName = dao.implTypeName,
methodName = method.element.jvmName,
returnType = method.element.returnType,
parameterNames = method.element.parameters.map { it.name },
@@ -622,37 +618,21 @@
}.build()
}
- // TODO(b/127483380): Reconsider the need of delegating method in KotlinPoet.
- private fun createDelegatingMethod(method: KotlinBoxedPrimitiveMethodDelegate): XFunSpec {
- val body = XCodeBlock.builder(codeLanguage).apply(
- javaCodeBuilder = {
- val args = method.concreteMethod.parameters.map {
- val paramTypename = it.type.typeName
- if (paramTypename.isBoxedPrimitive()) {
- CodeBlock.of("$L", paramTypename, it.name.toString())
- } else {
- CodeBlock.of("($T) $L", paramTypename.unbox(), it.name.toString())
- }
- }
- if (method.element.returnType.isVoid()) {
- addStatement(
- "$L($L)",
- method.element.jvmName,
- CodeBlock.join(args, ",$W")
- )
- } else {
- addStatement(
- "return $L($L)",
- method.element.jvmName,
- CodeBlock.join(args, ",$W")
- )
- }
- },
- kotlinCodeBuilder = { TODO("Kotlin codegen not yet implemented!") }
- ).build()
- return overrideWithoutAnnotations(method.element, declaredDao)
- .addCode(body)
- .build()
+ private fun createBoxedPrimitiveBridgeMethodDelegate(
+ method: KotlinBoxedPrimitiveMethodDelegate
+ ): XFunSpec {
+ val scope = CodeGenScope(this)
+ return overrideWithoutAnnotations(method.element, declaredDao).apply {
+ KotlinBoxedPrimitiveMethodDelegateBinder.execute(
+ methodName = method.element.jvmName,
+ returnType = method.element.returnType,
+ parameters = method.concreteMethod.parameters.map {
+ it.type.asTypeName() to it.name
+ },
+ scope = scope
+ )
+ addCode(scope.generate())
+ }.build()
}
private fun overrideWithoutAnnotations(
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
index a17ced3..8be0ffb 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/writer/KotlinCodeGenTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/writer/KotlinCodeGenTest.kt
@@ -22,10 +22,15 @@
import androidx.room.compiler.processing.util.XTestInvocation
import androidx.room.compiler.processing.util.runKspTest
import androidx.room.processor.Context
+import com.google.testing.junit.testparameterinjector.TestParameter
+import com.google.testing.junit.testparameterinjector.TestParameterInjector
import loadTestSource
+import org.jetbrains.kotlin.config.JvmDefaultMode
import org.junit.Test
+import org.junit.runner.RunWith
// Dany's Kotlin codegen test playground (and tests too)
+@RunWith(TestParameterInjector::class)
class KotlinCodeGenTest {
val databaseSrc = Source.kotlin(
@@ -677,6 +682,79 @@
)
}
+ @Test
+ fun delegatingFunctions_defaultImplBridge(
+ @TestParameter("DISABLE", "ALL_COMPATIBILITY", "ALL_INCOMPATIBLE")
+ jvmDefaultMode: JvmDefaultMode
+ ) {
+ val testName = object {}.javaClass.enclosingMethod!!.name
+ val src = Source.kotlin(
+ "MyDao.kt",
+ """
+ import androidx.room.*
+ import androidx.sqlite.db.SupportSQLiteQuery
+
+ @Dao
+ interface MyDao {
+ @Query("SELECT * FROM MyEntity")
+ fun getEntity(): MyEntity
+
+ fun implemented() {
+ TODO("")
+ }
+ }
+
+ @Entity
+ data class MyEntity(
+ @PrimaryKey
+ val pk: Long,
+ )
+ """.trimIndent()
+ )
+ runTest(
+ sources = listOf(src, databaseSrc),
+ expectedFilePath = getTestGoldenPath(testName),
+ jvmDefaultMode = jvmDefaultMode
+ )
+ }
+
+ @Test
+ fun delegatingFunctions_boxedPrimitiveBridge() {
+ val testName = object {}.javaClass.enclosingMethod!!.name
+ val src = Source.kotlin(
+ "MyDao.kt",
+ """
+ import androidx.room.*
+ import androidx.sqlite.db.SupportSQLiteQuery
+
+ interface BaseDao<T> {
+ fun getEntity(id: T): MyEntity
+
+ fun insertEntity(id: T): T
+ }
+
+ @Dao
+ interface MyDao : BaseDao<Long> {
+ @Query("SELECT * FROM MyEntity WHERE pk = :id")
+ override fun getEntity(id: Long): MyEntity
+
+ @Query("INSERT INTO MyEntity (pk) VALUES (:id)")
+ override fun insertEntity(id: Long): Long
+ }
+
+ @Entity
+ data class MyEntity(
+ @PrimaryKey
+ val pk: Long,
+ )
+ """.trimIndent()
+ )
+ runTest(
+ sources = listOf(src, databaseSrc),
+ expectedFilePath = getTestGoldenPath(testName),
+ )
+ }
+
private fun getTestGoldenPath(testName: String): String {
return "kotlinCodeGen/$testName.kt"
}
@@ -684,11 +762,13 @@
private fun runTest(
sources: List<Source>,
expectedFilePath: String,
+ jvmDefaultMode: JvmDefaultMode = JvmDefaultMode.DEFAULT,
handler: (XTestInvocation) -> Unit = { }
) {
runKspTest(
sources = sources,
options = mapOf(Context.BooleanProcessorOptions.GENERATE_KOTLIN.argName to "true"),
+ kotlincArguments = listOf("-Xjvm-default=${jvmDefaultMode.description}")
) {
val databaseFqn = "androidx.room.Database"
DatabaseProcessingStep().process(
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/delegatingFunctions_boxedPrimitiveBridge.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/delegatingFunctions_boxedPrimitiveBridge.kt
new file mode 100644
index 0000000..74c1fe5
--- /dev/null
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/delegatingFunctions_boxedPrimitiveBridge.kt
@@ -0,0 +1,79 @@
+import android.database.Cursor
+import androidx.room.RoomDatabase
+import androidx.room.RoomSQLiteQuery
+import androidx.room.RoomSQLiteQuery.Companion.acquire
+import androidx.room.SharedSQLiteStatement
+import androidx.room.util.getColumnIndexOrThrow
+import androidx.room.util.query
+import androidx.sqlite.db.SupportSQLiteStatement
+import java.lang.Class
+import javax.`annotation`.processing.Generated
+import kotlin.Int
+import kotlin.Long
+import kotlin.String
+import kotlin.Suppress
+import kotlin.collections.List
+import kotlin.jvm.JvmStatic
+
+@Generated(value = ["androidx.room.RoomProcessor"])
+@Suppress(names = ["unchecked", "deprecation"])
+public class MyDao_Impl : MyDao {
+ private val __db: RoomDatabase
+
+ private val __preparedStmtOfInsertEntity: SharedSQLiteStatement
+
+ public constructor(__db: RoomDatabase) {
+ this.__db = __db
+ this.__preparedStmtOfInsertEntity = object : SharedSQLiteStatement(__db) {
+ public override fun createQuery(): String {
+ val _query: String = "INSERT INTO MyEntity (pk) VALUES (?)"
+ return _query
+ }
+ }
+ }
+
+ public override fun insertEntity(id: Long): Long {
+ __db.assertNotSuspendingTransaction()
+ val _stmt: SupportSQLiteStatement = __preparedStmtOfInsertEntity.acquire()
+ var _argIndex: Int = 1
+ _stmt.bindLong(_argIndex, id)
+ __db.beginTransaction()
+ try {
+ val _result: Long = _stmt.executeInsert()
+ __db.setTransactionSuccessful()
+ return _result
+ } finally {
+ __db.endTransaction()
+ __preparedStmtOfInsertEntity.release(_stmt)
+ }
+ }
+
+ public override fun getEntity(id: Long): MyEntity {
+ val _sql: String = "SELECT * FROM MyEntity WHERE pk = ?"
+ val _statement: RoomSQLiteQuery = acquire(_sql, 1)
+ var _argIndex: Int = 1
+ _statement.bindLong(_argIndex, id)
+ __db.assertNotSuspendingTransaction()
+ val _cursor: Cursor = query(__db, _statement, false, null)
+ try {
+ val _cursorIndexOfPk: Int = getColumnIndexOrThrow(_cursor, "pk")
+ val _result: MyEntity
+ if (_cursor.moveToFirst()) {
+ val _tmpPk: Long
+ _tmpPk = _cursor.getLong(_cursorIndexOfPk)
+ _result = MyEntity(_tmpPk)
+ } else {
+ error("Cursor was empty, but expected a single item.")
+ }
+ return _result
+ } finally {
+ _cursor.close()
+ _statement.release()
+ }
+ }
+
+ public companion object {
+ @JvmStatic
+ public fun getRequiredConverters(): List<Class<*>> = emptyList()
+ }
+}
\ No newline at end of file
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/delegatingFunctions_defaultImplBridge.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/delegatingFunctions_defaultImplBridge.kt
new file mode 100644
index 0000000..4fdc4e8
--- /dev/null
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/delegatingFunctions_defaultImplBridge.kt
@@ -0,0 +1,51 @@
+import android.database.Cursor
+import androidx.room.RoomDatabase
+import androidx.room.RoomSQLiteQuery
+import androidx.room.RoomSQLiteQuery.Companion.acquire
+import androidx.room.util.getColumnIndexOrThrow
+import androidx.room.util.query
+import java.lang.Class
+import javax.`annotation`.processing.Generated
+import kotlin.Int
+import kotlin.Long
+import kotlin.String
+import kotlin.Suppress
+import kotlin.collections.List
+import kotlin.jvm.JvmStatic
+
+@Generated(value = ["androidx.room.RoomProcessor"])
+@Suppress(names = ["unchecked", "deprecation"])
+public class MyDao_Impl : MyDao {
+ private val __db: RoomDatabase
+
+ public constructor(__db: RoomDatabase) {
+ this.__db = __db
+ }
+
+ public override fun getEntity(): MyEntity {
+ val _sql: String = "SELECT * FROM MyEntity"
+ val _statement: RoomSQLiteQuery = acquire(_sql, 0)
+ __db.assertNotSuspendingTransaction()
+ val _cursor: Cursor = query(__db, _statement, false, null)
+ try {
+ val _cursorIndexOfPk: Int = getColumnIndexOrThrow(_cursor, "pk")
+ val _result: MyEntity
+ if (_cursor.moveToFirst()) {
+ val _tmpPk: Long
+ _tmpPk = _cursor.getLong(_cursorIndexOfPk)
+ _result = MyEntity(_tmpPk)
+ } else {
+ error("Cursor was empty, but expected a single item.")
+ }
+ return _result
+ } finally {
+ _cursor.close()
+ _statement.release()
+ }
+ }
+
+ public companion object {
+ @JvmStatic
+ public fun getRequiredConverters(): List<Class<*>> = emptyList()
+ }
+}
\ No newline at end of file