Support JVM polymorphic signature calls to methods with void return type
#KT-32026 Fixed
This commit is contained in:
@@ -51,6 +51,7 @@ import org.jetbrains.kotlin.resolve.*
|
|||||||
import org.jetbrains.kotlin.resolve.BindingContextUtils.getDelegationConstructorCall
|
import org.jetbrains.kotlin.resolve.BindingContextUtils.getDelegationConstructorCall
|
||||||
import org.jetbrains.kotlin.resolve.BindingContextUtils.isBoxedLocalCapturedInClosure
|
import org.jetbrains.kotlin.resolve.BindingContextUtils.isBoxedLocalCapturedInClosure
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorUtils.*
|
import org.jetbrains.kotlin.resolve.DescriptorUtils.*
|
||||||
|
import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.DefaultValueArgument
|
import org.jetbrains.kotlin.resolve.calls.model.DefaultValueArgument
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.VarargValueArgument
|
import org.jetbrains.kotlin.resolve.calls.model.VarargValueArgument
|
||||||
@@ -725,27 +726,26 @@ class KotlinTypeMapper @JvmOverloads constructor(
|
|||||||
val builtIns = f.builtIns
|
val builtIns = f.builtIns
|
||||||
val arrayOfNullableAny = builtIns.getArrayType(Variance.INVARIANT, builtIns.nullableAnyType)
|
val arrayOfNullableAny = builtIns.getArrayType(Variance.INVARIANT, builtIns.nullableAnyType)
|
||||||
return mapSignatureWithCustomParameters(
|
return mapSignatureWithCustomParameters(
|
||||||
f, kind, sequenceOf(arrayOfNullableAny), null,
|
f, kind, sequenceOf(arrayOfNullableAny), null, null,
|
||||||
skipGenericSignature = false,
|
skipGenericSignature = false,
|
||||||
hasSpecialBridge = false
|
hasSpecialBridge = false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val parameterTypes: Sequence<KotlinType>
|
val parameterTypes: Sequence<KotlinType>
|
||||||
val returnType: KotlinType?
|
val (returnType, returnAsmType) =
|
||||||
|
if (languageVersionSettings.supportsFeature(LanguageFeature.PolymorphicSignature) && isPolymorphicSignature(f)) {
|
||||||
if (languageVersionSettings.supportsFeature(LanguageFeature.PolymorphicSignature) && isPolymorphicSignature(f)) {
|
if (resolvedCall == null) {
|
||||||
if (resolvedCall == null) {
|
throw UnsupportedOperationException("Cannot determine polymorphic signature without a resolved call: $f")
|
||||||
throw UnsupportedOperationException("Cannot determine polymorphic signature without a resolved call: $f")
|
}
|
||||||
|
parameterTypes = extractPolymorphicParameterTypes(resolvedCall)
|
||||||
|
extractPolymorphicReturnType(resolvedCall)
|
||||||
|
} else {
|
||||||
|
parameterTypes = f.valueParameters.asSequence().map { it.type }
|
||||||
|
null to null
|
||||||
}
|
}
|
||||||
parameterTypes = extractPolymorphicParameterTypes(resolvedCall)
|
|
||||||
returnType = extractPolymorphicReturnType(resolvedCall)
|
|
||||||
} else {
|
|
||||||
parameterTypes = f.valueParameters.asSequence().map { it.type }
|
|
||||||
returnType = null
|
|
||||||
}
|
|
||||||
|
|
||||||
return mapSignatureWithCustomParameters(f, kind, parameterTypes, returnType, skipGenericSignature, hasSpecialBridge)
|
return mapSignatureWithCustomParameters(f, kind, parameterTypes, returnType, returnAsmType, skipGenericSignature, hasSpecialBridge)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun mapSignatureWithCustomParameters(
|
fun mapSignatureWithCustomParameters(
|
||||||
@@ -753,15 +753,15 @@ class KotlinTypeMapper @JvmOverloads constructor(
|
|||||||
kind: OwnerKind,
|
kind: OwnerKind,
|
||||||
valueParameters: List<ValueParameterDescriptor>,
|
valueParameters: List<ValueParameterDescriptor>,
|
||||||
skipGenericSignature: Boolean
|
skipGenericSignature: Boolean
|
||||||
): JvmMethodGenericSignature {
|
): JvmMethodGenericSignature =
|
||||||
return mapSignatureWithCustomParameters(f, kind, valueParameters.asSequence().map { it.type }, null, skipGenericSignature, false)
|
mapSignatureWithCustomParameters(f, kind, valueParameters.asSequence().map { it.type }, null, null, skipGenericSignature, false)
|
||||||
}
|
|
||||||
|
|
||||||
private fun mapSignatureWithCustomParameters(
|
private fun mapSignatureWithCustomParameters(
|
||||||
f: FunctionDescriptor,
|
f: FunctionDescriptor,
|
||||||
kind: OwnerKind,
|
kind: OwnerKind,
|
||||||
valueParameterTypes: Sequence<KotlinType>,
|
valueParameterTypes: Sequence<KotlinType>,
|
||||||
customReturnType: KotlinType?,
|
customReturnType: KotlinType?,
|
||||||
|
customReturnAsmType: Type?,
|
||||||
skipGenericSignature: Boolean,
|
skipGenericSignature: Boolean,
|
||||||
hasSpecialBridge: Boolean
|
hasSpecialBridge: Boolean
|
||||||
): JvmMethodGenericSignature {
|
): JvmMethodGenericSignature {
|
||||||
@@ -827,7 +827,11 @@ class KotlinTypeMapper @JvmOverloads constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
sw.writeReturnType()
|
sw.writeReturnType()
|
||||||
customReturnType?.let { mapReturnType(f, sw, it) } ?: mapReturnType(f, sw)
|
when {
|
||||||
|
customReturnAsmType != null -> sw.writeAsmType(customReturnAsmType)
|
||||||
|
customReturnType != null -> mapReturnType(f, sw, customReturnType)
|
||||||
|
else -> mapReturnType(f, sw)
|
||||||
|
}
|
||||||
sw.writeReturnTypeEnd()
|
sw.writeReturnTypeEnd()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -859,10 +863,10 @@ class KotlinTypeMapper @JvmOverloads constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun extractPolymorphicReturnType(resolvedCall: ResolvedCall<*>): KotlinType? {
|
private fun extractPolymorphicReturnType(resolvedCall: ResolvedCall<*>): Pair<KotlinType?, Type?> {
|
||||||
// Return type is polymorphic only in case it's Object; see VarHandle.compareAndSet and similar.
|
// Return type is polymorphic only in case it's Object; see VarHandle.compareAndSet and similar.
|
||||||
val originalReturnType = resolvedCall.resultingDescriptor.returnType
|
val originalReturnType = resolvedCall.resultingDescriptor.returnType
|
||||||
if (originalReturnType != null && !KotlinBuiltIns.isAny(originalReturnType)) return null
|
if (originalReturnType != null && !KotlinBuiltIns.isAny(originalReturnType)) return null to null
|
||||||
|
|
||||||
var expression = resolvedCall.call.callElement as? KtExpression ?: throw UnsupportedOperationException(
|
var expression = resolvedCall.call.callElement as? KtExpression ?: throw UnsupportedOperationException(
|
||||||
"Polymorphic signature method call must be an expression: " + resolvedCall.resultingDescriptor
|
"Polymorphic signature method call must be an expression: " + resolvedCall.resultingDescriptor
|
||||||
@@ -873,10 +877,16 @@ class KotlinTypeMapper @JvmOverloads constructor(
|
|||||||
}
|
}
|
||||||
expression = expression.getOutermostParenthesizerOrThis()
|
expression = expression.getOutermostParenthesizerOrThis()
|
||||||
|
|
||||||
return if (expression.parent is KtBinaryExpressionWithTypeRHS)
|
// See https://docs.oracle.com/javase/11/docs/api/java/lang/invoke/MethodHandle.html
|
||||||
bindingContext.getType(expression.parent as KtExpression)
|
return when {
|
||||||
else
|
// `invokeExact(...) as X` is generated to `invokevirtual (...)LX;`.
|
||||||
null
|
expression.parent is KtBinaryExpressionWithTypeRHS ->
|
||||||
|
bindingContext.getType(expression.parent as KtExpression) to null
|
||||||
|
// `invokeExact(...)` without a cast is generated to `invokevirtual (...)V` in a statement context,
|
||||||
|
// and to `invokevirtual (...)Ljava/lang/Object;` in an expression context.
|
||||||
|
expression.isUsedAsExpression(bindingContext) -> null to OBJECT_TYPE
|
||||||
|
else -> null to Type.VOID_TYPE
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkOwnerCompatibility(descriptor: FunctionDescriptor) {
|
private fun checkOwnerCompatibility(descriptor: FunctionDescriptor) {
|
||||||
|
|||||||
@@ -47,5 +47,16 @@ fun box(): String {
|
|||||||
val r8 = mh.invoke(arrayOf(args))
|
val r8 = mh.invoke(arrayOf(args))
|
||||||
if (r8 !is Array<*> || !r8.contentEquals(args)) return "Fail 8: $r8"
|
if (r8 !is Array<*> || !r8.contentEquals(args)) return "Fail 8: $r8"
|
||||||
|
|
||||||
|
// The next two calls check behavior in a statement context (where the call result is not used)
|
||||||
|
|
||||||
|
try {
|
||||||
|
mh.invokeExact(args)
|
||||||
|
return "Fail 9"
|
||||||
|
} catch (e: WrongMethodTypeException) {
|
||||||
|
// OK
|
||||||
|
}
|
||||||
|
|
||||||
|
mh.invoke(args)
|
||||||
|
|
||||||
return "OK"
|
return "OK"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// !LANGUAGE: -PolymorphicSignature
|
||||||
// IGNORE_BACKEND_FIR: JVM_IR
|
// IGNORE_BACKEND_FIR: JVM_IR
|
||||||
// TARGET_BACKEND: JVM
|
// TARGET_BACKEND: JVM
|
||||||
// FULL_JDK
|
// FULL_JDK
|
||||||
@@ -44,5 +45,11 @@ fun box(): String {
|
|||||||
val r8 = mh.invoke(arrayOf(args))
|
val r8 = mh.invoke(arrayOf(args))
|
||||||
if (r8 !is Array<*> || r8[0] !is Array<*> || !(r8[0] as Array<*>).contentEquals(args)) return "Fail 8: $r8"
|
if (r8 !is Array<*> || r8[0] !is Array<*> || !(r8[0] as Array<*>).contentEquals(args)) return "Fail 8: $r8"
|
||||||
|
|
||||||
|
// The next two calls check behavior in a statement context (where the call result is not used)
|
||||||
|
|
||||||
|
mh.invokeExact(args)
|
||||||
|
|
||||||
|
mh.invoke(args)
|
||||||
|
|
||||||
return "OK"
|
return "OK"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
// !LANGUAGE: +PolymorphicSignature
|
||||||
|
// TARGET_BACKEND: JVM
|
||||||
|
// IGNORE_BACKEND_FIR: JVM_IR
|
||||||
|
// FULL_JDK
|
||||||
|
// SKIP_JDK6
|
||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
import java.lang.invoke.MethodHandles
|
||||||
|
import java.lang.invoke.MethodType
|
||||||
|
|
||||||
|
var state = "Fail"
|
||||||
|
|
||||||
|
class C {
|
||||||
|
fun foo(s: String) {
|
||||||
|
state = s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
val mh = MethodHandles.lookup().findVirtual(
|
||||||
|
C::class.java, "foo",
|
||||||
|
MethodType.methodType(Void.TYPE, String::class.java)
|
||||||
|
)
|
||||||
|
|
||||||
|
mh.invokeExact(C(), "OK")
|
||||||
|
|
||||||
|
return state
|
||||||
|
}
|
||||||
+5
@@ -18419,6 +18419,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
|||||||
public void testVarargOfObjects_before() throws Exception {
|
public void testVarargOfObjects_before() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/polymorphicSignature/varargOfObjects_before.kt");
|
runTest("compiler/testData/codegen/box/polymorphicSignature/varargOfObjects_before.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("voidReturnType.kt")
|
||||||
|
public void testVoidReturnType() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/polymorphicSignature/voidReturnType.kt");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("compiler/testData/codegen/box/primitiveTypes")
|
@TestMetadata("compiler/testData/codegen/box/primitiveTypes")
|
||||||
|
|||||||
+5
@@ -18419,6 +18419,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
|||||||
public void testVarargOfObjects_before() throws Exception {
|
public void testVarargOfObjects_before() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/polymorphicSignature/varargOfObjects_before.kt");
|
runTest("compiler/testData/codegen/box/polymorphicSignature/varargOfObjects_before.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("voidReturnType.kt")
|
||||||
|
public void testVoidReturnType() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/polymorphicSignature/voidReturnType.kt");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("compiler/testData/codegen/box/primitiveTypes")
|
@TestMetadata("compiler/testData/codegen/box/primitiveTypes")
|
||||||
|
|||||||
+5
@@ -16903,6 +16903,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
|||||||
public void testVarargOfObjects_before() throws Exception {
|
public void testVarargOfObjects_before() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/polymorphicSignature/varargOfObjects_before.kt");
|
runTest("compiler/testData/codegen/box/polymorphicSignature/varargOfObjects_before.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("voidReturnType.kt")
|
||||||
|
public void testVoidReturnType() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/polymorphicSignature/voidReturnType.kt");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("compiler/testData/codegen/box/primitiveTypes")
|
@TestMetadata("compiler/testData/codegen/box/primitiveTypes")
|
||||||
|
|||||||
Reference in New Issue
Block a user