Read property accessor meteadata in KAPT

This CL updates KotlinMetadataElement to also read property accessors
and synthesize KmFunction for them so that JavacExecutableElement can
find them.

This does not have any impact right now but will eventually be used to
support internal properties properly.

Note that, accessors are not functions in metadata. As such, I'm
synthesizing a KmFunction from KmAccessor, which may not be 100%
accurate. When in doubt, I preferred 0 flags (instead of flags of
properties etc). Since accessors are not always defined, I also made the
KmValueParameter's name nullable.

I've also added name and jvmName to KmFunction to distinguish between
the name in source vs the name generated by the kotlin compiler. This
will again be used for supporting internal properties once we add
relevant API to XMethodElement.

Bug: 205289020
Test: KotlinMetadataElementTest
Change-Id: If85f0832ee91bcdff25a660b11e1a38e625fc60a
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/kotlin/KotlinClassMetadataUtils.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/kotlin/KotlinClassMetadataUtils.kt
index 30f0cac..ca4d05a 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/kotlin/KotlinClassMetadataUtils.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/kotlin/KotlinClassMetadataUtils.kt
@@ -16,6 +16,7 @@
 
 package androidx.room.compiler.processing.javac.kotlin
 
+import androidx.room.compiler.processing.util.sanitizeAsJavaParameterName
 import kotlinx.metadata.ClassName
 import kotlinx.metadata.Flag
 import kotlinx.metadata.Flags
@@ -25,15 +26,19 @@
 import kotlinx.metadata.KmExtensionType
 import kotlinx.metadata.KmFunctionExtensionVisitor
 import kotlinx.metadata.KmFunctionVisitor
+import kotlinx.metadata.KmPropertyExtensionVisitor
 import kotlinx.metadata.KmPropertyVisitor
 import kotlinx.metadata.KmTypeParameterVisitor
 import kotlinx.metadata.KmTypeVisitor
 import kotlinx.metadata.KmValueParameterVisitor
 import kotlinx.metadata.KmVariance
 import kotlinx.metadata.jvm.JvmConstructorExtensionVisitor
+import kotlinx.metadata.jvm.JvmFieldSignature
 import kotlinx.metadata.jvm.JvmFunctionExtensionVisitor
 import kotlinx.metadata.jvm.JvmMethodSignature
+import kotlinx.metadata.jvm.JvmPropertyExtensionVisitor
 import kotlinx.metadata.jvm.KotlinClassMetadata
+import java.util.Locale
 
 // represents a function or constructor
 internal interface KmExecutable {
@@ -44,6 +49,14 @@
  * Represents the kotlin metadata of a function
  */
 internal data class KmFunction(
+    /**
+     * Name of the function in byte code
+     */
+    val jvmName: String,
+    /**
+     * Name of the function in source code
+     */
+    val name: String,
     val descriptor: String,
     private val flags: Flags,
     override val parameters: List<KmValueParameter>,
@@ -65,7 +78,9 @@
 
 internal data class KmProperty(
     val name: String,
-    val type: KmType
+    val type: KmType,
+    val getter: KmFunction?,
+    val setter: KmFunction?
 ) {
     val typeParameters
         get() = type.typeArguments
@@ -118,7 +133,7 @@
     override fun visitFunction(flags: Flags, name: String): KmFunctionVisitor {
         return object : KmFunctionVisitor() {
 
-            lateinit var descriptor: String
+            lateinit var methodSignature: JvmMethodSignature
             val parameters = mutableListOf<KmValueParameter>()
             lateinit var returnType: KmType
 
@@ -137,7 +152,7 @@
                 }
                 return object : JvmFunctionExtensionVisitor() {
                     override fun visit(signature: JvmMethodSignature?) {
-                        descriptor = signature!!.asString()
+                        methodSignature = signature!!
                     }
                 }
             }
@@ -149,7 +164,16 @@
             }
 
             override fun visitEnd() {
-                result.add(KmFunction(descriptor, flags, parameters, returnType))
+                result.add(
+                    KmFunction(
+                        name = name,
+                        jvmName = methodSignature.name,
+                        descriptor = methodSignature.asString(),
+                        flags = flags,
+                        parameters = parameters,
+                        returnType = returnType
+                    )
+                )
             }
         }
     }
