Support calling methods annotated with PolymorphicSignature
#KT-14416 Fixed #KT-26165 Fixed
This commit is contained in:
@@ -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<out T>',
|
||||
// while its lower bound may be Nothing-typed after approximation
|
||||
val lazyVararg = codegen.genVarargs(argument, valueParameters[i].type.upperIfFlexible())
|
||||
|
||||
@@ -2401,12 +2401,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> 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<StackValue, StackValue> impleme
|
||||
ResolvedCall<FunctionDescriptor> 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(
|
||||
|
||||
@@ -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"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<KotlinType> 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<KotlinType> 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<KotlinType> extractPolymorphicParameterTypes(@NotNull ResolvedCall<?> resolvedCall) {
|
||||
List<ResolvedValueArgument> 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;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
+23
@@ -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
|
||||
}
|
||||
@@ -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<Any>::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"
|
||||
}
|
||||
@@ -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<Any>::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"
|
||||
}
|
||||
@@ -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) }
|
||||
}
|
||||
+43
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
+43
@@ -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)
|
||||
|
||||
+43
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
+13
@@ -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)
|
||||
|
||||
+13
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user