Move parametersMap.kt to 'cli-common'

Also reformat and slightly refactor it. This allows to get rid of
dependency of 'util' on 'kotlin-reflect-api'.
This commit is contained in:
Alexander Udalov
2020-03-15 19:00:59 +01:00
committed by Alexander Udalov
parent 92534eadaa
commit 87da4be9bb
2 changed files with 63 additions and 60 deletions
@@ -1,5 +1,5 @@
/* /*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
@@ -10,23 +10,20 @@ import kotlin.reflect.full.isSubclassOf
import kotlin.reflect.jvm.jvmErasure import kotlin.reflect.jvm.jvmErasure
fun tryConstructClassFromStringArgs(clazz: Class<*>, args: List<String>): Any? { fun tryConstructClassFromStringArgs(clazz: Class<*>, args: List<String>): Any? {
return try {
try { clazz.getConstructor(Array<String>::class.java).newInstance(args.toTypedArray())
return clazz.getConstructor(Array<String>::class.java).newInstance(args.toTypedArray()) } catch (e: NoSuchMethodException) {
}
catch (e: NoSuchMethodException) {
for (ctor in clazz.kotlin.constructors) { for (ctor in clazz.kotlin.constructors) {
val mapping = tryCreateCallableMappingFromStringArgs(ctor, args) val mapping = tryCreateCallableMappingFromStringArgs(ctor, args)
if (mapping != null) { if (mapping != null) {
try { try {
return ctor.callBy(mapping) return ctor.callBy(mapping)
} } catch (e: Exception) { // TODO: find the exact exception type thrown then callBy fails
catch (e: Exception) { // TODO: find the exact exception type thrown then callBy fails
} }
} }
} }
null
} }
return null
} }
fun tryCreateCallableMapping(callable: KCallable<*>, args: List<Any?>): Map<KParameter, Any?>? = fun tryCreateCallableMapping(callable: KCallable<*>, args: List<Any?>): Map<KParameter, Any?>? =
@@ -60,11 +57,11 @@ fun tryCreateCallableMappingFromNamedArgs(callable: KCallable<*>, args: List<Pai
private data class NamedArgument<out T>(val name: String?, val value: T?) private data class NamedArgument<out T>(val name: String?, val value: T?)
private interface ArgsConverter<T> { private interface ArgsConverter<T> {
sealed class Result { sealed class Result {
object Failure : Result() object Failure : Result()
class Success(val v: Any?) : Result() class Success(val v: Any?) : Result()
} }
fun tryConvertSingle(parameter: KParameter, arg: NamedArgument<T>): Result fun tryConvertSingle(parameter: KParameter, arg: NamedArgument<T>): Result
fun tryConvertVararg(parameter: KParameter, firstArg: NamedArgument<T>, restArgsIt: Iterator<NamedArgument<T>>): Result fun tryConvertVararg(parameter: KParameter, firstArg: NamedArgument<T>, restArgsIt: Iterator<NamedArgument<T>>): Result
fun tryConvertTail(parameter: KParameter, firstArg: NamedArgument<T>, restArgsIt: Iterator<NamedArgument<T>>): Result fun tryConvertTail(parameter: KParameter, firstArg: NamedArgument<T>, restArgsIt: Iterator<NamedArgument<T>>): Result
@@ -72,7 +69,11 @@ private interface ArgsConverter<T> {
private enum class ArgsTraversalState { UNNAMED, NAMED, TAIL } private enum class ArgsTraversalState { UNNAMED, NAMED, TAIL }
private fun <T> tryCreateCallableMapping(callable: KCallable<*>, args: Iterator<NamedArgument<T>>, converter: ArgsConverter<T>): Map<KParameter, Any?>? { private fun <T> tryCreateCallableMapping(
callable: KCallable<*>,
args: Iterator<NamedArgument<T>>,
converter: ArgsConverter<T>
): Map<KParameter, Any?>? {
val res = mutableMapOf<KParameter, Any?>() val res = mutableMapOf<KParameter, Any?>()
var state = ArgsTraversalState.UNNAMED var state = ArgsTraversalState.UNNAMED
val unboundParams = callable.parameters.toMutableList() val unboundParams = callable.parameters.toMutableList()
@@ -98,28 +99,24 @@ private fun <T> tryCreateCallableMapping(callable: KCallable<*>, args: Iterator<
// if we do not allow to overload on nullability, drop this check // if we do not allow to overload on nullability, drop this check
return null // failed to match: null for a non-nullable value return null // failed to match: null for a non-nullable value
} }
res.put(par, cvtRes.v) res[par] = cvtRes.v
} } else if (par.type.jvmErasure.java.isArray) {
else if (par.type.jvmErasure.java.isArray) {
// try vararg // try vararg
val cvtVRes = converter.tryConvertVararg(par, arg, argIt) val cvtVRes = converter.tryConvertVararg(par, arg, argIt)
if (cvtVRes is ArgsConverter.Result.Success) { if (cvtVRes is ArgsConverter.Result.Success) {
res.put(par, cvtVRes.v) res[par] = cvtVRes.v
} } else return null // failed to match: no suitable param for unnamed arg
else return null // failed to match: no suitable param for unnamed arg } else return null // failed to match: no suitable param for unnamed arg
}
else return null // failed to match: no suitable param for unnamed arg
} }
ArgsTraversalState.NAMED -> { ArgsTraversalState.NAMED -> {
assert(arg.name != null) assert(arg.name != null)
val parIdx = unboundParams.indexOfFirst { it.name == arg.name }.takeIf { it >= 0 } val parIdx = unboundParams.indexOfFirst { it.name == arg.name }.takeIf { it >= 0 }
?: return null // failed to match: no matching named parameter found ?: return null // failed to match: no matching named parameter found
val par = unboundParams.removeAt(parIdx) val par = unboundParams.removeAt(parIdx)
val cvtRes = converter.tryConvertSingle(par, arg) val cvtRes = converter.tryConvertSingle(par, arg)
if (cvtRes is ArgsConverter.Result.Success) { if (cvtRes is ArgsConverter.Result.Success) {
res.put(par, cvtRes.v) res[par] = cvtRes.v
} } else return null // failed to match: cannot convert arg to param's type
else return null // failed to match: cannot convert arg to param's type
} }
ArgsTraversalState.TAIL -> { ArgsTraversalState.TAIL -> {
assert(arg.name == null) assert(arg.name == null)
@@ -127,9 +124,8 @@ private fun <T> tryCreateCallableMapping(callable: KCallable<*>, args: Iterator<
val cvtVRes = converter.tryConvertTail(par, arg, argIt) val cvtVRes = converter.tryConvertTail(par, arg, argIt)
if (cvtVRes is ArgsConverter.Result.Success) { if (cvtVRes is ArgsConverter.Result.Success) {
if (argIt.hasNext()) return null // failed to match: not all tail args are consumed if (argIt.hasNext()) return null // failed to match: not all tail args are consumed
res.put(par, cvtVRes.v) res[par] = cvtVRes.v
} } else return null // failed to match: no suitable param for tail arg(s)
else return null // failed to match: no suitable param for tail arg(s)
} }
} }
} }
@@ -140,12 +136,10 @@ private fun <T> tryCreateCallableMapping(callable: KCallable<*>, args: Iterator<
} }
private fun KType.allowsNulls(): Boolean = private fun KType.allowsNulls(): Boolean =
isMarkedNullable || isMarkedNullable || classifier.let { it is KTypeParameter && it.upperBounds.any(KType::allowsNulls) }
classifier.let { it is KTypeParameter && it.upperBounds.any(KType::allowsNulls) }
private class StringArgsConverter : ArgsConverter<String> { private class StringArgsConverter : ArgsConverter<String> {
override fun tryConvertSingle(parameter: KParameter, arg: NamedArgument<String>): ArgsConverter.Result { override fun tryConvertSingle(parameter: KParameter, arg: NamedArgument<String>): ArgsConverter.Result {
val value = arg.value ?: return ArgsConverter.Result.Success(null) val value = arg.value ?: return ArgsConverter.Result.Success(null)
@@ -165,20 +159,23 @@ private class StringArgsConverter : ArgsConverter<String> {
return if (primitive != null) ArgsConverter.Result.Success(primitive) else ArgsConverter.Result.Failure return if (primitive != null) ArgsConverter.Result.Success(primitive) else ArgsConverter.Result.Failure
} }
override fun tryConvertVararg(parameter: KParameter, firstArg: NamedArgument<String>, restArgsIt: Iterator<NamedArgument<String>>): ArgsConverter.Result { override fun tryConvertVararg(
parameter: KParameter,
firstArg: NamedArgument<String>,
restArgsIt: Iterator<NamedArgument<String>>
): ArgsConverter.Result {
fun convertPrimitivesArray(type: KType, args: Sequence<String?>): Any? = fun convertPrimitivesArray(type: KType, args: Sequence<String?>): Any? =
when (type.classifier) { when (type.classifier) {
IntArray::class -> args.map { it?.toIntOrNull() } IntArray::class -> args.map { it?.toIntOrNull() }
LongArray::class -> args.map { it?.toLongOrNull() } LongArray::class -> args.map { it?.toLongOrNull() }
ShortArray::class -> args.map { it?.toShortOrNull() } ShortArray::class -> args.map { it?.toShortOrNull() }
ByteArray::class -> args.map { it?.toByteOrNull() } ByteArray::class -> args.map { it?.toByteOrNull() }
CharArray::class -> args.map { it?.singleOrNull() } CharArray::class -> args.map { it?.singleOrNull() }
FloatArray::class -> args.map { it?.toFloatOrNull() } FloatArray::class -> args.map { it?.toFloatOrNull() }
DoubleArray::class -> args.map { it?.toDoubleOrNull() } DoubleArray::class -> args.map { it?.toDoubleOrNull() }
BooleanArray::class -> args.map { it?.toBoolean() } BooleanArray::class -> args.map { it?.toBoolean() }
else -> null else -> null
}?.toList()?.takeUnless { null in it }?.toTypedArray() }?.toList()?.takeUnless { null in it }?.toTypedArray()
val parameterType = parameter.type val parameterType = parameter.type
if (parameterType.jvmErasure.java.isArray) { if (parameterType.jvmErasure.java.isArray) {
@@ -195,8 +192,12 @@ private class StringArgsConverter : ArgsConverter<String> {
return ArgsConverter.Result.Failure return ArgsConverter.Result.Failure
} }
override fun tryConvertTail(parameter: KParameter, firstArg: NamedArgument<String>, restArgsIt: Iterator<NamedArgument<String>>): ArgsConverter.Result = override fun tryConvertTail(
tryConvertVararg(parameter, firstArg, restArgsIt) parameter: KParameter,
firstArg: NamedArgument<String>,
restArgsIt: Iterator<NamedArgument<String>>
): ArgsConverter.Result =
tryConvertVararg(parameter, firstArg, restArgsIt)
} }
private class AnyArgsConverter : ArgsConverter<Any> { private class AnyArgsConverter : ArgsConverter<Any> {
@@ -205,28 +206,27 @@ private class AnyArgsConverter : ArgsConverter<Any> {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
fun convertPrimitivesArray(type: KType?, arg: Any?): Any? = fun convertPrimitivesArray(type: KType?, arg: Any?): Any? =
when (type?.classifier) { when (type?.classifier) {
IntArray::class -> (arg as? Array<Int>)?.toIntArray() IntArray::class -> (arg as? Array<Int>)?.toIntArray()
LongArray::class -> (arg as? Array<Long>)?.toLongArray() LongArray::class -> (arg as? Array<Long>)?.toLongArray()
ShortArray::class -> (arg as? Array<Short>)?.toShortArray() ShortArray::class -> (arg as? Array<Short>)?.toShortArray()
ByteArray::class -> (arg as? Array<Byte>)?.toByteArray() ByteArray::class -> (arg as? Array<Byte>)?.toByteArray()
CharArray::class -> (arg as? Array<Char>)?.toCharArray() CharArray::class -> (arg as? Array<Char>)?.toCharArray()
FloatArray::class -> (arg as? Array<Float>)?.toFloatArray() FloatArray::class -> (arg as? Array<Float>)?.toFloatArray()
DoubleArray::class -> (arg as? Array<Double>)?.toDoubleArray() DoubleArray::class -> (arg as? Array<Double>)?.toDoubleArray()
BooleanArray::class -> (arg as? Array<Boolean>)?.toBooleanArray() BooleanArray::class -> (arg as? Array<Boolean>)?.toBooleanArray()
else -> null else -> null
} }
if (value::class.isSubclassOf(parameter.type.jvmErasure)) return ArgsConverter.Result.Success(value) if (value::class.isSubclassOf(parameter.type.jvmErasure)) return ArgsConverter.Result.Success(value)
return convertPrimitivesArray(parameter.type, value)?.let { ArgsConverter.Result.Success(it) } return convertPrimitivesArray(parameter.type, value)?.let { ArgsConverter.Result.Success(it) }
?: ArgsConverter.Result.Failure ?: ArgsConverter.Result.Failure
} }
override fun tryConvertVararg( override fun tryConvertVararg(
parameter: KParameter, firstArg: NamedArgument<Any>, restArgsIt: Iterator<NamedArgument<Any>> parameter: KParameter, firstArg: NamedArgument<Any>, restArgsIt: Iterator<NamedArgument<Any>>
): ArgsConverter.Result { ): ArgsConverter.Result {
val parameterType = parameter.type val parameterType = parameter.type
if (parameterType.jvmErasure.java.isArray) { if (parameterType.jvmErasure.java.isArray) {
val argsSequence = sequenceOf(firstArg.value) + restArgsIt.asSequence().map { it.value } val argsSequence = sequenceOf(firstArg.value) + restArgsIt.asSequence().map { it.value }
@@ -239,8 +239,12 @@ private class AnyArgsConverter : ArgsConverter<Any> {
return ArgsConverter.Result.Failure return ArgsConverter.Result.Failure
} }
override fun tryConvertTail(parameter: KParameter, firstArg: NamedArgument<Any>, restArgsIt: Iterator<NamedArgument<Any>>): ArgsConverter.Result = override fun tryConvertTail(
tryConvertSingle(parameter, firstArg) parameter: KParameter,
firstArg: NamedArgument<Any>,
restArgsIt: Iterator<NamedArgument<Any>>
): ArgsConverter.Result =
tryConvertSingle(parameter, firstArg)
} }
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
-1
View File
@@ -7,7 +7,6 @@ dependencies {
api(kotlinStdlib()) api(kotlinStdlib())
api(project(":compiler:compiler.version")) api(project(":compiler:compiler.version"))
compileOnly(project(":kotlin-reflect-api"))
compileOnly(intellijCoreDep()) { includeJars("intellij-core") } compileOnly(intellijCoreDep()) { includeJars("intellij-core") }
compileOnly(intellijDep()) { includeIntellijCoreJarDependencies(project) } compileOnly(intellijDep()) { includeIntellijCoreJarDependencies(project) }
compileOnly(jpsStandalone()) { includeJars("jps-model") } compileOnly(jpsStandalone()) { includeJars("jps-model") }