diff --git a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/parseCommandLineArguments.kt b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/parseCommandLineArguments.kt index c72cc0e1b027b..f8b9aed723eac 100644 --- a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/parseCommandLineArguments.kt +++ b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/parseCommandLineArguments.kt @@ -21,6 +21,7 @@ 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 java.util.concurrent.ConcurrentHashMap import kotlin.reflect.KClass import kotlin.reflect.cast @@ -114,54 +115,38 @@ fun parseCommandLineArgumentsFromEnvironment(arguments parseCommandLineArguments(settingsFromEnvironment, arguments, overrideArguments = true) } -private fun parsePreprocessedCommandLineArguments( - args: List, - result: A, - errors: Lazy, - overrideArguments: Boolean -) { - data class ArgumentField(val getter: Method, val setter: Method, val argument: Argument) +private data class ArgumentField(val getter: Method, val setter: Method, val argument: Argument) - val superClasses = mutableListOf>(result::class.java) - while (superClasses.last() != Any::class.java) { - superClasses.add(superClasses.last().superclass) - } +private val argumentsCache = ConcurrentHashMap, Map>() - val resultClass = result::class.java - val properties = superClasses.flatMap { - it.declaredFields.mapNotNull { field -> +private fun getArguments(klass: Class<*>): Map = argumentsCache.getOrPut(klass) { + if (klass == Any::class.java) emptyMap() + else buildMap { + putAll(getArguments(klass.superclass)) + for (field in klass.declaredFields) { 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 getter = klass.getMethod(JvmAbi.getterName(field.name)) + val setter = klass.getMethod(JvmAbi.setterName(field.name), field.type) + val argumentField = ArgumentField(getter, setter, argument) + for (key in listOf(argument.value, argument.shortName, argument.deprecatedName)) { + if (key.isNotEmpty()) put(key, argumentField) + } } } } +} + +private fun parsePreprocessedCommandLineArguments( + args: List, + result: A, + errors: Lazy, + overrideArguments: Boolean +) { + val properties = getArguments(result::class.java) val visitedArgs = mutableSetOf() var freeArgsStarted = false - fun ArgumentField.matches(arg: String): Boolean { - if (argument.shortName.takeUnless(String::isEmpty) == arg) { - return true - } - - val deprecatedName = argument.deprecatedName - if (deprecatedName.isNotEmpty() && (deprecatedName == arg || arg.startsWith("$deprecatedName="))) { - errors.value.deprecatedArguments[deprecatedName] = argument.value - return true - } - - if (argument.value == arg) { - if (argument.isAdvanced && getter.returnType.kotlin != Boolean::class) { - errors.value.extraArgumentsPassedInObsoleteForm.add(arg) - } - return true - } - - return arg.startsWith(argument.value + "=") - } - val freeArgs = ArrayList() val internalArguments = ArrayList() @@ -199,7 +184,8 @@ private fun parsePreprocessedCommandLineArguments( continue } - val argumentField = properties.firstOrNull { it.matches(arg) } + val key = arg.substringBefore('=') + val argumentField = properties[key] if (argumentField == null) { when { arg.startsWith(ADVANCED_ARGUMENT_PREFIX) -> errors.value.unknownExtraFlags.add(arg) @@ -210,6 +196,24 @@ private fun parsePreprocessedCommandLineArguments( } val (getter, setter, argument) = argumentField + + // Tests for -shortName=value, which isn't currently allowed. + if (key != arg && key == argument.shortName) { + errors.value.unknownArgs.add(arg) + continue + } + + val deprecatedName = argument.deprecatedName + if (deprecatedName == key) { + errors.value.deprecatedArguments[deprecatedName] = argument.value + } + + if (argument.value == arg) { + if (argument.isAdvanced && getter.returnType.kotlin != Boolean::class) { + errors.value.extraArgumentsPassedInObsoleteForm.add(arg) + } + } + val value: Any = when { getter.returnType.kotlin == Boolean::class -> { if (arg.startsWith(argument.value + "=")) {