@@ -239,13 +263,45 @@
         getterFlags: Flags,
         setterFlags: Flags
     ): KmPropertyVisitor {
+        var setterParam: KmValueParameter? = null
+        var getter: JvmMethodSignature? = null
+        var setter: JvmMethodSignature? = null
         return object : KmPropertyVisitor() {
             lateinit var returnType: KmType
             override fun visitEnd() {
                 result.add(
                     KmProperty(
                         type = returnType,
-                        name = name
+                        name = name,
+                        setter = setter?.let { setterSignature ->
+                            // setter parameter visitor may not be invoked when not declared
+                            // explicitly
+                            val param = setterParam ?: KmValueParameter(
+                                // kotlinc will set this to set-? but it is better to not expose
+                                // it here since it is not valid name
+                                name = "set-?".sanitizeAsJavaParameterName(0),
+                                type = returnType,
+                                flags = 0
+                            )
+                            KmFunction(
+                                jvmName = setterSignature.name,
+                                name = computeSetterName(name),
+                                descriptor = setterSignature.asString(),
+                                flags = 0,
+                                parameters = listOf(param),
+                                returnType = KM_VOID_TYPE
+                            )
+                        },
+                        getter = getter?.let { getterSignature ->
+                            KmFunction(
+                                jvmName = getterSignature.name,
+                                name = computeGetterName(name),
+                                descriptor = getterSignature.asString(),
+                                flags = flags,
+                                parameters = emptyList(),
+                                returnType = returnType
+                            )
+                        }
                     )
                 )
             }
@@ -255,6 +311,35 @@
                     returnType = it
                 }
             }
+
+            override fun visitSetterParameter(
+                flags: Flags,
+                name: String
+            ): KmValueParameterVisitor {
+                return ValueParameterReader(
+                    name = name,
+                    flags = flags
+                ) {
+                    setterParam = it
+                }
+            }
+
+            override fun visitExtensions(type: KmExtensionType): KmPropertyExtensionVisitor? {
+                if (type != JvmPropertyExtensionVisitor.TYPE) {
+                    return null
+                }
+                return object : JvmPropertyExtensionVisitor() {
+                    override fun visit(
+                        jvmFlags: Flags,
+                        fieldSignature: JvmFieldSignature?,
+                        getterSignature: JvmMethodSignature?,
+                        setterSignature: JvmMethodSignature?
+                    ) {
+                        getter = getterSignature
+                        setter = setterSignature
+                    }
+                }
+            }
         }
     }
 }
@@ -388,3 +473,47 @@
         }
     }
 }
+
+/**
+ * Computes the getter name based on the property. Note that this might be different than the
+ * JVM name for internal properties.
+ * See: https://kotlinlang.org/docs/java-to-kotlin-interop.html#properties
+ */
+private fun computeGetterName(
+    propName: String
+): String {
+    return if (propName.startsWith("is")) {
+        propName
+    } else {
+        val capitalizedName = propName.replaceFirstChar {
+            if (it.isLowerCase()) it.titlecase(
+                Locale.US
+            ) else it.toString()
+        }
+        "get$capitalizedName"
+    }
+}
+
+/**
+ * Computes the getter name based on the property. Note that this might be different than the
+ * JVM name for internal properties.
+ * See: https://kotlinlang.org/docs/java-to-kotlin-interop.html#properties
+ */
+private fun computeSetterName(propName: String): String {
+    return if (propName.startsWith("is")) {
+        "set${propName.substring(2)}"
+    } else {
+        val capitalizedName = propName.replaceFirstChar {
+            if (it.isLowerCase()) it.titlecase(
+                Locale.US
+            ) else it.toString()
+        }
+        "set$capitalizedName"
+    }
+}
+
+private val KM_VOID_TYPE = KmType(
+    flags = 0,
+    typeArguments = emptyList(),
+    extendsBound = null
+)
\ No newline at end of file
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/kotlin/KotlinMetadataElement.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/kotlin/KotlinMetadataElement.kt
index 10b7be7..adb2631 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/kotlin/KotlinMetadataElement.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/kotlin/KotlinMetadataElement.kt
@@ -71,7 +71,23 @@
             "must pass an element type of method"
         }
         val methodSignature = method.descriptor
