CLI: Change kotlin reflection to java reflection

The command line argument parser is using between 0.25s and 0.5s
(depending on platform) on finding annotated properties. This fix
replaces the slow kotlin reflection with java reflection, which is an
order of magnitude faster.

 #KT-58183 Fixed
This commit is contained in:
Troels Bjerre Lund
2023-04-29 07:55:23 +02:00
committed by Space Team
parent b28b0e70b6
commit 111bb461a9
12 changed files with 61 additions and 43 deletions
@@ -12,8 +12,8 @@ import org.jetbrains.kotlin.cli.common.arguments.CommonToolArguments
import org.jetbrains.kotlin.cli.common.arguments.isAdvanced
import org.jetbrains.kotlin.cli.common.arguments.resolvedDelimiter
import kotlin.reflect.KClass
import kotlin.reflect.full.findAnnotation
import kotlin.reflect.full.memberProperties
import kotlin.reflect.jvm.javaField
@Suppress("UNCHECKED_CAST")
@JvmOverloads
@@ -33,7 +33,7 @@ internal fun <T : CommonToolArguments> toArgumentStrings(
): List<String> = ArrayList<String>().apply {
val defaultArguments = type.newArgumentsInstance()
type.memberProperties.forEach { property ->
val argumentAnnotation = property.findAnnotation<Argument>() ?: return@forEach
val argumentAnnotation = property.javaField?.getAnnotation(Argument::class.java) ?: return@forEach
val rawPropertyValue = property.get(thisArguments)
val rawDefaultValue = property.get(defaultArguments)
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.config.JvmTarget
import kotlin.reflect.KClass
import kotlin.reflect.KProperty1
import kotlin.reflect.full.findAnnotation
import kotlin.reflect.jvm.javaField
//used by IJ facet import
@SuppressWarnings("unused")
@@ -24,7 +25,8 @@ sealed class ExplicitDefaultSubstitutor {
abstract fun isSubstitutable(args: List<String>): Boolean
protected val argument: Argument by lazy {
substitutedProperty.findAnnotation() ?: error("Property \"${substitutedProperty.name}\" has no Argument annotation")
substitutedProperty.javaField?.getAnnotation(Argument::class.java)
?: error("Property \"${substitutedProperty.name}\" has no Argument annotation")
}
}
@@ -14,10 +14,10 @@ import org.junit.jupiter.params.provider.MethodSource
import java.util.Base64.getEncoder
import kotlin.random.Random
import kotlin.reflect.*
import kotlin.reflect.full.findAnnotation
import kotlin.reflect.full.isSubtypeOf
import kotlin.reflect.full.memberProperties
import kotlin.reflect.full.withNullability
import kotlin.reflect.jvm.javaField
import kotlin.test.assertContentEquals
import kotlin.test.fail
@@ -72,7 +72,7 @@ class CompilerArgumentParsingTest {
private fun assertEqualArguments(expected: CommonToolArguments, actual: CommonToolArguments) {
if (expected::class != actual::class) fail("Expected class '${expected::class}', found: '${actual::class}'")
expected::class.memberProperties
.filter { it.findAnnotation<Argument>() != null }
.filter { it.javaField?.getAnnotation(Argument::class.java) != null }
.ifEmpty { fail("No members with ${Argument::class} annotation") }
.map { property ->
@Suppress("UNCHECKED_CAST")
@@ -11,8 +11,8 @@ import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.MethodSource
import kotlin.reflect.KClass
import kotlin.reflect.KVisibility
import kotlin.reflect.full.findAnnotation
import kotlin.reflect.full.memberProperties
import kotlin.reflect.jvm.javaField
import kotlin.test.fail
@@ -22,7 +22,7 @@ class CompilerArgumentsImplementationTest {
@MethodSource("implementations")
fun `test - all properties with Argument annotation - are public`(implementation: KClass<out CommonToolArguments>) {
implementation.memberProperties.forEach { property ->
if (property.findAnnotation<Argument>() != null) {
if (property.javaField?.getAnnotation(Argument::class.java) != null) {
if (property.visibility != KVisibility.PUBLIC) {
fail(
"Property '${property.name}: ${property.returnType}' " +