diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/CallBasedArgumentGenerator.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/CallBasedArgumentGenerator.kt index 83fd3549832..ce3d1059b03 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/CallBasedArgumentGenerator.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/CallBasedArgumentGenerator.kt @@ -5,6 +5,8 @@ package org.jetbrains.kotlin.codegen +import org.jetbrains.kotlin.config.LanguageFeature +import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor import org.jetbrains.kotlin.resolve.calls.model.DefaultValueArgument import org.jetbrains.kotlin.resolve.calls.model.ExpressionValueArgument @@ -22,8 +24,14 @@ class CallBasedArgumentGenerator( private val isVarargInvoke: Boolean = JvmCodegenUtil.isDeclarationOfBigArityFunctionInvoke(valueParameters.firstOrNull()?.containingDeclaration) + private val isPolymorphicSignature: Boolean = + codegen.state.languageVersionSettings.supportsFeature(LanguageFeature.PolymorphicSignature) && + (valueParameters.firstOrNull()?.containingDeclaration as? FunctionDescriptor)?.let { function -> + JvmCodegenUtil.isPolymorphicSignature(function) + } == true + init { - if (!isVarargInvoke) { + if (!isVarargInvoke && !isPolymorphicSignature) { assert(valueParameters.size == valueParameterTypes.size) { "Value parameters and their types mismatch in sizes: ${valueParameters.size} != ${valueParameterTypes.size}" } @@ -49,6 +57,15 @@ class CallBasedArgumentGenerator( } override fun generateVararg(i: Int, argument: VarargValueArgument) { + if (isPolymorphicSignature) { + for ((index, arg) in argument.arguments.withIndex()) { + val expression = arg.getArgumentExpression()!! + val type = JvmKotlinType(valueParameterTypes[index], codegen.kotlinType(expression)) + callGenerator.genValueAndPut(null, expression, type, index) + } + return + } + // Upper bound for type of vararg parameter should always have a form of 'Array', // while its lower bound may be Nothing-typed after approximation val lazyVararg = codegen.genVarargs(argument, valueParameters[i].type.upperIfFlexible()) diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index 5f5469c2868..6ae77d5df39 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -2401,12 +2401,7 @@ public class ExpressionCodegen extends KtVisitor impleme return intrinsic.toCallable(fd, superCall, resolvedCall, this); } - return resolveToCallableMethod(fd, superCall); - } - - @NotNull - private CallableMethod resolveToCallableMethod(@NotNull FunctionDescriptor fd, boolean superCall) { - return typeMapper.mapToCallableMethod(SamCodegenUtil.resolveSamAdapter(fd), superCall); + return typeMapper.mapToCallableMethod(SamCodegenUtil.resolveSamAdapter(fd), resolvedCall, superCall, null); } public void invokeMethodWithArguments( @@ -4381,7 +4376,7 @@ public class ExpressionCodegen extends KtVisitor impleme ResolvedCall resolvedCall = isGetter ? resolvedGetCall : resolvedSetCall; assert resolvedCall != null : "No resolved call for " + operationDescriptor; Callable callable = resolveToCallable(accessibleFunctionDescriptor(resolvedCall), false, resolvedCall); - Callable callableMethod = resolveToCallableMethod(operationDescriptor, false); + Callable callableMethod = typeMapper.mapToCallableMethod(SamCodegenUtil.resolveSamAdapter(operationDescriptor), false); Type[] argumentTypes = callableMethod.getParameterTypes(); StackValue.CollectionElementReceiver collectionElementReceiver = createCollectionElementReceiver( diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmCodegenUtil.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmCodegenUtil.java index d8e05e08698..b4af23286cb 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmCodegenUtil.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmCodegenUtil.java @@ -28,6 +28,7 @@ import org.jetbrains.kotlin.load.java.descriptors.JavaCallableMemberDescriptor; import org.jetbrains.kotlin.load.java.descriptors.JavaPropertyDescriptor; import org.jetbrains.kotlin.load.kotlin.ModuleVisibilityUtilsKt; import org.jetbrains.kotlin.metadata.jvm.deserialization.ModuleMapping; +import org.jetbrains.kotlin.name.FqName; import org.jetbrains.kotlin.name.Name; import org.jetbrains.kotlin.psi.Call; import org.jetbrains.kotlin.psi.KtFile; @@ -384,4 +385,8 @@ public class JvmCodegenUtil { : "ClassDescriptor should not be null:" + specifier.getText(); return superClass; } + + public static boolean isPolymorphicSignature(@NotNull FunctionDescriptor descriptor) { + return descriptor.getAnnotations().hasAnnotation(new FqName("java.lang.invoke.MethodHandle.PolymorphicSignature")); + } } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.java index 666100be7b9..222dcc06ff8 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.java @@ -46,14 +46,13 @@ import org.jetbrains.kotlin.load.kotlin.*; import org.jetbrains.kotlin.load.kotlin.incremental.IncrementalPackageFragmentProvider.IncrementalMultifileClassPackageFragment; import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmBytecodeBinaryVersion; import org.jetbrains.kotlin.name.*; -import org.jetbrains.kotlin.psi.KtExpression; -import org.jetbrains.kotlin.psi.KtFile; -import org.jetbrains.kotlin.psi.KtFunctionLiteral; -import org.jetbrains.kotlin.psi.KtLambdaExpression; +import org.jetbrains.kotlin.psi.*; +import org.jetbrains.kotlin.psi.psiUtil.KtPsiUtilKt; import org.jetbrains.kotlin.resolve.*; import org.jetbrains.kotlin.resolve.calls.model.DefaultValueArgument; import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall; import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument; +import org.jetbrains.kotlin.resolve.calls.model.VarargValueArgument; import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt; import org.jetbrains.kotlin.resolve.jvm.JvmClassName; import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodGenericSignature; @@ -791,7 +790,12 @@ public class KotlinTypeMapper { } @NotNull - public CallableMethod mapToCallableMethod(@NotNull FunctionDescriptor descriptor, boolean superCall, @Nullable OwnerKind kind) { + public CallableMethod mapToCallableMethod( + @NotNull FunctionDescriptor descriptor, + @Nullable ResolvedCall resolvedCall, + boolean superCall, + @Nullable OwnerKind kind + ) { // we generate constructors of inline classes as usual functions if (descriptor instanceof ConstructorDescriptor && kind != OwnerKind.ERASED_INLINE_CLASS) { JvmMethodSignature method = mapSignatureSkipGeneric(descriptor.getOriginal()); @@ -904,7 +908,7 @@ public class KotlinTypeMapper { signature = toInlinedErasedClass ? mapSignatureForInlineErasedClassSkipGeneric(functionToCall) - : mapSignatureSkipGeneric(functionToCall); + : mapSignature(functionToCall, resolvedCall, OwnerKind.IMPLEMENTATION, true, false); returnKotlinType = functionToCall.getReturnType(); ClassDescriptor receiver = (currentIsInterface && !originalIsInterface) || currentOwner instanceof FunctionClassDescriptor @@ -965,6 +969,11 @@ public class KotlinTypeMapper { ); } + @NotNull + public CallableMethod mapToCallableMethod(@NotNull FunctionDescriptor descriptor, boolean superCall, @Nullable OwnerKind kind) { + return mapToCallableMethod(descriptor, null, superCall, kind); + } + @NotNull public CallableMethod mapToCallableMethod(@NotNull FunctionDescriptor descriptor, boolean superCall) { return mapToCallableMethod(descriptor, superCall, null); @@ -1215,17 +1224,20 @@ public class KotlinTypeMapper { } @NotNull - public JvmMethodGenericSignature mapSignatureWithGeneric(@NotNull FunctionDescriptor f, @NotNull OwnerKind kind, boolean hasSpecialBridge) { - return mapSignature(f, kind, false, hasSpecialBridge); + public JvmMethodGenericSignature mapSignatureWithGeneric( + @NotNull FunctionDescriptor f, @NotNull OwnerKind kind, boolean hasSpecialBridge + ) { + return mapSignature(f, null, kind, false, hasSpecialBridge); } private JvmMethodGenericSignature mapSignature(@NotNull FunctionDescriptor f, @NotNull OwnerKind kind, boolean skipGenericSignature) { - return mapSignature(f, kind, skipGenericSignature, false); + return mapSignature(f, null, kind, skipGenericSignature, false); } @NotNull private JvmMethodGenericSignature mapSignature( @NotNull FunctionDescriptor f, + @Nullable ResolvedCall resolvedCall, @NotNull OwnerKind kind, boolean skipGenericSignature, boolean hasSpecialBridge @@ -1254,13 +1266,25 @@ public class KotlinTypeMapper { if (isDeclarationOfBigArityFunctionInvoke(f) || isDeclarationOfBigArityCreateCoroutineMethod(f)) { KotlinBuiltIns builtIns = DescriptorUtilsKt.getBuiltIns(f); KotlinType arrayOfNullableAny = builtIns.getArrayType(Variance.INVARIANT, builtIns.getNullableAnyType()); - return mapSignatureWithCustomParameters(f, kind, Stream.of(arrayOfNullableAny), false, false); + return mapSignatureWithCustomParameters(f, kind, Stream.of(arrayOfNullableAny), null, false, false); } - return mapSignatureWithCustomParameters( - f, kind, f.getValueParameters().stream().map(ValueParameterDescriptor::getType), - skipGenericSignature, hasSpecialBridge - ); + Stream parameterTypes; + KotlinType returnType; + + if (languageVersionSettings.supportsFeature(LanguageFeature.PolymorphicSignature) && JvmCodegenUtil.isPolymorphicSignature(f)) { + if (resolvedCall == null) { + throw new UnsupportedOperationException("Cannot determine polymorphic signature without a resolved call: " + f); + } + parameterTypes = extractPolymorphicParameterTypes(resolvedCall); + returnType = extractPolymorphicReturnType(resolvedCall); + } + else { + parameterTypes = f.getValueParameters().stream().map(ValueParameterDescriptor::getType); + returnType = null; + } + + return mapSignatureWithCustomParameters(f, kind, parameterTypes, returnType, skipGenericSignature, hasSpecialBridge); } @NotNull @@ -1271,8 +1295,7 @@ public class KotlinTypeMapper { boolean skipGenericSignature ) { return mapSignatureWithCustomParameters( - f, kind, valueParameters.stream().map(ValueParameterDescriptor::getType), - skipGenericSignature, false + f, kind, valueParameters.stream().map(ValueParameterDescriptor::getType), null, skipGenericSignature, false ); } @@ -1281,6 +1304,7 @@ public class KotlinTypeMapper { @NotNull FunctionDescriptor f, @NotNull OwnerKind kind, @NotNull Stream valueParameterTypes, + @Nullable KotlinType customReturnType, boolean skipGenericSignature, boolean hasSpecialBridge ) { @@ -1344,7 +1368,12 @@ public class KotlinTypeMapper { }); sw.writeReturnType(); - mapReturnType(f, sw); + if (customReturnType != null) { + mapReturnType(f, sw, customReturnType); + } + else { + mapReturnType(f, sw); + } sw.writeReturnTypeEnd(); } @@ -1363,6 +1392,54 @@ public class KotlinTypeMapper { return signature; } + @NotNull + private Stream extractPolymorphicParameterTypes(@NotNull ResolvedCall resolvedCall) { + List valueArgumentsByIndex = resolvedCall.getValueArgumentsByIndex(); + if (valueArgumentsByIndex == null || + valueArgumentsByIndex.size() != 1 || + !(valueArgumentsByIndex.get(0) instanceof VarargValueArgument)) { + throw new UnsupportedOperationException( + "Polymorphic signature is only supported for methods with one vararg argument: " + resolvedCall.getResultingDescriptor() + ); + } + + VarargValueArgument argument = (VarargValueArgument) valueArgumentsByIndex.get(0); + return argument.getArguments().stream().map(arg -> { + KtExpression expression = arg.getArgumentExpression(); + if (expression == null) { + throw new UnsupportedOperationException( + "Polymorphic signature argument must be an expression: " + resolvedCall.getResultingDescriptor() + ); + } + return bindingContext.getType(expression); + }); + } + + @Nullable + private KotlinType extractPolymorphicReturnType(@NotNull ResolvedCall resolvedCall) { + // Return type is polymorphic only in case it's Object; see VarHandle.compareAndSet and similar. + KotlinType originalReturnType = resolvedCall.getResultingDescriptor().getReturnType(); + if (originalReturnType != null && !KotlinBuiltIns.isAny(originalReturnType)) return null; + + if (!(resolvedCall.getCall().getCallElement() instanceof KtExpression)) { + throw new UnsupportedOperationException( + "Polymorphic signature method call must be an expression: " + resolvedCall.getResultingDescriptor() + ); + } + KtExpression expression = (KtExpression) resolvedCall.getCall().getCallElement(); + + while (true) { + KtQualifiedExpression parent = KtPsiUtilKt.getQualifiedExpressionForSelector(expression); + if (parent == null) break; + expression = parent; + } + expression = KtPsiUtilKt.getOutermostParenthesizerOrThis(expression); + + return expression.getParent() instanceof KtBinaryExpressionWithTypeRHS + ? bindingContext.getType((KtExpression) expression.getParent()) + : null; + } + private void checkOwnerCompatibility(@NotNull FunctionDescriptor descriptor) { KotlinJvmBinaryClass ownerClass = KotlinJvmBinaryClassUtilKt.getContainingKotlinJvmBinaryClass(descriptor); if (ownerClass == null) return; diff --git a/compiler/testData/codegen/box/polymorphicSignature/anonymousSubclass.kt b/compiler/testData/codegen/box/polymorphicSignature/anonymousSubclass.kt new file mode 100644 index 00000000000..859cc7e1e16 --- /dev/null +++ b/compiler/testData/codegen/box/polymorphicSignature/anonymousSubclass.kt @@ -0,0 +1,37 @@ +// !LANGUAGE: +PolymorphicSignature +// TARGET_BACKEND: JVM +// IGNORE_BACKEND: JVM_IR +// FULL_JDK +// SKIP_JDK6 +// WITH_RUNTIME + +import java.lang.invoke.MethodHandles +import java.lang.invoke.MethodType +import java.lang.invoke.WrongMethodTypeException + +interface I { + fun get(): String +} + +class C { + fun run(i: I): String = i.get() +} + +fun box(): String { + val mh = MethodHandles.lookup().findVirtual( + C::class.java, "run", + MethodType.methodType(String::class.java, I::class.java) + ) + + try { + return mh.invokeExact(C(), object : I { + override fun get(): String = "Fail" + }) as String + } catch (e: WrongMethodTypeException) { + // OK + } + + return mh.invoke(C(), object : I { + override fun get(): String = "OK" + }) as String +} diff --git a/compiler/testData/codegen/box/polymorphicSignature/invoke.kt b/compiler/testData/codegen/box/polymorphicSignature/invoke.kt new file mode 100644 index 00000000000..05a835cb3f6 --- /dev/null +++ b/compiler/testData/codegen/box/polymorphicSignature/invoke.kt @@ -0,0 +1,47 @@ +// !LANGUAGE: +PolymorphicSignature +// TARGET_BACKEND: JVM +// IGNORE_BACKEND: JVM_IR +// FULL_JDK +// SKIP_JDK6 +// WITH_RUNTIME + +import java.lang.invoke.MethodHandles +import java.lang.invoke.MethodType + +open class Base +class Derived : Base() { + override fun toString() = "!" +} + +class C { + fun foo(s: String, d: Double?, x: Base): String = "$s$d$x" +} + +@Target(AnnotationTarget.EXPRESSION) +@Retention(AnnotationRetention.SOURCE) +annotation class IrrelevantAnnotation + +fun box(): String { + val mh = MethodHandles.lookup().findVirtual( + C::class.java, "foo", + MethodType.methodType(String::class.java, String::class.java, Double::class.javaObjectType, Base::class.java) + ) + + val result1: String = mh.invoke(C(), "Hello", 0.01, Derived()) as String + if (result1 != "Hello0.01!") return "Fail 1: $result1" + + // Check parenthesized/annotated expressions + invoke via "()" + val result2 = (@IrrelevantAnnotation() ((mh(C(), (("Hello")), 0.01, Derived())))) as String + if (result1 != result2) return "Fail 2: $result1 != $result2" + + // Check deep qualified expressions + val o = object { + val p = object { + val handle = mh + } + } + val result3 = (o.p).handle.invoke(C(), "Hello", 0.01, Derived()) as String + if (result1 != result3) return "Fail 3: $result1 != $result3" + + return "OK" +} diff --git a/compiler/testData/codegen/box/polymorphicSignature/invokeExact.kt b/compiler/testData/codegen/box/polymorphicSignature/invokeExact.kt new file mode 100644 index 00000000000..70297f48c37 --- /dev/null +++ b/compiler/testData/codegen/box/polymorphicSignature/invokeExact.kt @@ -0,0 +1,22 @@ +// !LANGUAGE: +PolymorphicSignature +// TARGET_BACKEND: JVM +// IGNORE_BACKEND: JVM_IR +// FULL_JDK +// SKIP_JDK6 +// WITH_RUNTIME + +import java.lang.invoke.MethodHandles +import java.lang.invoke.MethodType + +class C { + fun foo(s: String, d: Double, x: Int): String = "$s$d$x" +} + +fun box(): String { + val mh = MethodHandles.lookup().findVirtual( + C::class.java, "foo", + MethodType.methodType(String::class.java, String::class.java, Double::class.java, Int::class.java) + ) + val result: String = mh.invokeExact(C(), "Hello", 0.01, 42) as String + return if (result == "Hello0.0142") "OK" else "Fail: $result" +} diff --git a/compiler/testData/codegen/box/polymorphicSignature/invokeExactWithInlineClass.kt b/compiler/testData/codegen/box/polymorphicSignature/invokeExactWithInlineClass.kt new file mode 100644 index 00000000000..77b806bb9a3 --- /dev/null +++ b/compiler/testData/codegen/box/polymorphicSignature/invokeExactWithInlineClass.kt @@ -0,0 +1,23 @@ +// !LANGUAGE: +PolymorphicSignature +// TARGET_BACKEND: JVM +// IGNORE_BACKEND: JVM_IR +// FULL_JDK +// SKIP_JDK6 +// WITH_REFLECT + +import java.lang.invoke.MethodHandles +import kotlin.reflect.jvm.javaMethod + +inline class Z(val s: String) + +fun foo(z: Z): String = z.s + +fun box(): String { + val mh = MethodHandles.lookup().unreflect(::foo.javaMethod!!) + + // TODO: it's unclear whether this should throw or not, see KT-28214. + val r1 = mh.invokeExact(Z("OK")) as String + if (r1 != "OK") return "Fail r1: $r1" + + return mh.invokeExact("OK") as String +} diff --git a/compiler/testData/codegen/box/polymorphicSignature/varargOfObjects_after.kt b/compiler/testData/codegen/box/polymorphicSignature/varargOfObjects_after.kt new file mode 100644 index 00000000000..1a40de0c3c2 --- /dev/null +++ b/compiler/testData/codegen/box/polymorphicSignature/varargOfObjects_after.kt @@ -0,0 +1,51 @@ +// !LANGUAGE: +PolymorphicSignature +// TARGET_BACKEND: JVM +// IGNORE_BACKEND: JVM_IR +// FULL_JDK +// SKIP_JDK6 +// WITH_RUNTIME + +import java.lang.invoke.MethodHandles +import java.lang.invoke.MethodType +import java.lang.invoke.WrongMethodTypeException + +fun foo(vararg args: Any?): Any? = args[0] + +fun box(): String { + val mh = MethodHandles.lookup().findStatic( + object {}::class.java.enclosingClass, "foo", + MethodType.methodType(Any::class.java, Array::class.java) + ) + + val args = arrayOf("aaa", 1) + + val r1 = mh.invokeExact(args) + if (r1 != "aaa") return "Fail 1: $r1" + + val r2 = mh.invokeExact(*args) + if (r2 != "aaa") return "Fail 2: $r2" + + val r3 = mh.invokeExact(arrayOf(args) as Array<*>) + if (r3 !is Array<*> || !r3.contentEquals(args)) return "Fail 3: $r3" + + try { + mh.invokeExact(arrayOf(args)) + return "Fail 4" + } catch (e: WrongMethodTypeException) { + // OK + } + + val r5 = mh.invoke(args) + if (r5 != "aaa") return "Fail 5: $r5" + + val r6 = mh.invoke(*args) + if (r6 != "aaa") return "Fail 6: $r6" + + val r7 = mh.invoke(arrayOf(args) as Array<*>) + if (r7 !is Array<*> || !r7.contentEquals(args)) return "Fail 7: $r7" + + val r8 = mh.invoke(arrayOf(args)) + if (r8 !is Array<*> || !r8.contentEquals(args)) return "Fail 8: $r8" + + return "OK" +} diff --git a/compiler/testData/codegen/box/polymorphicSignature/varargOfObjects_before.kt b/compiler/testData/codegen/box/polymorphicSignature/varargOfObjects_before.kt new file mode 100644 index 00000000000..8b173a2259e --- /dev/null +++ b/compiler/testData/codegen/box/polymorphicSignature/varargOfObjects_before.kt @@ -0,0 +1,48 @@ +// TARGET_BACKEND: JVM +// IGNORE_BACKEND: JVM_IR +// FULL_JDK +// SKIP_JDK6 +// WITH_RUNTIME + +import java.lang.invoke.MethodHandles +import java.lang.invoke.MethodType +import java.lang.invoke.WrongMethodTypeException + +fun foo(vararg args: Any?): Any? = args[0] + +fun box(): String { + val mh = MethodHandles.lookup().findStatic( + object {}::class.java.enclosingClass, "foo", + MethodType.methodType(Any::class.java, Array::class.java) + ) + + val args = arrayOf("aaa", 1) + + // Note that before PolymorphicSignature was supported in the compiler, this call (and some subsequent calls) made little sense + // because the vararg was always wrapped in another array. But it compiled and worked somehow, and this test checks its behavior. + val r1 = mh.invokeExact(args) + if (r1 !is Array<*> || !r1.contentEquals(args)) return "Fail 1: $r1" + + val r2 = mh.invokeExact(*args) + if (r2 != "aaa") return "Fail 2: $r2" + + val r3 = mh.invokeExact(arrayOf(args) as Array<*>) + if (r3 !is Array<*> || r3[0] !is Array<*> || !(r3[0] as Array<*>).contentEquals(args)) return "Fail 3: $r3" + + val r4 = mh.invokeExact(arrayOf(args)) + if (r4 !is Array<*> || r4[0] !is Array<*> || !(r4[0] as Array<*>).contentEquals(args)) return "Fail 4: $r4" + + val r5 = mh.invoke(args) + if (r5 !is Array<*> || !r5.contentEquals(args)) return "Fail 5: $r5" + + val r6 = mh.invoke(*args) + if (r6 != "aaa") return "Fail 6: $r6" + + val r7 = mh.invoke(arrayOf(args) as Array<*>) + if (r7 !is Array<*> || r7[0] !is Array<*> || !(r7[0] as Array<*>).contentEquals(args)) return "Fail 7: $r7" + + val r8 = mh.invoke(arrayOf(args)) + if (r8 !is Array<*> || r8[0] !is Array<*> || !(r8[0] as Array<*>).contentEquals(args)) return "Fail 8: $r8" + + return "OK" +} diff --git a/compiler/testData/codegen/java9/box/varHandle.kt b/compiler/testData/codegen/java9/box/varHandle.kt new file mode 100644 index 00000000000..f1511259d47 --- /dev/null +++ b/compiler/testData/codegen/java9/box/varHandle.kt @@ -0,0 +1,38 @@ +// !LANGUAGE: +PolymorphicSignature +// TARGET_BACKEND: JVM +// IGNORE_BACKEND: JVM_IR +// FULL_JDK +// SKIP_JDK6 +// WITH_RUNTIME + +import java.lang.invoke.MethodHandles +import kotlin.concurrent.thread + +fun box(): String { + val handle = MethodHandles.arrayElementVarHandle(ByteArray::class.java) + val array = ByteArray(10) + + val index = 0 + + // Check that we don't consider non-Object return type of a signature-polymorphic method to be polymorphic. + handle.weakCompareAndSetPlain(array, index, 0.toByte(), 21.toByte()) as Comparable<*> + + val oldValue = 42.toByte() + val newValue = (-74).toByte() + + thread { + Thread.sleep(400L) + + handle.setVolatile(array, index, oldValue) + } + + while (!handle.compareAndSet(array, index, oldValue, newValue)) { + Thread.sleep(10L) + } + + return if (handle.getVolatile(array, index) == newValue) "OK" else "Fail" +} + +fun main() { + box().let { if (it != "OK") throw AssertionError(it) } +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index e06b89f77d8..3f0db485486 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -16773,6 +16773,49 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { } } + @TestMetadata("compiler/testData/codegen/box/polymorphicSignature") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class PolymorphicSignature extends AbstractBlackBoxCodegenTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath); + } + + public void testAllFilesPresentInPolymorphicSignature() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/polymorphicSignature"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); + } + + @TestMetadata("anonymousSubclass.kt") + public void testAnonymousSubclass() throws Exception { + runTest("compiler/testData/codegen/box/polymorphicSignature/anonymousSubclass.kt"); + } + + @TestMetadata("invoke.kt") + public void testInvoke() throws Exception { + runTest("compiler/testData/codegen/box/polymorphicSignature/invoke.kt"); + } + + @TestMetadata("invokeExact.kt") + public void testInvokeExact() throws Exception { + runTest("compiler/testData/codegen/box/polymorphicSignature/invokeExact.kt"); + } + + @TestMetadata("invokeExactWithInlineClass.kt") + public void testInvokeExactWithInlineClass() throws Exception { + runTest("compiler/testData/codegen/box/polymorphicSignature/invokeExactWithInlineClass.kt"); + } + + @TestMetadata("varargOfObjects_after.kt") + public void testVarargOfObjects_after() throws Exception { + runTest("compiler/testData/codegen/box/polymorphicSignature/varargOfObjects_after.kt"); + } + + @TestMetadata("varargOfObjects_before.kt") + public void testVarargOfObjects_before() throws Exception { + runTest("compiler/testData/codegen/box/polymorphicSignature/varargOfObjects_before.kt"); + } + } + @TestMetadata("compiler/testData/codegen/box/primitiveTypes") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/Java9CodegenTest.kt b/compiler/tests/org/jetbrains/kotlin/codegen/Java9CodegenTest.kt new file mode 100644 index 00000000000..41ebc617ccd --- /dev/null +++ b/compiler/tests/org/jetbrains/kotlin/codegen/Java9CodegenTest.kt @@ -0,0 +1,54 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.codegen + +import org.jetbrains.kotlin.cli.common.output.writeAll +import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime +import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils +import org.jetbrains.kotlin.test.ConfigurationKind +import org.jetbrains.kotlin.test.KotlinTestUtils +import org.jetbrains.kotlin.test.TestJdkKind +import java.io.File +import java.util.concurrent.TimeUnit + +// TODO: merge into main codegen box tests somehow +class Java9CodegenTest : AbstractBlackBoxCodegenTest() { + override fun setUp() { + super.setUp() + val fileName = KotlinTestUtils.getTestDataPathBase() + "/codegen/" + getPrefix() + "/" + getTestName(true) + ".kt" + val testFile = TestFile(fileName, File(fileName).readText()) + createEnvironmentWithMockJdkAndIdeaAnnotations(ConfigurationKind.NO_KOTLIN_REFLECT, listOf(testFile), TestJdkKind.FULL_JDK_9) + } + + override fun getPrefix(): String = "java9/box" + + override fun blackBox() { + val tmpdir = KotlinTestUtils.tmpDirForTest(this) + generateClassesInFile().writeAll(tmpdir, null) + + val jdk9Home = KotlinTestUtils.getJdk9Home() + val javaExe = File(jdk9Home, "bin/java.exe").takeIf(File::exists) + ?: File(jdk9Home, "bin/java").takeIf(File::exists) + ?: error("Can't find 'java' executable in $jdk9Home") + + val command = arrayOf( + javaExe.absolutePath, + "-ea", + "-classpath", + listOf(tmpdir, ForTestCompileRuntime.runtimeJarForTests()).joinToString(File.pathSeparator, transform = File::getAbsolutePath), + PackagePartClassUtils.getFilePartShortName(getTestName(false)) + ) + + val process = ProcessBuilder(*command).inheritIO().start() + process.waitFor(1, TimeUnit.MINUTES) + assertEquals(0, process.exitValue()) + } + + fun testVarHandle() { + loadFile() + blackBox() + } +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index dcd2ec038c6..2308b9728c9 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -16773,6 +16773,49 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes } } + @TestMetadata("compiler/testData/codegen/box/polymorphicSignature") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class PolymorphicSignature extends AbstractLightAnalysisModeTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath); + } + + public void testAllFilesPresentInPolymorphicSignature() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/polymorphicSignature"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); + } + + @TestMetadata("anonymousSubclass.kt") + public void testAnonymousSubclass() throws Exception { + runTest("compiler/testData/codegen/box/polymorphicSignature/anonymousSubclass.kt"); + } + + @TestMetadata("invoke.kt") + public void testInvoke() throws Exception { + runTest("compiler/testData/codegen/box/polymorphicSignature/invoke.kt"); + } + + @TestMetadata("invokeExact.kt") + public void testInvokeExact() throws Exception { + runTest("compiler/testData/codegen/box/polymorphicSignature/invokeExact.kt"); + } + + @TestMetadata("invokeExactWithInlineClass.kt") + public void testInvokeExactWithInlineClass() throws Exception { + runTest("compiler/testData/codegen/box/polymorphicSignature/invokeExactWithInlineClass.kt"); + } + + @TestMetadata("varargOfObjects_after.kt") + public void testVarargOfObjects_after() throws Exception { + runTest("compiler/testData/codegen/box/polymorphicSignature/varargOfObjects_after.kt"); + } + + @TestMetadata("varargOfObjects_before.kt") + public void testVarargOfObjects_before() throws Exception { + runTest("compiler/testData/codegen/box/polymorphicSignature/varargOfObjects_before.kt"); + } + } + @TestMetadata("compiler/testData/codegen/box/primitiveTypes") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index ddcf9464364..26f046ce90c 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -16778,6 +16778,49 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes } } + @TestMetadata("compiler/testData/codegen/box/polymorphicSignature") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class PolymorphicSignature extends AbstractIrBlackBoxCodegenTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM_IR, testDataFilePath); + } + + public void testAllFilesPresentInPolymorphicSignature() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/polymorphicSignature"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM_IR, true); + } + + @TestMetadata("anonymousSubclass.kt") + public void testAnonymousSubclass() throws Exception { + runTest("compiler/testData/codegen/box/polymorphicSignature/anonymousSubclass.kt"); + } + + @TestMetadata("invoke.kt") + public void testInvoke() throws Exception { + runTest("compiler/testData/codegen/box/polymorphicSignature/invoke.kt"); + } + + @TestMetadata("invokeExact.kt") + public void testInvokeExact() throws Exception { + runTest("compiler/testData/codegen/box/polymorphicSignature/invokeExact.kt"); + } + + @TestMetadata("invokeExactWithInlineClass.kt") + public void testInvokeExactWithInlineClass() throws Exception { + runTest("compiler/testData/codegen/box/polymorphicSignature/invokeExactWithInlineClass.kt"); + } + + @TestMetadata("varargOfObjects_after.kt") + public void testVarargOfObjects_after() throws Exception { + runTest("compiler/testData/codegen/box/polymorphicSignature/varargOfObjects_after.kt"); + } + + @TestMetadata("varargOfObjects_before.kt") + public void testVarargOfObjects_before() throws Exception { + runTest("compiler/testData/codegen/box/polymorphicSignature/varargOfObjects_before.kt"); + } + } + @TestMetadata("compiler/testData/codegen/box/primitiveTypes") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt index 2e60148cf91..f749257257c 100644 --- a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt +++ b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt @@ -92,6 +92,7 @@ enum class LanguageFeature( RestrictReturnStatementTarget(KOTLIN_1_4, kind = BUG_FIX), NoConstantValueAttributeForNonConstVals(KOTLIN_1_4, kind = BUG_FIX), WarningOnMainUnusedParameter(KOTLIN_1_4), + PolymorphicSignature(KOTLIN_1_4), ProperVisibilityForCompanionObjectInstanceField(sinceVersion = null, kind = BUG_FIX), // Temporarily disabled, see KT-27084/KT-22379 diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrJsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrJsCodegenBoxTestGenerated.java index 7df061536cd..6766b349a4f 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrJsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrJsCodegenBoxTestGenerated.java @@ -14318,6 +14318,19 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest { } } + @TestMetadata("compiler/testData/codegen/box/polymorphicSignature") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class PolymorphicSignature extends AbstractIrJsCodegenBoxTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS_IR, testDataFilePath); + } + + public void testAllFilesPresentInPolymorphicSignature() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/polymorphicSignature"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS_IR, true); + } + } + @TestMetadata("compiler/testData/codegen/box/primitiveTypes") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index fd58ec951a6..5391e50ecf5 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -15373,6 +15373,19 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { } } + @TestMetadata("compiler/testData/codegen/box/polymorphicSignature") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class PolymorphicSignature extends AbstractJsCodegenBoxTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS, testDataFilePath); + } + + public void testAllFilesPresentInPolymorphicSignature() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/polymorphicSignature"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS, true); + } + } + @TestMetadata("compiler/testData/codegen/box/primitiveTypes") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)