-        return functionList.firstOrNull { it.descriptor == methodSignature }
+        functionList.firstOrNull { it.descriptor == methodSignature }?.let {
+            return it
+        }
+        // might be a property getter or setter
+        return propertyList.firstNotNullOfOrNull { property ->
+            when {
+                property.getter?.descriptor == methodSignature -> {
+                    property.getter
+                }
+                property.setter?.descriptor == methodSignature -> {
+                    property.setter
+                }
+                else -> {
+                    null
+                }
+            }
+        }
     }
 
     fun getConstructorMetadata(method: ExecutableElement): KmConstructor? {
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/util/NamingUtils.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/util/NamingUtils.kt
index 1d96f2d..7889069 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/util/NamingUtils.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/util/NamingUtils.kt
@@ -27,5 +27,5 @@
 ): String = if (this != null && SourceVersion.isName(this)) {
     this
 } else {
-    "arg$argIndex"
+    "p$argIndex"
 }
\ No newline at end of file
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/FallbackLocationInformationTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/FallbackLocationInformationTest.kt
index b4cbf09..fa4306c 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/FallbackLocationInformationTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/FallbackLocationInformationTest.kt
@@ -49,10 +49,10 @@
             class JavaSubject {
                 String field1;
                 // naming this arg0 because javac cannot read the real param name after compilation
-                JavaSubject(int arg0) {
+                JavaSubject(int p0) {
                 }
                 // naming this arg0 because javac cannot read the real param name after compilation
-                void method1(int arg0) {}
+                void method1(int p0) {}
             }
             """.trimIndent()
         )
@@ -109,7 +109,7 @@
                 assertThat(
                     propSetter.parameters.first().fallbackLocationText
                 ).isEqualTo(
-                    "arg0 in foo.bar.KotlinSubject.setProp(java.lang.String)"
+                    "p0 in foo.bar.KotlinSubject.setProp(java.lang.String)"
                 )
             }
 
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableElementTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableElementTest.kt
index 7a03f87..a30674a 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableElementTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableElementTest.kt
@@ -416,6 +416,7 @@
                     protected set
                 protected var prop7: String
                     private set
+                internal var prop8: String
             }
             """.trimIndent()
         )
@@ -429,19 +430,24 @@
                     "setX", "setProp1", "setProp3", "setZ", "setProp4", "getProp4", "setProp7"
                 )
             )
