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:
committed by
Space Team
parent
b28b0e70b6
commit
111bb461a9
+31
-21
@@ -18,13 +18,13 @@ package org.jetbrains.kotlin.cli.common.arguments
|
||||
|
||||
import org.jetbrains.kotlin.cli.common.CompilerSystemProperties
|
||||
import org.jetbrains.kotlin.konan.file.File
|
||||
import org.jetbrains.kotlin.load.java.JvmAbi
|
||||
import org.jetbrains.kotlin.utils.SmartList
|
||||
import java.lang.reflect.Method
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KMutableProperty1
|
||||
import kotlin.reflect.cast
|
||||
import kotlin.reflect.full.memberProperties
|
||||
|
||||
@Target(AnnotationTarget.PROPERTY)
|
||||
@Target(AnnotationTarget.FIELD)
|
||||
annotation class Argument(
|
||||
val value: String,
|
||||
val shortName: String = "",
|
||||
@@ -120,13 +120,22 @@ private fun <A : CommonToolArguments> parsePreprocessedCommandLineArguments(
|
||||
errors: Lazy<ArgumentParseErrors>,
|
||||
overrideArguments: Boolean
|
||||
) {
|
||||
data class ArgumentField(val property: KMutableProperty1<A, Any?>, val argument: Argument)
|
||||
data class ArgumentField(val getter: Method, val setter: Method, val argument: Argument)
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val properties = result::class.memberProperties.mapNotNull { property ->
|
||||
if (property !is KMutableProperty1<*, *>) return@mapNotNull null
|
||||
val argument = property.annotations.firstOrNull { it is Argument } as Argument? ?: return@mapNotNull null
|
||||
ArgumentField(property as KMutableProperty1<A, Any?>, argument)
|
||||
val superClasses = mutableListOf<Class<*>>(result::class.java)
|
||||
while (superClasses.last() != Any::class.java) {
|
||||
superClasses.add(superClasses.last().superclass)
|
||||
}
|
||||
|
||||
val resultClass = result::class.java
|
||||
val properties = superClasses.flatMap {
|
||||
it.declaredFields.mapNotNull { field ->
|
||||
field.getAnnotation(Argument::class.java)?.let { argument ->
|
||||
val getter = resultClass.getMethod(JvmAbi.getterName(field.name))
|
||||
val setter = resultClass.getMethod(JvmAbi.setterName(field.name), field.type)
|
||||
ArgumentField(getter, setter, argument)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val visitedArgs = mutableSetOf<String>()
|
||||
@@ -144,7 +153,7 @@ private fun <A : CommonToolArguments> parsePreprocessedCommandLineArguments(
|
||||
}
|
||||
|
||||
if (argument.value == arg) {
|
||||
if (argument.isAdvanced && property.returnType.classifier != Boolean::class) {
|
||||
if (argument.isAdvanced && getter.returnType.kotlin != Boolean::class) {
|
||||
errors.value.extraArgumentsPassedInObsoleteForm.add(arg)
|
||||
}
|
||||
return true
|
||||
@@ -200,9 +209,9 @@ private fun <A : CommonToolArguments> parsePreprocessedCommandLineArguments(
|
||||
continue
|
||||
}
|
||||
|
||||
val (property, argument) = argumentField
|
||||
val (getter, setter, argument) = argumentField
|
||||
val value: Any = when {
|
||||
argumentField.property.returnType.classifier == Boolean::class -> {
|
||||
getter.returnType.kotlin == Boolean::class -> {
|
||||
if (arg.startsWith(argument.value + "=")) {
|
||||
// Can't use toBooleanStrict yet because this part of the compiler is used in Gradle and needs API version 1.4.
|
||||
when (arg.substring(argument.value.length + 1)) {
|
||||
@@ -227,13 +236,12 @@ private fun <A : CommonToolArguments> parsePreprocessedCommandLineArguments(
|
||||
}
|
||||
}
|
||||
|
||||
if ((argumentField.property.returnType.classifier as? KClass<*>)?.java?.isArray == false
|
||||
&& !visitedArgs.add(argument.value) && value is String && property.get(result) != value
|
||||
if (!getter.returnType.isArray && !visitedArgs.add(argument.value) && value is String && getter(result) != value
|
||||
) {
|
||||
errors.value.duplicateArguments[argument.value] = value
|
||||
}
|
||||
|
||||
updateField(property, result, value, argument.resolvedDelimiter, overrideArguments)
|
||||
updateField(getter, setter, result, value, argument.resolvedDelimiter, overrideArguments)
|
||||
}
|
||||
|
||||
result.freeArgs += freeArgs
|
||||
@@ -257,25 +265,27 @@ private fun <A : CommonToolArguments> A.updateInternalArguments(
|
||||
}
|
||||
|
||||
private fun <A : CommonToolArguments> updateField(
|
||||
property: KMutableProperty1<A, Any?>,
|
||||
getter: Method,
|
||||
setter: Method,
|
||||
result: A,
|
||||
value: Any,
|
||||
delimiter: String?,
|
||||
overrideArguments: Boolean
|
||||
) {
|
||||
when (property.returnType.classifier) {
|
||||
Boolean::class, String::class -> property.set(result, value)
|
||||
when (getter.returnType.kotlin) {
|
||||
Boolean::class, String::class -> setter(result, value)
|
||||
Array<String>::class -> {
|
||||
val newElements = if (delimiter.isNullOrEmpty()) {
|
||||
arrayOf(value as String)
|
||||
} else {
|
||||
(value as String).split(delimiter).toTypedArray()
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val oldValue = property.get(result) as Array<String>?
|
||||
property.set(result, if (oldValue != null && !overrideArguments) arrayOf(*oldValue, *newElements) else newElements)
|
||||
val oldValue = getter(result) as Array<String>?
|
||||
setter(result, if (oldValue != null && !overrideArguments) arrayOf(*oldValue, *newElements) else newElements)
|
||||
}
|
||||
else -> throw IllegalStateException("Unsupported argument type: ${property.returnType}")
|
||||
else -> throw IllegalStateException("Unsupported argument type: ${getter.returnType}")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user