Reflect: Handle methods with inline class default parameters

This commit is contained in:
Steven Schäfer
2020-04-23 15:37:48 +02:00
committed by Alexander Udalov
parent 86d6470ced
commit 905a8ca0d0
10 changed files with 75 additions and 20 deletions
@@ -23660,6 +23660,11 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
runTest("compiler/testData/codegen/box/reflection/callBy/extensionFunction.kt");
}
@TestMetadata("inlineClassDefaultArguments.kt")
public void testInlineClassDefaultArguments() throws Exception {
runTest("compiler/testData/codegen/box/reflection/callBy/inlineClassDefaultArguments.kt");
}
@TestMetadata("inlineClassFunctionsAndConstructors.kt")
public void testInlineClassFunctionsAndConstructors() throws Exception {
runTest("compiler/testData/codegen/box/reflection/callBy/inlineClassFunctionsAndConstructors.kt");
@@ -0,0 +1,10 @@
// IGNORE_BACKEND: JS_IR, JS, NATIVE
// WITH_REFLECT
inline class A(val x: Int)
fun test(x: A = A(0)) = "OK"
fun box(): String {
return (::test).callBy(mapOf())
}
@@ -25246,6 +25246,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/reflection/callBy/extensionFunction.kt");
}
@TestMetadata("inlineClassDefaultArguments.kt")
public void testInlineClassDefaultArguments() throws Exception {
runTest("compiler/testData/codegen/box/reflection/callBy/inlineClassDefaultArguments.kt");
}
@TestMetadata("inlineClassFunctionsAndConstructors.kt")
public void testInlineClassFunctionsAndConstructors() throws Exception {
runTest("compiler/testData/codegen/box/reflection/callBy/inlineClassFunctionsAndConstructors.kt");
@@ -24063,6 +24063,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/reflection/callBy/extensionFunction.kt");
}
@TestMetadata("inlineClassDefaultArguments.kt")
public void testInlineClassDefaultArguments() throws Exception {
runTest("compiler/testData/codegen/box/reflection/callBy/inlineClassDefaultArguments.kt");
}
@TestMetadata("inlineClassFunctionsAndConstructors.kt")
public void testInlineClassFunctionsAndConstructors() throws Exception {
runTest("compiler/testData/codegen/box/reflection/callBy/inlineClassFunctionsAndConstructors.kt");
@@ -23660,6 +23660,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/reflection/callBy/extensionFunction.kt");
}
@TestMetadata("inlineClassDefaultArguments.kt")
public void testInlineClassDefaultArguments() throws Exception {
runTest("compiler/testData/codegen/box/reflection/callBy/inlineClassDefaultArguments.kt");
}
@TestMetadata("inlineClassFunctionsAndConstructors.kt")
public void testInlineClassFunctionsAndConstructors() throws Exception {
runTest("compiler/testData/codegen/box/reflection/callBy/inlineClassFunctionsAndConstructors.kt");
@@ -132,7 +132,9 @@ internal abstract class KCallableImpl<out R> : KCallable<R> {
arguments.add(args[parameter])
}
parameter.isOptional -> {
arguments.add(defaultPrimitiveValue(parameter.type.javaType))
// For inline class types, the javaType refers to the underlying type of the inline class,
// but we have to pass null in order to mark the argument as absent for InlineClassAwareCaller.
arguments.add(if (parameter.type.isInlineClassType) null else defaultPrimitiveValue(parameter.type.javaType))
mask = mask or (1 shl (index % Integer.SIZE))
anyOptional = true
}
@@ -192,22 +194,6 @@ internal abstract class KCallableImpl<out R> : KCallable<R> {
}
}
private fun defaultPrimitiveValue(type: Type): Any? =
if (type is Class<*> && type.isPrimitive) {
when (type) {
Boolean::class.java -> false
Char::class.java -> 0.toChar()
Byte::class.java -> 0.toByte()
Short::class.java -> 0.toShort()
Int::class.java -> 0
Float::class.java -> 0f
Long::class.java -> 0L
Double::class.java -> 0.0
Void.TYPE -> throw IllegalStateException("Parameter with void type is illegal")
else -> throw UnsupportedOperationException("Unknown primitive: $type")
}
} else null
private fun defaultEmptyArray(type: KType): Any =
type.jvmErasure.java.run {
if (isArray) ReflectArray.newInstance(componentType, 0)
@@ -17,6 +17,7 @@ import java.lang.reflect.Member
import java.lang.reflect.Method
import java.lang.reflect.Type
import kotlin.reflect.jvm.internal.KotlinReflectionInternalError
import kotlin.reflect.jvm.internal.defaultPrimitiveValue
import kotlin.reflect.jvm.internal.toJavaClass
/**
@@ -127,9 +128,15 @@ internal class InlineClassAwareCaller<out M : Member?>(
val method = unbox[index]
val arg = args[index]
// Note that arg may be null in case we're calling a $default method and it's an optional parameter of an inline class type
unboxed[index] =
if (method != null && arg != null) method.invoke(arg)
else arg
unboxed[index] = if (method != null) {
if (arg != null) {
method.invoke(arg)
} else {
defaultPrimitiveValue(method.returnType)
}
} else {
arg
}
}
val result = caller.call(unboxed)
@@ -46,6 +46,9 @@ import org.jetbrains.kotlin.descriptors.runtime.components.tryLoadClass
import org.jetbrains.kotlin.descriptors.runtime.structure.ReflectJavaAnnotation
import org.jetbrains.kotlin.descriptors.runtime.structure.ReflectJavaClass
import org.jetbrains.kotlin.descriptors.runtime.structure.safeClassLoader
import org.jetbrains.kotlin.resolve.isInlineClassType
import java.lang.reflect.Type
import kotlin.reflect.KType
internal val JVM_STATIC = FqName("kotlin.jvm.JvmStatic")
@@ -195,3 +198,22 @@ internal fun <M : MessageLite, D : CallableDescriptor> deserializeToDescriptor(
)
return MemberDeserializer(context).createDescriptor(proto)
}
internal val KType.isInlineClassType: Boolean
get() = (this as? KTypeImpl)?.type?.isInlineClassType() == true
internal fun defaultPrimitiveValue(type: Type): Any? =
if (type is Class<*> && type.isPrimitive) {
when (type) {
Boolean::class.java -> false
Char::class.java -> 0.toChar()
Byte::class.java -> 0.toByte()
Short::class.java -> 0.toShort()
Int::class.java -> 0
Float::class.java -> 0f
Long::class.java -> 0L
Double::class.java -> 0.0
Void.TYPE -> throw IllegalStateException("Parameter with void type is illegal")
else -> throw UnsupportedOperationException("Unknown primitive: $type")
}
} else null
@@ -19911,6 +19911,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/reflection/callBy/extensionFunction.kt");
}
@TestMetadata("inlineClassDefaultArguments.kt")
public void testInlineClassDefaultArguments() throws Exception {
runTest("compiler/testData/codegen/box/reflection/callBy/inlineClassDefaultArguments.kt");
}
@TestMetadata("inlineClassFunctionsAndConstructors.kt")
public void testInlineClassFunctionsAndConstructors() throws Exception {
runTest("compiler/testData/codegen/box/reflection/callBy/inlineClassFunctionsAndConstructors.kt");
@@ -19971,6 +19971,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/reflection/callBy/extensionFunction.kt");
}
@TestMetadata("inlineClassDefaultArguments.kt")
public void testInlineClassDefaultArguments() throws Exception {
runTest("compiler/testData/codegen/box/reflection/callBy/inlineClassDefaultArguments.kt");
}
@TestMetadata("inlineClassFunctionsAndConstructors.kt")
public void testInlineClassFunctionsAndConstructors() throws Exception {
runTest("compiler/testData/codegen/box/reflection/callBy/inlineClassFunctionsAndConstructors.kt");