-            listOf("getX", "getProp1", "getProp2", "getProp3", "getProp5", "getProp6").forEach {
+            listOf("getX", "getProp1", "getProp2", "getProp3", "getProp5", "getProp6",
+                "getProp8\$main").forEach {
                 klass.getMethod(it).let { method ->
                     assertThat(method.returnType.typeName).isEqualTo(String::class.typeName())
                     assertThat(method.parameters).isEmpty()
+                    assertThat(method.returnType.nullability).isEqualTo(XNullability.NONNULL)
                 }
             }
-            listOf("setY", "setProp2").forEach {
+            listOf("setY", "setProp2", "setProp8\$main").forEach {
                 klass.getMethod(it).let { method ->
                     assertThat(method.returnType.typeName).isEqualTo(TypeName.VOID)
                     assertThat(method.parameters.first().type.typeName).isEqualTo(
                         String::class.typeName()
                     )
                     assertThat(method.isPublic()).isTrue()
+                    assertThat(method.parameters.first().type.nullability).isEqualTo(
+                        XNullability.NONNULL
+                    )
                 }
             }
             listOf("getProp5", "getProp7").forEach {
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/javac/kotlin/KotlinMetadataElementTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/javac/kotlin/KotlinMetadataElementTest.kt
index 6825149..79b3ae2 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/javac/kotlin/KotlinMetadataElementTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/javac/kotlin/KotlinMetadataElementTest.kt
@@ -16,22 +16,29 @@
 
 package androidx.room.compiler.processing.javac.kotlin
 
+import androidx.room.compiler.processing.XNullability
 import androidx.room.compiler.processing.javac.JavacProcessingEnv
+import androidx.room.compiler.processing.javac.nullability
 import androidx.room.compiler.processing.util.Source
 import androidx.room.compiler.processing.util.compileFiles
 import androidx.room.compiler.processing.util.runJavaProcessorTest
 import androidx.room.compiler.processing.util.runKaptTest
+import androidx.room.compiler.processing.util.sanitizeAsJavaParameterName
 import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
 import org.junit.AssumptionViolatedException
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
+import org.junit.runners.Parameterized
 import javax.annotation.processing.ProcessingEnvironment
+import javax.lang.model.element.ExecutableElement
 import javax.lang.model.element.TypeElement
 import javax.lang.model.util.ElementFilter
 
-@RunWith(JUnit4::class)
-class KotlinMetadataElementTest {
+@RunWith(Parameterized::class)
+class KotlinMetadataElementTest(
+    private val preCompiled: Boolean
+) {
 
     @Test
     fun constructorParameters() {
@@ -197,7 +204,7 @@
                 "suspendFunction" to true,
                 "functionWithParams" to false,
                 "suspendFunctionWithParams" to false,
-                "getConstructorParam" to null // synthetic getter for constructor property
+                "getConstructorParam" to false // synthetic getter for constructor property
             )
         }
     }
@@ -307,6 +314,250 @@
     }
 
     @Test
+    fun accessors() {
+        val src = Source.kotlin(
+            "Kotlin.kt",
+            """
+            @JvmInline
+            value class ValueClass(val value: String)
+            class Subject {
+                val immutableProperty: String = ""
+                var mutableProperty: String = ""
+                var isProperty: String? = ""
+                var customSetter: String=  ""
+                    get(): String = ""
+                    set(myValue) { field = myValue }
+                var privateSetter: String = ""
+                    private set
+                internal var internalProp: String? = ""
+                internal var isInternalProp2: String = ""
+                // these won't show up in KAPT stubs since they don't have a valid JVM name but
+                // we are still testing them for consistency as they'll show up in metadata
+                var valueProp: ValueClass? = null
+                // these won't show up in KAPT stubs since they don't have a valid JVM name but
+                // we are still testing them for consistency as they'll show up in metadata
+                internal var internalValueProp: ValueClass = ValueClass("?")
+            }
+        """.trimIndent()
+        )
+        simpleRun(listOf(src)) { invocation ->
+            val (element, metadata) = getMetadataElement(
+                invocation,
+                "Subject"
+            )
+
+            fun assertSetter(
+                kmFunction: KmFunction?,
+                name: String,
+                jvmName: String,
+                paramNullable: Boolean
+            ) {
+                checkNotNull(kmFunction)
+                assertWithMessage(kmFunction.toString()).apply {
+                    that(kmFunction.name).isEqualTo(name)
+                    that(kmFunction.jvmName).isEqualTo(jvmName)
+                    // void is non null
+                    that(kmFunction.returnType.nullability).isEqualTo(XNullability.NONNULL)
+                    that(kmFunction.parameters).hasSize(1)
+                    that(
+                        kmFunction.parameters.single().isNullable()
+                    ).isEqualTo(paramNullable)
+                }
+            }
+
+            fun assertSetter(
+                metadata: KotlinMetadataElement,
+                method: ExecutableElement,
+                name: String,
+                jvmName: String,
+                paramNullable: Boolean
+            ) {
+                val kmFunction = metadata.getFunctionMetadata(method)
+                assertSetter(
+                    kmFunction = kmFunction,
+                    name = name,
+                    jvmName = jvmName,
+                    paramNullable = paramNullable
+                )
+                val paramName = kmFunction!!.parameters.single().name
+                val javacElementName = method.parameters.single().simpleName.toString()
+                assertWithMessage(
+                    kmFunction.toString()
+                ).that(
+                    paramName
+                ).isEqualTo(
+                    javacElementName.sanitizeAsJavaParameterName(0)
+                )
+            }
+
+            fun assertGetter(
+                kmFunction: KmFunction?,
+                name: String,
+                jvmName: String,
+                returnsNullable: Boolean
+            ) {
+                checkNotNull(kmFunction)
+                assertWithMessage(kmFunction.toString()).apply {
+                    that(kmFunction.name).isEqualTo(name)
+                    that(kmFunction.jvmName).isEqualTo(jvmName)
+                    that(kmFunction.returnType.isNullable()).isEqualTo(returnsNullable)
+                    that(kmFunction.parameters).isEmpty()
+                }
+            }
+            assertGetter(
+                kmFunction = metadata.getFunctionMetadata(
+                    element.getDeclaredMethod("getImmutableProperty")
+                ),
+                name = "getImmutableProperty",
+                jvmName = "getImmutableProperty",
+                returnsNullable = false
+            )
+            assertGetter(
+                kmFunction = metadata.getFunctionMetadata(
+                    element.getDeclaredMethod("getMutableProperty")
+                ),
+                name = "getMutableProperty",
+                jvmName = "getMutableProperty",
+                returnsNullable = false
+            )
+            assertSetter(
+                metadata = metadata,
+                method = element.getDeclaredMethod("setMutableProperty"),
+                name = "setMutableProperty",
+                jvmName = "setMutableProperty",
+                paramNullable = false
+            )
+            assertSetter(
+                metadata = metadata,
+                method = element.getDeclaredMethod("setProperty"),
+                name = "setProperty",
+                jvmName = "setProperty",
+                paramNullable = true
+            )
+            assertGetter(
+                kmFunction = metadata.getFunctionMetadata(
+                    element.getDeclaredMethod("isProperty")
+                ),
+                name = "isProperty",
+                jvmName = "isProperty",
+                returnsNullable = true
+            )
+            assertGetter(
+                kmFunction = metadata.getFunctionMetadata(
+                    element.getDeclaredMethod("getInternalProp\$main")
+                ),
+                name = "getInternalProp",
+                jvmName = "getInternalProp\$main",
+                returnsNullable = true
+            )
+            assertSetter(
+                metadata = metadata,
+                method = element.getDeclaredMethod("setInternalProp\$main"),
+                name = "setInternalProp",
+                jvmName = "setInternalProp\$main",
+                paramNullable = true
+            )
+            assertGetter(
+                kmFunction = metadata.getFunctionMetadata(
+                    element.getDeclaredMethod("isInternalProp2\$main")
+                ),
+                name = "isInternalProp2",
+                jvmName = "isInternalProp2\$main",
+                returnsNullable = false
+            )
+            assertSetter(
+                metadata = metadata,
+                method = element.getDeclaredMethod("setInternalProp2\$main"),
+                name = "setInternalProp2",
+                jvmName = "setInternalProp2\$main",
+                paramNullable = false
+            )
+            // read custom setter name properly
+            metadata.getFunctionMetadata(
+                element.getDeclaredMethod("setCustomSetter")
+            ).let { kmFunction ->
+                checkNotNull(kmFunction)
+                assertThat(
+                    kmFunction.parameters.single().name
+                ).isEqualTo("myValue")
+            }
+            // tests value class properties. They won't show up in KAPT stubs since they don't have
+            // valid java source names but we still validate them here for consistency. Maybe one
+            // day we'll change Javac element to include these if we support Kotlin codegen in KAPT
+            metadata.getPropertyMetadata("valueProp").let { valueProp ->
+                assertGetter(
+                    kmFunction = valueProp?.getter,
+                    name = "getValueProp",
+                    jvmName = "getValueProp-4LjoGxk",
+                    returnsNullable = true
+                )
+                assertSetter(
+                    kmFunction = valueProp?.setter,
+                    name = "setValueProp",
+                    jvmName = "setValueProp-d8IPsTA",
+                    paramNullable = true
+                )
+            }
+            metadata.getPropertyMetadata("internalValueProp").let { valueProp ->
+                assertGetter(
+                    kmFunction = valueProp?.getter,
+                    name = "getInternalValueProp",
+                    jvmName = "getInternalValueProp-NMFyIOA\$main",
+                    returnsNullable = false
+                )
+                assertSetter(
+                    kmFunction = valueProp?.setter,
+                    name = "setInternalValueProp",
+                    jvmName = "setInternalValueProp-FVOWAsA\$main",
+                    paramNullable = false
+                )
+            }
+        }
+    }
+
+    @Test
+    fun internalMethodName() {
+        val src = Source.kotlin(
+            "Kotlin.kt",
+            """
+            class Subject {
+                internal fun internalFun() {}
+                fun normalFun() {}
+                // there is no test case for functions receiving/returning value classes because
+                // they are not visible through KAPT
+            }
+        """.trimIndent()
+        )
+        simpleRun(listOf(src)) { invocation ->
+            val (element, metadata) = getMetadataElement(
+                invocation,
+                "Subject"
+            )
+            metadata.getFunctionMetadata(
+                element.getDeclaredMethod("internalFun\$main")
+            ).let { functionMetadata ->
+                assertThat(
+                    functionMetadata?.jvmName
+                ).isEqualTo("internalFun\$main")
+                assertThat(
+                    functionMetadata?.name
+                ).isEqualTo("internalFun")
+            }
+
+            metadata.getFunctionMetadata(
+                element.getDeclaredMethod("normalFun")
+            ).let { functionMetadata ->
+                assertThat(
+                    functionMetadata?.jvmName
+                ).isEqualTo("normalFun")
+                assertThat(
+                    functionMetadata?.name
+                ).isEqualTo("normalFun")
+            }
+        }
+    }
+
+    @Test
     fun genericParameterNullability() {
         val src = Source.kotlin(
             "Subject.kt",
@@ -470,6 +721,9 @@
 
     @Test
     fun withoutKotlinInClasspath() {
+        if (preCompiled) {
+            throw AssumptionViolatedException("this test doesn't care for precompiled code")
+        }
         val libSource = Source.kotlin(
             "lib.kt",
             """
@@ -505,11 +759,17 @@
 
     private fun TypeElement.getConstructors() = ElementFilter.constructorsIn(enclosedElements)
 
+    @Suppress("NAME_SHADOWING") // intentional
     private fun simpleRun(
         sources: List<Source> = emptyList(),
         handler: (ProcessingEnvironment) -> Unit
     ) {
-        runKaptTest(sources) {
+        val (sources, classpath) = if (preCompiled) {
+            emptyList<Source>() to compileFiles(sources)
+        } else {
+            sources to emptyList()
+        }
+        runKaptTest(sources = sources, classpath = classpath) {
             val processingEnv = it.processingEnv
             if (processingEnv !is JavacProcessingEnv) {
                 throw AssumptionViolatedException("This test only works for java/kapt compilation")
@@ -522,4 +782,10 @@
         processingEnv.elementUtils.getTypeElement(qName).let {
             it to KotlinMetadataElement.createFor(it)!!
         }
+
+    companion object {
+        @JvmStatic
+        @Parameterized.Parameters(name = "preCompiled_{0}")
+        fun params() = arrayOf(false, true)
+    }
 }
\ No newline at end of file